Python 如何将函数应用于dataframe的两列

Python 如何将函数应用于dataframe的两列,python,pandas,dataframe,Python,Pandas,Dataframe,假设我有一个df,它有'ID',col_1',col_2'列。我定义了一个函数: f=lambda x,y:my_函数\表达式 现在,我想将f应用于df的两列'col\u 1','col\u 2'以元素方式计算新列'col\u 3',类似于: df['col_3'] = df[['col_1','col_2']].apply(f) # Pandas gives : TypeError: ('<lambda>() takes exactly 2 arguments (1 given

假设我有一个
df
,它有
'ID',col_1',col_2'
列。我定义了一个函数:

f=lambda x,y:my_函数\表达式

现在,我想将
f
应用于
df
的两列
'col\u 1','col\u 2'
以元素方式计算新列
'col\u 3'
,类似于:

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

下面是一个在数据帧上使用
apply
的示例,我使用
axis=1
调用它

请注意,不同之处在于,与其尝试将两个值传递给函数
f
,不如重写函数以接受pandas 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

根据您的用例,有时创建一个
对象,然后在组上使用
应用
会很有帮助

您编写f的方式需要两个输入。如果您看到错误消息,它说您没有向f提供两个输入,只有一个。错误消息是正确的。
这种不匹配是因为df[['col1','col2']]返回一个包含两列的数据帧,而不是两个单独的列

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

您需要此函数签名,因为语法为.apply(f) 因此,f需要取单个的东西=数据帧,而不是当前f所期望的两个东西


由于您没有提供f的主体,我无法再提供更多详细信息,但这应该可以在不从根本上更改代码或使用其他方法的情况下提供出路,而不是应用您正在寻找的方法是Series.combine。 然而,似乎必须对数据类型采取一些谨慎措施。 在您的示例中,您会(就像我在测试答案时所做的那样)天真地调用

df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)
但是,这会引发错误:

ValueError: setting an array element with a sequence.
我最好的猜测是,它似乎期望结果与调用该方法的序列(这里是df.col_1)的类型相同。然而,以下工作:

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

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]

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

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

我将投票支持np.vectorize。它允许您只拍摄x个列,而不处理函数中的数据帧,因此对于您不控制的函数或向函数发送2个列和一个常量(即col_1,col_2,'foo')之类的操作非常有用


我确信这并没有使用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]
一个简单的解决方案是:

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

我举个例子来回答你的问题:

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

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

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)当返回列表的长度等于 列,然后返回一个数据帧,每列都获得 列表中相应的值

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)
回答这个问题时不加应用 使用轴=1的
apply
速度非常慢。使用基本的迭代方法可以获得更好的性能(尤其是在较大的数据集上)

创建更大的数据帧

df1 = df.sample(100000, replace=True).reset_index(drop=True)
时间安排 @托马斯回答

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

我假设您不想更改
get_sublist
函数,只想使用DataFrame的
apply
方法来完成这项工作。为了得到您想要的结果,我编写了两个帮助函数:
get\u sublist\u list
unlist
。正如函数名所示,首先获取子列表,然后从该列表中提取该子列表。最后,我们需要调用
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

如果您不使用
[]
来封装
获取子列表
函数,那么
获取子列表
函数将返回一个普通列表,它将引发
值错误:无法将输入数组从形状(3)广播到形状(2)
,正如@Ted Petrou所提到的。

有一个干净的,在熊猫身上这样做的一种方式:

df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)
这允许
f
成为具有多个输入值的用户定义函数,并使用(安全)列名而不是(不安全)数字索引来访问列

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

打印输出(df)

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

df['col_3'] = df.apply(lambda x: f(x['col 1'], x['col 2']), 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)

你能直接将f应用于列吗:df['col_3']=f(df['col_1'],df['col_2'])有助于了解
f
在做什么不,df['col_3']=f(df['col_1'],df['col_2'])不起作用。因为f只接受标量输入,而不接受向量输入。好的,你可以假设f=λx,y:x+y。(当然,我的实f不是那么简单,否则我可以直接df['col_3'
df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)
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)
  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.apply(lambda x: f(x['col 1'], x['col 2']), axis=1)
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)