无法将lambda函数传递给pandas DataFrame的apply方法

90

我正在尝试将一个函数应用于pandas DataFrame的所有行(实际上只是该DataFrame中的一个列)

我确信这是一个语法错误,但我不确定我的错误在哪里

df['col'].apply(lambda x, y:(x - y).total_seconds(), args=[d1], axis=1)

col列包含一堆datetime.datetime对象,而d1是其中最早的一个。我正在尝试获取每行的总秒数列。

我一直得到以下错误:

TypeError: <lambda>() got an unexpected keyword argument 'axis'

我不明白为什么axis被传递到我的lambda函数中

我也尝试过这样做

def diff_dates(d1, d2):
    return (d1-d2).total_seconds()

df['col'].apply(diff_dates, args=[d1], axis=1)

我也遇到了同样的错误。

2个回答

129
请注意,与DataFrame.apply调用不同,Series.apply调用没有axis参数

Series.apply(func, convert_dtype=True, args=(), **kwds)

func : function
convert_dtype : boolean, default True
Try to find better dtype for elementwise function results. If False, leave as dtype=object
args : tuple
Positional arguments to pass to function in addition to the value

有一个df,但是当你在一系列上调用它但又希望它在一行上工作时,它的工作方式不清楚。


0
一个单独的列(通常)是一个 pandas Series,正如 EdChum 所提到的,DataFrame.applyaxis 参数,但 Series.apply 没有,所以在 axis=1 上使用 apply 将不会对列起作用。
以下代码可以正常工作:
df['col'].apply(lambda x, y: (x - y).total_seconds(), args=(d1,))

如果要对行中的每个元素应用一个函数,也可以使用map

df['col'].map(lambda x: (x - d1).total_seconds())

由于apply只是Python循环的语法糖,所以列表推导式可能比它们都更有效率,因为它没有pandas的开销:

[(x - d1).total_seconds() for x in df['col'].tolist()]

对于单列DataFrame,可以传递axis=1:
df[['col']].apply(lambda x, y: (x - y).dt.total_seconds(), args=[d1], axis=1)

注意:如果可以的话,请避免使用apply

apply在大多数情况下甚至都不需要。对于OP中的情况(以及大多数其他情况),存在一种向量化操作(只需从列中减去d1 - 值会广播以匹配列),而且比apply快得多:

(df['col'] - d1).dt.total_seconds()

时间

向量化的减法比在列上使用apply快约150倍,在只有一列的DataFrame上使用apply快7000多倍,对于一个有1万行的数据框。由于apply是一个循环,随着行数的增加,这个差距会越来越大。

df = pd.DataFrame({'col': pd.date_range('2000', '2023', 10_000)})
d1 = df['col'].min()

%timeit df['col'].apply(lambda x, y: (x - y).total_seconds(), args=[d1])
# 124 ms ± 7.57 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit df['col'].map(lambda x: (x - d1).total_seconds())
# 127 ms ± 16.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit [(x - d1).total_seconds() for x in df['col'].tolist()]
# 107 ms ± 4.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit (df['col'] - d1).dt.total_seconds()
# 851 µs ± 189 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

%timeit df[['col']].apply(lambda x, y: (x - y).dt.total_seconds(), args=[d1], axis=1)
# 6.07 s ± 419 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接