使用Pandas按组有条件地偏移值

7

我正在寻找一种更高效和可维护的方式来按组有条件地偏移值。最容易展示一个例子。

Offset == False时,值始终为非负数;当Offset == True时,值始终为负数。我希望通过Label将正值(向下取整到0)与负值“折叠”起来。

注意,Label + Offset组合始终是唯一的。由于Offset是布尔值,每个Label最多只能有2行。

示例1:

df = pd.DataFrame({'Label': ['L1', 'L2', 'L3', 'L3'],
                   'Offset': [False, False, False, True],
                   'Value': [100, 100, 50, -100]})

# input
#   Label Offset  Value
# 0    L1  False    100
# 1    L2  False    100
# 2    L3  False     50
# 3    L3   True   -100

期望的输出:

  Label Offset  Value
0    L1  False    100
1    L2  False    100
2    L3  False      0
3    L3   True    -50

示例2

df = pd.DataFrame({'Label': ['L1', 'L2', 'L3', 'L3'],
                   'Offset': [False, False, False, True],
                   'Value': [100, 100, 100, -50]})

# input
#   Label Offset  Value
# 0    L1  False    100
# 1    L2  False    100
# 2    L3  False    100
# 3    L3   True    -50

期望的输出结果:

  Label Offset  Value
0    L1  False    100
1    L2  False    100
2    L3  False     50
3    L3   True      0

当前低效的解决方案

我的当前解决方案是手动循环,速度慢且难以维护:

for label in df['Label'].unique():
    mask = df['Label'] == label
    if len(df.loc[mask]) == 2:
        val_false = df.loc[~df['Offset'] & mask, 'Value'].iloc[0]
        val_true = df.loc[df['Offset'] & mask, 'Value'].iloc[0]
        if val_false > abs(val_true):
            df.loc[~df['Offset'] & mask, 'Value'] += val_true
            df.loc[df['Offset'] & mask, 'Value'] = 0
        else:
            df.loc[~df['Offset'] & mask, 'Value'] = 0
            df.loc[df['Offset'] & mask, 'Value'] += val_false

我正在寻找一种矢量化,或至少部分矢量化的解决方案来提高性能并摆脱混乱。


如果有3个L3值(100,-25,-25),那么它将被翻译为50、0、0? - Yuca
@Yuca,我会添加一个注释,Label + Offset组合始终是唯一的。由于Offset是布尔值,因此每个标签最多只能有2行。 - jpp
3个回答

6
也许:
label_sums = df.Value.groupby(df.Label).transform(sum)
df["new_sum"] = label_sums.where(np.sign(label_sums) == np.sign(df.Value), 0)

这给了我

In [42]: df
Out[42]: 
  Label  Offset  Value  new_sum
0    L1   False    100      100
1    L2   False    100      100
2    L3   False     50        0
3    L3    True   -100      -50
4    L4   False    100      100
5    L5   False    100      100
6    L6   False    100       50
7    L6    True    -50        0

我有一个东西的要点。@jpp 你可以看到我的已删除帖子。为了我自己的缘故,我会尝试稍后再回来 (-: - piRSquared
@piRSquared,好的,谢谢。 - jpp
1
@jpp 目前我的已删除帖子完全是这个答案的 Numpy 版本的抄袭。 - piRSquared

4
这是我最好的解决方案:创建一个辅助列,用于确定汇总显示的位置,然后将组中的其他成员设置为0。
df['aux'] = abs(df['Value'])
idx = abs(df.groupby(['Label'])['aux'].transform(max)) == abs(df['aux'])
df['aux2'] = False
df.loc[idx,'aux2'] = True
df  = df.join(df.groupby('Label').Value.sum(), on='Label', rsuffix = 'jpp')
df.loc[df['aux2']==False, 'Valuejpp'] = 0
df = df.drop(['aux', 'aux2','Value'], axis = 1)

结果

      Label  Offset  Valuejpp
0    L1   False       100
1    L2   False       100
2    L3   False         0
3    L3    True       -50

1
191k声望和1k声望之间答案质量的差异确实存在,但它能够工作! :) - Yuca

4

DSM数据

df1=df.copy()

df.loc[df.Offset,'Value']=df.Value.abs()

s1=(df.groupby('Label').Value.diff().lt(0)).groupby(df['Label']).transform('any')
s2=df.groupby('Label').Value.transform('count')

s3=df1.groupby('Label').Value.transform('sum')
np.where(s2<=1,df1.Value,np.where(s1,s3*(-df1.Offset),s3*df1.Offset))

Out[757]: array([100, 100,   0, -50, 100, 100,  50,   0], dtype=int64)

你可以使用 np.select 来完成,我相信你知道怎么做,伙计。 - BENY
是的,我想我脑子短路了!但是 DSM 的解决方案在我看来也很聪明。 - jpp
@jpp 哈哈,我有时也会遇到这种情况。 - BENY

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