假设我有一个df,其中有'ID', 'col_1', 'col_2'的列。我定义一个函数:

f = lambda 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']


评论

您可以将f直接应用于列:df ['col_3'] = f(df ['col_1'],df ['col_2'])

知道f在做什么会很有用

否,df ['col_3'] = f(df ['col_1'],df ['col_2'])不起作用。对于f只接受标量输入,不接受向量输入。好的,您可以假设f = lambda x,y:x + y。 (当然,我真正的f不是那么简单,否则我可以直接df ['col_3'] = df ['col_1'] + df ['col_2'])

我在url下方找到了一个相关的问答,但是我的问题是用两个现有列(而不是1中的2)来计算一个新列。 stackoverflow.com/questions/12356501 / ...

我认为我的响应stackoverflow.com/a/52854800/5447172以最Pythonic / Pandanic方式回答了此问题,没有解决方法或数字索引。它产生的正是您在示例中所需的输出。

#1 楼

这是在数据帧上使用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


根据您的用例,有时创建熊猫group对象,然后在组上使用apply有时会有所帮助。

评论


是的,我尝试使用Apply,但是找不到有效的语法表达式。并且,如果df的每一行都是唯一的,是否仍使用groupby?

– bigbug
2012年11月12日上午10:42

在我的答案中添加了一个示例,希望它能满足您的需求。如果不是,请提供一个更具体的示例函数,因为到目前为止,建议的任何方法都可以成功解决sum。

–阿曼
2012年11月12日14:51

您可以粘贴您的代码吗?我重写该函数:def get_sublist(x):返回mylist [x [1]:x [2] + 1]和df ['col_3'] = df.apply(get_sublist,axis = 1)给出'ValueError:操作数可以不能与形状(2)(3)一起播放

– bigbug
2012年11月16日在7:11

@Aman:在Pandas版本0.14.1(可能更早)中,use也可以使用lambda表达式。给您定义的df对象,另一种方法(效果相同)是df.apply(lambda x:x [0] + x [1],axis = 1)。

– Jubbles
2015年1月10日,下午1:37

@CanCeylan,您可以只在函数中使用列名而不是索引,然后您不必担心顺序更改或通过名称获取索引,例如参见stackoverflow.com/questions/13021654/…

–达沃斯
18-3-22在7:19



#2 楼

在Pandas中,有一种干净的单行方法:

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


这使f成为具有多个输入值的用户定义函数,并使用(安全)列名而不是(不安全)数字索引来访问列。

数据示例(基于原始问题):

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.apply(lambda x: get_sublist(x.col_1, x.col_2), axis=1)


print(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]


如果列名包含空格或与现有dataframe属性共享名称,则可以使用方括号进行索引:

df['col_3'] = df.apply(lambda x: f(x['col 1'], x['col 2']), axis=1)


评论


注意,如果使用axis = 1并且您将列称为name,则它实际上不会返回您的列数据,而是返回索引。类似于在groupby()中获取名称。我通过重命名专栏解决了这个问题。

–汤姆·海姆斯(Tom Hemmes)
19年5月22日在13:58

就是这个!我只是没有意识到您可以将具有多个输入参数的用户定义函数插入到lambda中。重要的是要注意(我认为)您正在使用DF.apply()而不是Series.apply()。这使您可以使用所需的两列为df编制索引,并将整个列传递到函数中,但是由于您使用的是apply(),因此它将按元素方式将函数应用于整个列。辉煌!感谢您的发表!

– Data-phile
19年5月31日在22:36

最后!你救了我的一天!

– Mysterio
19年9月5日在11:25

我认为建议的方法是df.loc [:,'new col'] = df.apply .....

–valearner
2月10日下午19:38

@valearner我认为在示例中没有任何理由偏爱.loc。如果您将其调整为其他问题设置(例如使用切片),则可能需要这样做。

–ajrwhite
2月17日下午2:05

#3 楼

一个简单的解决方案是:

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


评论


此答案与问题中的方法有何不同:df ['col_3'] = df [['col_1','col_2']]。apply(f)只是为了确认,问题中的方法无效,因为发布者未指定此axis = 1,默认值为axis = 0?

–Lost1
17年7月19日在19:41

此答案与@Anman的答案相当,但有点过时。他正在构造一个接受可迭代的匿名函数,并在将其传递给函数f之前将其解压缩。

–iao
17年10月10日在15:15

在我的情况下,此方法的速度提高了两倍,达到了10万行(与df.apply(lambda x:f(x.col_1,x.col_2),axis = 1)相比)

–西尔万
12月4日,0:13

#4 楼

一个有趣的问题!我的答案如下:

import pandas as pd

def sublst(row):
    return lst[row['J1']:row['J2']]

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

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


输出:

  ID  J1  J2
0  1   0   1
1  2   2   4
2  3   3   5
  ID  J1  J2      J3
0  1   0   1     [a]
1  2   2   4  [c, d]
2  3   3   5  [d, e]


我将列名称更改为ID,J1 ,J2,J3以确保ID
另一个简短版本:

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']

df['J3'] = df.apply(lambda row:lst[row['J1']:row['J2']],axis=1)
print df


#5 楼

您正在寻找的方法是Series.combine。
但是,似乎必须注意数据类型。
在您的示例中,您将(就像我在测试答案时所做的那样)天真地调用

df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)


但是,这会引发错误:
<最好的猜测是,似乎期望结果与调用该方法的系列(此处为df.col_1)具有相同的类型。但是,以下工作原理:

ValueError: setting an array element with a sequence.


#6 楼

您的书写方式需要两个输入。如果查看错误消息,它表示您没有为f提供两个输入,仅一个。错误消息是正确的。
不匹配是因为df [[''col1','col2']]返回一个包含两列的数据帧,而不是两列。

您需要更改f使其仅接受一个输入,将上述数据框保留为输入,然后将其分解为函数体内的x,y。然后执行所需的任何操作并返回单个值。

您需要此函数签名,因为语法为.apply(f)
,因此f需要采用单个值= dataframe而不是两个您当前的f期望的事物。

由于您没有提供f的正文,所以我无济于事-但这应该提供解决方法,而无需从根本上更改您的代码或使用其他方法,而不是应用

#7 楼

我将对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]


评论


这实际上并不能用熊猫回答这个问题。

–mnky9800n
16年5月17日在14:37

问题是“如何将函数应用于两列Pandas数据框”而不是“如何仅通过Pandas方法将函数应用于两列Pandas数据”,而numpy是Pandas的依赖项,因此无论如何都必须安装它,所以这似乎是一个奇怪的异议。

–特拉·华莱士(Trae Wallace)
16年5月19日在15:43

#8 楼

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)当返回的列表的长度等于数量时of
列,然后返回一个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)


在不应用情况下解决问题

apply与axis = 1一起使用非常慢。使用基本的迭代方法可能会获得更好的性能(尤其是在较大的数据集上)。

创建更大的数据框

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


Timings

# 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)


@托马斯回答

%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)


评论


很高兴从可以学习的地方看到如此详尽的答案。

–安德烈·莫罗(Andrea Moro)
2月14日下午2:19

#9 楼

我确定这不如使用Pandas或Numpy操作的解决方案快,但是如果您不想重写函数,则可以使用map。使用原始示例数据-

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'] = list(map(get_sublist,df['col_1'],df['col_2']))
#In Python 2 don't convert above to list


我们可以通过这种方式将任意数量的参数传递给函数。输出就是我们想要的

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]


评论


实际上,使用轴= 1时使用的答案要快得多

–特德·彼得鲁(Ted Petrou)
17-10-25在1:06

#10 楼

我对您的问题的示例:

def get_sublist(row, col1, col2):
    return mylist[row[col1]:row[col2]+1]
df.apply(get_sublist, axis=1, col1='col_1', col2='col_2')


#11 楼

如果您有庞大的数据集,则可以使用快捷方式使用一种简单但更快的(执行时间)方法:

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)


#12 楼

我想您不想更改get_sublist函数,而只想使用DataFrame的apply方法来完成这项工作。为了获得所需的结果,我编写了两个帮助功能:get_sublist_listunlist。顾名思义,首先获取子列表,然后从列表中提取该子列表。最后,我们需要调用apply函数以随后将这两个函数应用于df[['col_1','col_2']]数据帧。

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]

def get_sublist_list(cols):
    return [get_sublist(cols[0],cols[1])]

def unlist(list_of_lists):
    return list_of_lists[0]

df['col_3'] = df[['col_1','col_2']].apply(get_sublist_list,axis=1).apply(unlist)

df


如果不使用[]来封装get_sublist函数,则get_sublist_list函数将返回一个普通列表,它将引发ValueError: could not broadcast input array from shape (3) into shape (2),就像@Ted Petrou提到的那样。