Python Pandas,应用函数

10

我正在尝试使用apply函数来避免在函数中使用iterrows()迭代器:

然而,这个pandas方法的文档非常不清晰,我找不到如何使用它的示例,除了文档中的无用的.apply(sq.rt)……没有关于如何使用参数等的示例。

无论如何,这里有一个玩具示例,展示了我试图做的事情。

据我理解,apply函数实际上会像iterrows()一样执行,即迭代(如果axis=0,则迭代行)。在每次迭代中,函数的输入x应该是迭代的行。然而,我一直收到的错误消息似乎证明了这种假设是错误的……

grid = np.random.rand(5,2)
df = pd.DataFrame(grid)

def multiply(x):
    x[3]=x[0]*x[1]

df = df.apply(multiply, axis=0)

上面的示例返回了一个空的df。有谁能帮我理解一下吗?

2
该方法的文档页面并不是很丰富,但您可以在此处查看:http://pandas.pydata.org/pandas-docs/stable/basics.html#function-application。 - ayhan
@ayhan 这实际上解决了我的问题。谢谢。 - jim jarnac
4个回答

10
import pandas as pd
import numpy as np

grid = np.random.rand(5,2)
df = pd.DataFrame(grid)

def multiply(x):
    return x[0]*x[1]

df['multiply'] = df.apply(multiply, axis = 1)
print(df)

结果为:

          0         1  multiply
0  0.550750  0.713054  0.392715
1  0.061949  0.661614  0.040987
2  0.472134  0.783479  0.369907
3  0.827371  0.277591  0.229670
4  0.961102  0.137510  0.132162

解释:

您正在应用的函数需要返回一个值。同时,您将其应用于每一行,而不是每一列。您传递的 axis 参数在这方面是不正确的。

最后,请注意我在函数外将其设置为 'multiply' 列的值。您可以轻松更改它为像您已经使用的 df[3] = ...,得到一个如下的数据框:

          0         1         3
0  0.550750  0.713054  0.392715
1  0.061949  0.661614  0.040987
2  0.472134  0.783479  0.369907
3  0.827371  0.277591  0.229670
4  0.961102  0.137510  0.132162

5

需要注意,你也可以使用lambda函数。请查看它们的文档 Apply

针对你的示例,你可以运行:

df['multiply'] = df.apply(lambda row: row[0] * row[1], axis = 1)

这将产生与 @Andy 相同的输出。

如果您的函数形式为

def multiply(a,b):
    return a*b

df['multiply'] = df.apply(lambda row: multiply(row[0] ,row[1]), axis = 1)

优化性能的章节中有更多的示例。


3

Pandas禅宗之一的规则是:首先尝试找到向量化的解决方案

.apply(..., axis=1)不是向量化的!

考虑替代方案:

In [164]: df.prod(axis=1)
Out[164]:
0    0.770675
1    0.539782
2    0.318027
3    0.597172
4    0.211643
dtype: float64

In [165]: df[0] * df[1]
Out[165]:
0    0.770675
1    0.539782
2    0.318027
3    0.597172
4    0.211643
dtype: float64

针对 50,000 行数据框的时间测试:

In [166]: df = pd.concat([df] * 10**4, ignore_index=True)

In [167]: df.shape
Out[167]: (50000, 2)

In [168]: %timeit df.apply(multiply, axis=1)
1 loop, best of 3: 6.12 s per loop

In [169]: %timeit df.prod(axis=1)
100 loops, best of 3: 6.23 ms per loop

In [170]: def multiply_vect(x1, x2):
     ...:     return x1*x2
     ...:

In [171]: %timeit multiply_vect(df[0], df[1])
1000 loops, best of 3: 604 µs per loop

结论:仅在万不得已时(即无法使用其他方法)使用.apply()

嗨,谢谢 - 是的,我知道...我试图找到一个简单的例子来询问我的问题,但我想我有点失败了...这个例子太简单了,没有提到我遇到的两个问题,一个是如何检索正在迭代的行的索引(我相信它被转换为行的名称属性),另一个是如何将数据框作为参数传递...无论如何,谢谢...我会再考虑一下,也许我会直接使用我正在尝试做的函数来询问,这是一个百分位数函数。 - jim jarnac
我认为OP使用multiply的目的是为了理解如何使用.apply(),正如主题的标题所示,这只是一个玩具示例。 - Jon
@jimbasquiat,是的,最好提出一个新问题,描述您的特定问题。很可能有另一种(更好的)解决方案,与.apply(..., axis=1)相比。 - MaxU - stand with Ukraine
@MaxU 在这里转载:http://stackoverflow.com/questions/43482830/python-pandas-apply-function-and-percentile-calculation - jim jarnac

1
当应用一个函数时,你需要该函数返回该列/行操作的结果。由于明显地multiply没有返回,所以你得到了None。也就是说,apply应该返回特定值之间的结果,而不是进行赋值本身。
此外,你正在迭代错误的轴。你当前的代码获取每个的第一个和第二个元素并将它们相乘。
一个正确的multiply函数:
def multiply(x):
    return x[0]*x[1]

df[3] = df.apply(multiply, 'columns')

在这种情况下,您可以做得比apply更好,因为它不是矢量化操作。直接将列相乘即可。

注意:保留HTML标签。
df[3] = df[0]*df[1]

一般而言,应尽可能避免使用apply,因为它本质上只是一个循环。

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