【问题标题】:Applying similar functions across multiple columns in python/pandas在 python/pandas 中跨多个列应用类似的函数
【发布时间】:2015-10-23 22:29:00
【问题描述】:

问题:鉴于下面的数据框,我正在尝试编写将函数应用于三个不同列的代码,而无需编写三个单独的函数调用。

数据代码:

import pandas as pd
data = {'name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'],
    'days': [365, 365, 213, 318, 71],
    'spend_30day': [22, 241.5, 0, 27321.05, 345],
    'spend_90day': [22, 451.55, 64.32, 27321.05, 566.54],
    'spend_365day': [854.56, 451.55, 211.65, 27321.05, 566.54]}

df = pd.DataFrame(data)
cols = df.columns.tolist()
cols = ['name', 'days', 'spend_30day', 'spend_90day', 'spend_365day']
df = df[cols]
df

下面的函数实质上将按年计算支出;如果某人在“天数”列中的天数少于 365 天,则以下函数将告诉我如果他们有 365 天会花费多少:

def annualize_spend_365(row):
    if row['days']/(float(365)) < 1:
        return (row['spend_365day']/(row['days']/float(365)))
    else:
        return row['spend_365day']

然后我将函数应用于特定列:

df.spend_365day = df.apply(annualize_spend_365, axis=1).round(2)
df

这完全符合我对那一列的要求。但是,我不想为三个不同的“支出”列(30、90、365)中的每一个重写这个。我希望能够编写代码,将这个函数概括并一次应用到多个列。

我以为我可以创建列及其各自日期的列表,使用“zip”函数,并将函数嵌套在 for 循环中,但我在下面的尝试最终失败了:

spend_cols = [df.spend_30day, df.spend_90day, df.spend_365day]
days_list = [30, 90, 365]

for col, day in zip(spend_cols, days_list):
    def annualize_spend(row):
        if (row.days/(float(day)) < 1:
            return (row.col)/((row.days)/float(day))
        else:
            return row.col
    col = df.apply(annualize_spend, axis = 1)

错误:

AttributeError: ("'Series' object has no attribute 'col'")

我不确定为什么循环方法会失败。无论如何,我希望获得有关如何在 pandas 中泛化函数应用的指导。提前致谢!

【问题讨论】:

    标签: python function for-loop pandas


    【解决方案1】:

    看看你的两个函数定义:

    def annualize_spend_365(row):
        if row['days']/(float(365)) < 1:
            return (row['spend_365day']/(row['days']/float(365)))
        else:
            return row['spend_365day']
    

    #col in [df.spend_30day, df.spend_90day, df.spend_365day]
    def annualize_spend(row):
        if (row.days/(float(day)) < 1:
            return (row.col)/((row.days)/float(day))
        else:
            return row.col
    

    看到区别了吗?一方面,在第一种情况下,您访问具有显式字段名称的字段,并且它可以工作。在第二种情况下,您尝试访问 row.col,但它失败了,但在这种情况下,col 假定 中的相应字段 df。而是尝试

    spend_cols = ['spend_30day', 'spend_90day', 'spend_365day']
    

    在你的循环之前。另一方面,在语法df.days 中,字段名实际上是“days”,但在df.col 中,字段名不是字符串“col”,而是字符串@ 的value 987654329@。所以你可能想在后一种情况下使用row[col]。无论如何,我不确定将col 作为循环内的输出变量而不是col 是多么明智。


    我不熟悉pandas.DataFrame.apply,但可能使用单个函数定义,它将天数和感兴趣的字段作为输入变量:

    def annualize_spend(col,day,row):
        if (row['days']/(float(day)) < 1:
            return (row[col])/((row['days'])/float(day))
        else:
            return row[col]
    
    spend_cols = ['spend_30day', 'spend_90day', 'spend_365day']
    days_list = [30, 90, 365]
    
    for col, day in zip(spend_cols, days_list):
        col = df.apply(lambda row,col=col,day=day: annualize_spend(col,day,row), axis = 1)
    

    lambda 将确保您的函数只有一个输入参数在获得applyd 时悬空。

    【讨论】:

    • 工作就像一个魅力。我认为我的问题是概念性的,因此感谢您概述了一个更连贯的计划。如果有时间,能否解释一下 lambda 函数中的参数(row, col=col, day=day)?
    • @UncleMilton 肯定:) lambda 只能使用显式传递给它的变量,因此您将 colday 传递给它。以这种方式命名 lambda 的参数是一件懒惰的事情,可能这样会更清楚:lambda var1,var2=col,var3=day: annualize_spend(var2,var3,var1)。因此,您为 lambda 的 last 两个参数设置默认值,从而有效地将其呈现为 apply 的单输入函数。由于这些只是默认值,lambda 也可以在 2 或 3 输入语法中工作,但 apply 只使用单个变量,因此它最多必须有 1 个非默认参数。
    • :哇,非常感谢您的解释。还有一个(一般)问题:为什么将 lambda 传递给 apply 函数而不仅仅是函数本身?这就是为什么 df.apply(lambda row, col=col, day=day: Annualize_spend(col, day, row) 而不仅仅是 df.apply(annualize_spend) ?当函数具有已经创建了吗?(我已经看到这种方法用于更简单的函数,并且很好奇为什么在函数已经创建时调用 lambda 是必要的)。再次感谢,非常有帮助!
    • @UncleMilton 在没有 lambda 的情况下尝试它:) 你应该得到一个错误,你的函数需要 3 个参数,并且只指定了 1,你的函数应该如何知道 colday 是什么?函数定义中的名称非常随意,例如我使用lambda row,col=col,day=day: ...,因此函数定义中的变量名称无济于事(它只能帮助程序员)。所以这仅仅是因为apply 需要一个单输入函数,然后将其应用于变量。
    • 好吧,我想我明白了:使用多参数lambda 是您绕过apply 的单参数约束的方法。当想要使用applymapapplymap 应用任何 多参数函数时,可以推广这种方法。即,首先显式创建多参数函数。然后将该函数中的每个参数指定为 lambda 中的变量。最后,通过调用函数(连同每个参数)来完成 lambda。再次,非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-20
    • 2023-04-08
    • 2018-05-02
    相关资源
    最近更新 更多