假设我有一个df,它的列是" ID " " col_1 " " col_2 "我定义了一个函数:

F = x, y: my_function_expression。

现在我想应用f到df的两个列'col_1', 'col_2'来逐个元素计算一个新列'col_3',有点像:

df['col_3'] = df[['col_1','col_2']].apply(f)  
# Pandas gives : TypeError: ('<lambda>() takes exactly 2 arguments (1 given)'

怎么办?

**添加详细示例如下***

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

  ID  col_1  col_2            col_3
0  1      0      1       ['a', 'b']
1  2      2      4  ['c', 'd', 'e']
2  3      3      5  ['d', 'e', 'f']

当前回答

从apply返回一个列表是一个危险的操作,因为结果对象不能保证是Series或DataFrame。在某些情况下可能会提出例外。让我们来看一个简单的例子:

df = pd.DataFrame(data=np.random.randint(0, 5, (5,3)),
                  columns=['a', 'b', 'c'])
df
   a  b  c
0  4  0  0
1  2  0  1
2  2  2  2
3  1  2  2
4  3  0  0

从apply返回列表有三种可能的结果

1)如果返回的列表长度不等于列数,则返回一系列列表。

df.apply(lambda x: list(range(2)), axis=1)  # returns a Series
0    [0, 1]
1    [0, 1]
2    [0, 1]
3    [0, 1]
4    [0, 1]
dtype: object

2)当返回列表的长度等于 然后返回一个DataFrame,每个列都得到 列表中对应的值。

df.apply(lambda x: list(range(3)), axis=1) # returns a DataFrame
   a  b  c
0  0  1  2
1  0  1  2
2  0  1  2
3  0  1  2
4  0  1  2

3)如果返回的列表长度等于第一行的列数,但至少有一行的列表元素数与列数不同,将引发ValueError。

i = 0
def f(x):
    global i
    if i == 0:
        i += 1
        return list(range(3))
    return list(range(4))

df.apply(f, axis=1) 
ValueError: Shape of passed values is (5, 4), indices imply (5, 3)

胡乱回答问题

使用带有axis=1的apply非常慢。使用基本的迭代方法可以获得更好的性能(特别是在较大的数据集上)。

创建更大的数据框架

df1 = df.sample(100000, replace=True).reset_index(drop=True)

计时

# apply is slow with axis=1
%timeit df1.apply(lambda x: mylist[x['col_1']: x['col_2']+1], axis=1)
2.59 s ± 76.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# zip - similar to @Thomas
%timeit [mylist[v1:v2+1] for v1, v2 in zip(df1.col_1, df1.col_2)]  
29.5 ms ± 534 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

@Thomas回答

%timeit list(map(get_sublist, df1['col_1'],df1['col_2']))
34 ms ± 459 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

其他回答

有两种简单的方法: 比方说,我们想在名为col_sum的输出列中求col1和col2的和

方法1

f = lambda x : x.col1 + x.col2
df['col_sum'] = df.apply(f, axis=1)

方法2

def f(x):
    x['col_sum'] = x.col_1 + col_2
    return x
df = df.apply(f, axis=1)

当一些复杂的函数必须应用到数据帧时,应该使用方法2。当需要多列输出时,也可以使用方法2。

我要投票支持np。vectorize。它允许你只拍摄x个列,而不处理函数中的数据帧,所以它非常适合你不控制的函数,或者做一些像发送2列和一个常数到一个函数(即col_1, col_2, 'foo')。

import numpy as np
import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

df.loc[:,'col_3'] = np.vectorize(get_sublist, otypes=["O"]) (df['col_1'], df['col_2'])


df

ID  col_1   col_2   col_3
0   1   0   1   [a, b]
1   2   2   4   [c, d, e]
2   3   3   5   [d, e, f]

一个简单的解决方案是:

df['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)

如果你有一个巨大的数据集,那么你可以使用一种简单但更快(执行时间)的方式来做到这一点,使用swifter:

import pandas as pd
import swifter

def fnc(m,x,c):
    return m*x+c

df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})
df["y"] = df.swifter.apply(lambda x: fnc(x.m, x.x, x.c), axis=1)

下面是一个在dataframe上使用apply的例子,我用axis = 1调用它。

注意,不同之处在于,不是试图将两个值传递给函数f,而是重写函数以接受pandas Series对象,然后对Series进行索引以获得所需的值。

In [49]: df
Out[49]: 
          0         1
0  1.000000  0.000000
1 -0.494375  0.570994
2  1.000000  0.000000
3  1.876360 -0.229738
4  1.000000  0.000000

In [50]: def f(x):    
   ....:  return x[0] + x[1]  
   ....:  

In [51]: df.apply(f, axis=1) #passes a Series object, row-wise
Out[51]: 
0    1.000000
1    0.076619
2    1.000000
3    1.646622
4    1.000000

根据您的用例,有时创建pandas组对象,然后在组上使用apply是有帮助的。