56

I would like to use df.groupby() in combination with apply() to apply a function to each row per group.

I normally use the following code, which usually works (note, that this is without groupby()):

df.apply(myFunction, args=(arg1,))

With the groupby() I tried the following:

df.groupby('columnName').apply(myFunction, args=(arg1,))

However, I get the following error:

TypeError: myFunction() got an unexpected keyword argument 'args'

Hence, my question is: How can I use groupby() and apply() with a function that needs arguments?

ekad
  • 14,436
  • 26
  • 44
  • 46
beta
  • 5,324
  • 15
  • 57
  • 99
  • 5
    This would work with `df.groupby('columnName').apply(myFunction, ('arg1'))` – Zero Sep 11 '17 at 13:30
  • 1
    @Zero this is great answer as it is very similar to OP's attempted solution and doesn't require a lambda. I suggest you post it as an answer. – DontDivideByZero Oct 16 '17 at 09:19
  • @Zero, I have the very same quetsion as the OP, but this doesn't work for me - I still get the very same error as the OP. Also, may I ask why your comment should work and why the OP's approach (which is the same as mine) doesn't? I haven't found it documented anywhere – Pythonista anonymous Oct 16 '17 at 12:22
  • try `.apply(myFunction, args = ('arg1',)` note the `,`after `arg1`. – beta Oct 17 '17 at 10:22
  • actually, i just tried it by myself and it doesnt work either... – beta Oct 17 '17 at 10:29

3 Answers3

59

pandas.core.groupby.GroupBy.apply does NOT have named parameter args, but pandas.DataFrame.apply does have it.

So try this:

df.groupby('columnName').apply(lambda x: myFunction(x, arg1))

or as suggested by @Zero:

df.groupby('columnName').apply(myFunction, ('arg1'))

Demo:

In [82]: df = pd.DataFrame(np.random.randint(5,size=(5,3)), columns=list('abc'))

In [83]: df
Out[83]:
   a  b  c
0  0  3  1
1  0  3  4
2  3  0  4
3  4  2  3
4  3  4  1

In [84]: def f(ser, n):
    ...:     return ser.max() * n
    ...:

In [85]: df.apply(f, args=(10,))
Out[85]:
a    40
b    40
c    40
dtype: int64

when using GroupBy.apply you can pass either a named arguments:

In [86]: df.groupby('a').apply(f, n=10)
Out[86]:
    a   b   c
a
0   0  30  40
3  30  40  40
4  40  20  30

a tuple of arguments:

In [87]: df.groupby('a').apply(f, (10))
Out[87]:
    a   b   c
a
0   0  30  40
3  30  40  40
4  40  20  30
MaxU - stand with Ukraine
  • 205,989
  • 36
  • 386
  • 419
9

Some confusion here over why using an args parameter throws an error might stem from the fact that pandas.DataFrame.apply does have an args parameter (a tuple), while pandas.core.groupby.GroupBy.apply does not.

So, when you call .apply on a DataFrame itself, you can use this argument; when you call .apply on a groupby object, you cannot.

In @MaxU's answer, the expression lambda x: myFunction(x, arg1) is passed to func (the first parameter); there is no need to specify additional *args/**kwargs because arg1 is specified in lambda.

An example:

import numpy as np
import pandas as pd

# Called on DataFrame - `args` is a 1-tuple
# `0` / `1` are just the axis arguments to np.sum
df.apply(np.sum, axis=0)  # equiv to df.sum(0)
df.apply(np.sum, axis=1)  # equiv to df.sum(1)


# Called on groupby object of the DataFrame - will throw TypeError
print(df.groupby('col1').apply(np.sum, args=(0,)))
# TypeError: sum() got an unexpected keyword argument 'args'
Brad Solomon
  • 38,521
  • 31
  • 149
  • 235
6

For me

df2 = df.groupby('columnName').apply(lambda x: my_function(x, arg1, arg2,))

worked

Hitesh Somani
  • 620
  • 4
  • 11
  • 16