为什么我的Pandas的'apply'函数不能引用多列?

250
我有一些问题涉及Pandas中的apply函数,主要是在使用如下数据框时涉及多列。
df = DataFrame ({'a' : np.random.randn(6),
                 'b' : ['foo', 'bar'] * 3,
                 'c' : np.random.randn(6)})

以及以下的函数

def my_test(a, b):
    return a % b

当我尝试使用这个函数时:

df['Value'] = df.apply(lambda row: my_test(row[a], row[c]), axis=1)

我收到了错误信息:

NameError: ("global name 'a' is not defined", u'occurred at index 0')

我不理解这个信息,我已经正确定义了名字。

非常感谢任何关于这个问题的帮助。

更新

感谢您的帮助。我的代码确实存在一些语法错误,索引应该放在''内。但是,当使用更复杂的函数时,我仍然遇到相同的问题:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 

1
尽可能避免使用apply。如果您不确定是否需要使用它,那么您可能不需要。我建议查看何时应该在代码中使用pandas apply()? - cs95
这只是关于引用数据框列的语法错误和函数为什么需要参数的问题。至于你的第二个问题,函数my_test(a)不知道df是什么,因为它没有作为参数传递进来(除非df应该是全局变量,这将是可怕的做法)。你需要将函数内部需要的所有值作为参数传递(最好按顺序),否则函数怎么知道df来自哪里呢?此外,在一个充满全局变量的命名空间中编程是不好的实践,你不会捕捉到这样的错误。 - smci
6个回答

397

看起来你忘记了你的字符串中的 ''

In [43]: df['Value'] = df.apply(lambda row: my_test(row['a'], row['c']), axis=1)

In [44]: df
Out[44]:
                    a    b         c     Value
          0 -1.674308  foo  0.343801  0.044698
          1 -2.163236  bar -2.046438 -0.116798
          2 -0.199115  foo -0.458050 -0.199115
          3  0.918646  bar -0.007185 -0.001006
          4  1.336830  foo  0.534292  0.268245
          5  0.976844  bar -0.773630 -0.570417

顺便说一句,在我看来,以下方式更加优雅:

In [53]: def my_test2(row):
....:     return row['a'] % row['c']
....:     

In [54]: df['Value'] = df.apply(my_test2, axis=1)

谢谢,你是对的,我忘了''。然而,我在一个更复杂的函数中仍然遇到了同样的问题。非常感谢你的帮助。谢谢。 - Andy
5
@Andy 的 [53-54] 部分使您能够应用更复杂的函数。 - Andy Hayden
@Andy,你可以像In[53]那样定义你的复杂函数。 - waitingkuo
所有的应用策略都是一样的吗?我对Pandas还很陌生,一直觉得apply有点神秘,但你在[53-54]中的策略很容易理解(希望能记住)...在大表上,它是否和其他形式的apply一样快呢? - whytheq
为什么创建一个单独的方法被认为更加优雅,即使是对于微小的方法也是如此。我已经在Python中进行了重要的项目七年,但由于某些观点,包括这个观点,我可能永远不会被认为是“Pythonista”。 - WestCoastProjects
3
axis=1 在这里很重要。 - Luis

34
如果您只想计算 (列 a) % (列 b),则不需要使用apply,直接进行计算即可。
In [7]: df['a'] % df['c']                                                                                                                                                        
Out[7]: 
0   -1.132022                                                                                                                                                                    
1   -0.939493                                                                                                                                                                    
2    0.201931                                                                                                                                                                    
3    0.511374                                                                                                                                                                    
4   -0.694647                                                                                                                                                                    
5   -0.023486                                                                                                                                                                    
Name: a

16
明白了,这只是一个示例,用来说明我在将函数应用于多列时遇到的问题。 - Andy

18

假设我们想要将函数 add5 应用于 DataFrame df 的列 'a' 和 'b'

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)

在尝试您的代码片段时,我遇到了以下错误。 TypeError: ('必须是字符串,而不是整数','发生在索引b处') 请您能否查看一下。 - Debashis Sahoo
你的数据框中的列b是字符串类型或对象类型列,它应该是一个整数列,才能与数字相加。 - Mir_Murtaza
更改不是只有在赋值之后才会生效吗? - S.aad

11

上面提出的所有建议都是有效的,但如果你想让你的计算更加高效,你应该利用numpy向量操作(如此处所指)

import pandas as pd
import numpy as np


df = pd.DataFrame ({'a' : np.random.randn(6),
             'b' : ['foo', 'bar'] * 3,
             'c' : np.random.randn(6)})

示例1:使用pandas.apply()进行循环:

%%timeit
def my_test2(row):
    return row['a'] % row['c']

df['Value'] = df.apply(my_test2, axis=1)

最慢的运行时间比最快的运行时间长了7.49倍。这可能意味着中间结果被缓存了。1000个循环,3次取最佳结果:每个循环481微秒

示例2:使用pandas.apply()进行向量化:

%%timeit
df['a'] % df['c']

最慢的运行时间比最快的运行时间长了458.85倍。这可能意味着一个中间结果被缓存了。10000次循环中,取3次最好的结果:每个循环耗时70.9微秒。

示例3: 使用numpy数组进行向量化操作:

%%timeit
df['a'].values % df['c'].values
最慢的速度比最快的速度慢了7.98倍。这可能意味着中间结果正在被缓存。100000次循环,3次中的最佳运行时间为每次6.39微秒,因此使用numpy数组进行向量化处理可将速度提高近两个数量级。

对于大数字来说,结果变化更加显著。例如将6替换为10K,我得到了248毫秒、332微秒、263微秒的结果。因此,两个向量化的解决方案更接近,但非向量化的解决方案要慢1000倍。(在python-3.7上进行测试) - stason

3

这与之前的解决方案相同,但我已经在df.apply本身中定义了该函数:

df['Value'] = df.apply(lambda row: row['a']%row['c'], axis=1)

2
我已经给出了上述所有三种方法的比较。
使用值:
%timeit df['value'] = df['a'].values % df['c'].values
平均每次循环需要139微秒,标准差为1.91微秒(7次运行,每次10000个循环)。
不使用值:
%timeit df['value'] = df['a']%df['c']
平均每次循环需要216微秒,标准差为1.86微秒(7次运行,每次1000个循环)。
应用函数:
%timeit df['Value'] = df.apply(lambda row: row['a']%row['c'], axis=1)
平均每次循环需要474微秒,标准差为5.07微秒(7次运行,每次1000个循环)。

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