将两个参数的函数应用于列。

68
你能否创建一个pandas函数,将两个不同列的值作为参数?
我有一个函数,如果两列的值在相同的范围内,则返回1;否则返回0:
def segmentMatch(RealTime, ResponseTime):
    if RealTime <= 566 and ResponseTime <= 566:
        matchVar = 1
    elif 566 < RealTime <= 1132 and 566 < ResponseTime <= 1132:
        matchVar = 1
    elif 1132 < RealTime <= 1698 and 1132 < ResponseTime <= 1698:
        matchVar = 1
    else:
        matchVar = 0
    return matchVar

我希望第一个参数RealTime是数据框中的一列,这样函数就可以取该列中每一行的值。例如,RealTimedf['TimeCol'],第二个参数是df['ResponseCol']。我希望结果是数据框中的一个新列。我找到了几个 帖子回答了类似的问题,但是看起来这些参数是变量,而不是数据框中的行值。
我尝试了以下方法,但没有成功:
df['NewCol'] = df.apply(segmentMatch, args=(df['TimeCol'], df['ResponseCol']), axis=1)
4个回答

114

为什么不这样做呢?

df['NewCol'] = df.apply(lambda x: segmentMatch(x['TimeCol'], x['ResponseCol']), 
                        axis=1)

与你的例子中尝试将列作为参数传递不同,我们现在只需将每行中相应的条目作为参数传递,并将结果存储在'NewCol'中。


2
谢谢!我甚至可以用参数来使用它!尝试了在没有lambda函数的情况下实现此功能,但无法解决 :) - Zach
1
@N.Wouda,您能否解释一下您上面回答中发生了什么?lambda表达式参数x的值是多少?看起来它应该是我的数据框名称df,但我从未定义过这样的变量,所以有点困惑。谢谢。 - mmTmmR
1
@mmTmmR 是的,df 就是你的 DataFrame。根据文档x 的值是一个 pandas 行。使用 df 更多的是一种约定,因为任何其他名称也可以。x 同样如此。 - Nelewout
1
我在搜索互联网上花了4个小时,几乎要发布新帖子了。这是一个绝佳的解决方案,可以帮助避免在传递多个参数时出现错误,并且在if语句中使用布尔运算符时也能有效避免错误。 - k0rnik
4
... axis=1 ... 我在桌子上猛撞了45分钟,直到我看到那个!谢谢! - beep_check
感谢您回答有关将两个参数应用于一列和一个常量值的应用函数的问题。 - RF1991

24

如果你在函数外部定义函数,就不需要使用lambda函数:

def segmentMatch(vec):
    RealTime = vec[0]
    ResponseTime = vec[1]
    if RealTime <= 566 and ResponseTime <= 566:
        matchVar = 1
    elif 566 < RealTime <= 1132 and 566 < ResponseTime <= 1132:
        matchVar = 1
    elif 1132 < RealTime <= 1698 and 1132 < ResponseTime <= 1698:
        matchVar = 1
    else:
        matchVar = 0
    return matchVar

df['NewCol'] = df[['TimeCol', 'ResponseCol']].apply(segmentMatch, axis=1)

如果“segmentMatch”返回一个由2个值组成的向量,那么您可以按照以下方式操作:

def segmentMatch(vec):
    ......
    return pd.Series((matchVar1, matchVar2)) 

df[['NewCol', 'NewCol2']] = df[['TimeCol','ResponseCol']].apply(segmentMatch, axis=1)

4
通过使用 assign(),可以以支持链式语法的方式执行此操作:

一种更加友好的链式语法执行此操作的方法是通过 assign()函数:

df.assign( NewCol = lambda x: segmentMatch(x['TimeCol'], x['ResponseCol']) )

0
在我目前的工作场所,使用lambda函数是不被赞同的,也许你在你的工作场所遇到了同样的问题。因此,我想出了这个解决方案,只要你自己的函数逻辑正确,它就可以适用于任意数量的输入或输出列。
import functools # not required, but helps in production
def unpack_df_columns(func):
    """
    A general use decorator to unpack a df[subset] of columns
    into a function which expects the values at those columns
    as arguments
    """
    
    @functools.wraps(func)
    def _unpack_df_columns(*args, **kwargs):
        
        # args[0] is a pandas series equal in length as the 
        # df[subset] to which the apply function is applied 
        series = args[0]

        # series.values holds the number of arguments expected
        # by func and is os length len(df[subset].columns)
        return func(*series.values)

    return _unpack_df_columns

@unpack_df_columns
def two_arg_func(a, b):
    return pd.Series((a+b, a*b))

@unpack_df_columns
def three_arg_func(x, y, z):
    return x+y+z

df["x_y_z_sum"] = df[['x', 'y', 'z']].apply(three_arg_func, axis=1)

df[["a_b_sum", "a_b_prod"]] = df[['a', 'b']].apply(two_arg_func, axis=1)

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