使用 groupby 变换从特定行中减去值

Posted

技术标签:

【中文标题】使用 groupby 变换从特定行中减去值【英文标题】:Subtract value from particular row using groupby transform 【发布时间】:2020-01-10 10:12:52 【问题描述】:

有一个包含多个组的数据框(Id 列)。每个组内有多个级别(Level 列)。所有组都有一个名为'Base' 的级别。对于每个组,我想从所有其他级别的值中减去 'Base' 值。

使用pandas.join 和一点点来回我能够得到我想要的。

import pandas as pd

df = pd.DataFrame('Id':['A', 'A', 'A', 'B', 'B', 'B'],
                   'Level':['Down', 'Base', 'Up', 'Base', 'Down', 'Up'],
                   'Value':[8, 10, 15, 6, 3, 8]
                   ).set_index('Id')

df = df.join(df[df['Level']=='Base']['Value'], rsuffix='_Base')
df['Delta'] = df['Value'] - df['Value_Base']
df.drop('Value_Base', inplace=True, axis=1)

#The input
df_in
Out[3]: 
   Level  Value
Id             
A   Down      8
A   Base     10
A     Up     15
B   Base      6
B   Down      3
B     Up      8

# The output after the above operation (and hopefully after a groupby.transform)
df_out
Out[4]: 
   Level  Value  Delta
Id                    
A   Down      8     -2
A   Base     10      0
A     Up     15      5
B   Base      6      0
B   Down      3     -3
B     Up      8      2

我猜上面的解决方案还不错,但我希望使用groupbytransform 可以达到相同的结果。我试过了

df_in.groupby('Id').transform(lambda x : x['Value'] - x[x['Level']=='Base']['Value'])

但这没有用。谁能告诉我我做错了什么?

【问题讨论】:

这基本上就是我目前正在做的事情。问题实际上是关于如何使用groupby.transform 来达到同样的效果。 【参考方案1】:

没有变形,但我觉得很酷:

df['Delta']=df['Value']-df.pivot(columns='Level')['Value']['Base']

   Level  Value  Delta
Id                    
A   Down      8     -2
A   Base     10      0
A     Up     15      5
B   Base      6      0
B   Down      3     -3
B     Up      8      2

【讨论】:

不错。无论是否转换,您都可以通过一条易于阅读的行来实现所需的结果。 +1【参考方案2】:

如果每个组确实需要transform 并且始终需要Base,一种可能的解决方案是创建MultiIndex,然后通过xs 选择:

df['Delta'] =df['Value'] - (df.set_index('Level', append=True)
                              .groupby(level=0)['Value']
                              .transform(lambda x:  x.xs('Base', level=1)[0])
                              .values)
print (df)
   Level  Value  Delta
Id                    
A   Down      8     -2
A   Base     10      0
A     Up     15      5
B   Base      6      0
B   Down      3     -3
B     Up      8      2

如果组中不存在某些Base,也可以使用类似的解决方案:

f = lambda x:  next(iter(x.xs('Base', level=1)), np.nan)
df = df.set_index('Level', append=True)
df['Delta']  = df['Value'] - df.groupby(level=0)['Value'].transform(f)
df = df.reset_index(level=1)                           
print (df)
   Level  Value  Delta
Id                    
A   Down      8     -2
A   Base     10      0
A     Up     15      5
B   Base      6      0
B   Down      3     -3
B     Up      8      2

更好的解决方案是:

df['Delta'] = df['Value'] - df.index.map(df.loc[df['Level'].eq('Base'), 'Value'])
print (df)
   Level  Value  Delta
Id                    
A   Down      8     -2
A   Base     10      0
A     Up     15      5
B   Base      6      0
B   Down      3     -3
B     Up      8      2

【讨论】:

太棒了。根据您和 Billy Bonnaros 的回答,transform 函数似乎不是这里的最佳选择。

以上是关于使用 groupby 变换从特定行中减去值的主要内容,如果未能解决你的问题,请参考以下文章

从 SQLite 中的所有行中减去平均值

Groupby & Sum 从一个特定值的出现到另一个特定值或相同值的出现

如何从特定小时范围内的行中选择最小值?

从余额行中递归减去存款

如何从熊猫数据框中的当前行中减去前一行并将其应用于每一行;不使用循环?

减去前一个数据帧的当前值