12

I have a method called counting that takes 2 arguments. I need to call this method using the apply() method. However when I am passing the two parameters to the apply method it is giving the following error:

TypeError: counting() takes exactly 2 arguments (1 given)

I have seen the following thread python pandas: apply a function with arguments to a series. Update and I do not want to use functool.partial as I do not want to import additional classes to be able to pass parameters.

def counting(dic, strWord):
    if strWord in dic:
        return dic[strWord]
    else:
        return 0

DF['new_column'] = DF['dic_column'].apply(counting, 'word')

If I give a single parameter, it works:

def awesome_count(dic):
    if strWord in dic:
       return dic[strWord]
    else:
       return 0

DF['new_column'] = DF['dic_column'].apply(counting)
Community
  • 1
  • 1
Bonson
  • 1,418
  • 4
  • 18
  • 38
  • What is `dic` and where do you expect it to come from? Also, what's your problem with `partial`? It's part of the standard library ... – tzaman Oct 19 '15 at 00:18
  • Where did you want the second argument to come from? – Ignacio Vazquez-Abrams Oct 19 '15 at 00:20
  • Modified the question for more clarity. dic is the column value that would come by default via the apply function. The second argument is a new argument that is being passed using logic. – Bonson Oct 19 '15 at 00:24

2 Answers2

21

You could just use a lambda:

DF['new_column'] = DF['dic_column'].apply(lambda dic: counting(dic, 'word'))

On the other hand, there's absolutely nothing wrong with using partial here:

from functools import partial
count_word = partial(counting, strWord='word')
DF['new_column'] = DF['dic_column'].apply(count_word)

As @EdChum mentions, if your counting method is actually just looking up a word or defaulting it to zero, you can just use the handy dict.get method instead of writing one yourself:

DF['new_column'] = DF['dic_column'].apply(lambda dic: dic.get('word', 0))

And a non-lambda way to do the above, via the operator module:

from operator import methodcaller
count_word = methodcaller(get, 'word', 0)
DF['new_column'] = DF['dic_column'].apply(count_word)
tzaman
  • 46,925
  • 11
  • 90
  • 115
  • 1
    This gives me the required solution. Thank you. To your earlier question, I had assumed that this method would be better than using partial. Is it correct? Also, if possible, kindly confirm my understanding of this solution that here the value of the column is passed to the variable dic that is then passed to the method counting along with another variable value. – Bonson Oct 19 '15 at 00:34
  • 1
    `partial` is equivalent here, lambda isn't 'better' in any way: e.g. `countWord = partial(counting, strWord='word')` and then `apply(countWord)`. And yes, your understanding is correct. – tzaman Oct 19 '15 at 03:32
  • I think in this case you can just do `DF['new_column'] = DF['dic_column'].apply(lambda x: dic.get(x,0))` – EdChum Oct 19 '15 at 08:55
  • @EdChum not quite, the since the *dictionary* is the parameter to the lambda, not the word being looked up. But yeah, if `counting` is actually that simple, it can just be `lambda dic: dic.get('word', 0)`. – tzaman Oct 19 '15 at 17:43
3

The accepted answer is totally perfect. Taught me some interesting things about Python. But just for fun, here's more precisely what we're looking for:

selected_words =  ['awesome', 'great', 'fantastic', 'amazing', 'love', 'horrible', 'bad', 'terrible', 'awful', 'wow', 'hate']
for this_word in selected_words:
    products[this_word] = products['word_count'].apply(lambda dic: dic.get(this_word, 0))

Thanks for posting the question!

L0j1k
  • 12,255
  • 7
  • 53
  • 65