【问题标题】:Pandas: Passing two columns to an expanding_apply function熊猫:将两列传递给扩展应用函数
【发布时间】:2015-08-10 16:07:04
【问题描述】:

我有数据框:

import pandas as pd
id = [0,0,0,0,0,1,1,1,1,1]
value = [1,3,2,5,4,4,3,2,1,5]
test = pd.DataFrame(zip(id, value), columns = ['id', 'value'])

我想要一个扩展的 apply 函数来识别我们是否达到给定 id 的新最大值。生成的数据框应如下所示:

   id  value  new_max
0   0      1        1    
1   0      3        1    
2   0      2        0    
3   0      5        1    
4   0      4        0    
5   1      4        1    
6   1      3        0    
7   1      2        0    
8   1      1        0    
9   1      5        1

我似乎无法将两列传递给扩展应用函数。

我尝试创建一个新列:

test['id_value'] = zip(test['id'], test['value'])

然后传递元组:

def new_max(x):
    v, w = list(zip(*x)[0]), list(zip(*x)[1])
    last_id = v[-1]
    last_value = w[-1]
    if any(j >= last_value for j in [w[i] for i, k in enumerate(v[0:-1]) if k == last_id]):
        return 0
    else:
        return 1


test['new_max'] = test['id_value'].apply(lambda x: pd.expanding_apply(x, new_max))

但我得到了错误:

AttributeError: 'tuple' object has no attribute 'dtype'

任何建议将不胜感激!


一种绕过两列的解决方案 (虽然通过两列知道如何做到这一点仍然很好)

def new_max2(x):
    if any(j >= x[-1] for j in x[0:-1]):
        return 0
    else:
        return 1

test.groupby('id')['value'].apply(lambda x: pd.expanding_apply(x, new_max2))

【问题讨论】:

  • 如果你有一个新的最大值,后面紧跟着重复相同的值,例如[5,1,2,3,6,6],您希望两个 6 都有 1 还是只有第一个? (我假设您只想要第一个,因为它是设置新最大值的那个。)
  • @DSM(只有第一个,我写错了代码,所以我会编辑)感谢您的回复。我只是想通了,其实。绕过两列的传递,但如果有人知道如何做到这一点,它可能对其他有这个问题的人有所帮助。我会在上面发布我的特定解决方案和问题。

标签: pandas lambda


【解决方案1】:

这解决了问题,而不是传递多列的一般问题:我会使用groupbycummax,然后看看我们是否达到了新值。例如:

grouped = df.groupby("id")["value"]
cummax = grouped.cummax()
cummax_is_new_value = cummax != cummax.groupby(df.id).shift()
df["new_max"] = cummax_is_new_value.astype(int)

给我

>>> df
    id  value  new_max
0    0      1        1
1    0      3        1
2    0      2        0
3    0      5        1
4    0      4        0
5    1      4        1
6    1      3        0
7    1      2        0
8    1      1        0
9    1      5        1
10   2      1        1
10   2      1        0
10   2      0        0
10   2      1        0
10   3      1        1

最初我只是检查该值是否与前一个值相同,但在 [1, 0, 1] 之类的情况下失败了,其中第二个 1 既等于累积最大值,又不等于以前的值。这样,我们始终使用分组的累积值,因此我们实际上只是按组获取新的累积值。

【讨论】:

  • 谢谢@DSM,比我的解决方案优雅得多。
【解决方案2】:

自从我与apply 合作以来已经有很长时间了,至少之前的几个版本,所以我的回忆可能很糟糕,或者事情可能已经改变了。但是,我记得分组数据作为第一个参数自动传递。

将自己的函数传递给apply 时的诱惑是这样做:

def user_func(df, arg1, arg2):
    return whatever_you_like

DF = pd.DataFrame(your_data)

DF.groupby('col1').appy(user_func(arg1, arg2))

但这不是正确的语法。事实上,最后一行的正确语法是

DF.groupby('col1').apply(user_func, arg1, arg2)

expanding_apply 是否以相同的方式工作我不知道,这可能完全过时,但可能值得一试。

【讨论】:

    【解决方案3】:

    我认为是使用列表推导的更通用的解决方案(未矢量化):

    假设您有 2 列,a 和 b。

    df = pd.DataFrame(dict(a=[1,2,3], b=[4,5,6]))
    

    而你要计算and和b之和的运行比例

    def myFunc(df): 
        return df["a"].sum() / df["b"].sum()
    

    这是一个可行的解决方案,前提是您的函数采用数据框并输出单个值

    df.assign(sum_a_sum_b_ratio = [myFunc(d) for d in df.expanding()])
    
        a   b   sum_a_sum_b_ratio
    0   1   4   0.250000
    1   2   5   0.333333
    2   3   6   0.400000
    

    【讨论】:

      猜你喜欢
      • 2013-10-19
      • 2016-08-07
      • 1970-01-01
      • 2022-08-10
      • 1970-01-01
      • 2015-01-13
      • 2022-01-23
      • 2021-02-12
      • 1970-01-01
      相关资源
      最近更新 更多