如何在Pandas中按组计算加权均值时忽略组内最小值和最大值?

5

我有一个类似于这样的数据框

pd.DataFrame({'A': ['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10'],
  ...:                    'B': ['A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C'],
  ...:                    'W': [0.5, 0.2, 0.3, 0.2, 0.1, 0.4, 0.3, 0.4, 0.5, 0.1],
  ...:                    'V': [9, 1, 7, 4, 3, 5, 2, 6, 8, 10]})
Out[9]: 
     A  B    W   V
0   C1  A  0.5   9
1   C2  A  0.2   1
2   C3  A  0.3   7
3   C4  B  0.2   4
4   C5  B  0.1   3
5   C6  B  0.4   5
6   C7  B  0.3   2
7   C8  C  0.4   6
8   C9  C  0.5   8
9  C10  C  0.1  10

我想要按照'B'列中的分组来计算加权平均值,忽略最小值和最大值(在'V'列中),其中:
W列 = 权重
V列 = 值
为了计算每个组的简单平均值,考虑所有的数值,我可以这样做:
df['mean'] = df.groupby('B').apply(lambda x: (x.V * (x.W / x.W.sum())).sum()).reindex(df.B).values
print(df)
     A  B    W   V  mean
0   C1  A  0.5   9   6.8
1   C2  A  0.2   1   6.8
2   C3  A  0.3   7   6.8
3   C4  B  0.2   4   3.7
4   C5  B  0.1   3   3.7
5   C6  B  0.4   5   3.7
6   C7  B  0.3   2   3.7
7   C8  C  0.4   6   7.4
8   C9  C  0.5   8   7.4
9  C10  C  0.1  10   7.4

然而,我想忽略每个组中的最大值和最小值来计算每个组的平均值。结果应该如下所示:

     A  B    W   V  meanNoMinMax
0   C1  A  0.5   9   7.0
1   C2  A  0.2   1   7.0
2   C3  A  0.3   7   7.0
3   C4  B  0.2   4   3.666667
4   C5  B  0.1   3   3.666667
5   C6  B  0.4   5   3.666667
6   C7  B  0.3   2   3.666667
7   C8  C  0.4   6   8.0
8   C9  C  0.5   8   8.0
9  C10  C  0.1  10   8.0

我该如何用一行代码(或者很少的几行)实现这个功能?

逻辑

在每个分组中忽略V中的最小值和最大值后,可以得到以下表格,用于计算每个分组中忽略最小值和最大值的平均值。

     A  B    W   V
1   C3  A  0.3   7
3   C4  B  0.2   4
4   C5  B  0.1   3
8   C9  C  0.5   8

1
为什么 A V==1 保留在那里? - BENY
谢谢您指出。我已经编辑了我的问题。 - idt_tt
1
如果您有两行具有最小值(或最大值),您会考虑两者还是只考虑一个出现? - Ben.T
3个回答

4

添加条件并修复您的代码。

df['mean'] = df.groupby('B').apply(lambda x: (x.V * (x.W[(x.V!=x.V.max()) & (x.V!=x.V.min())] / x.W[(x.V!=x.V.max()) & (x.V!=x.V.min())].sum())).sum()).reindex(df.B).values
df
Out[293]: 
     A  B    W   V      mean
0   C1  A  0.5   9  7.000000
1   C2  A  0.2   1  7.000000
2   C3  A  0.3   7  7.000000
3   C4  B  0.2   4  3.666667
4   C5  B  0.1   3  3.666667
5   C6  B  0.4   5  3.666667
6   C7  B  0.3   2  3.666667
7   C8  C  0.4   6  8.000000
8   C9  C  0.5   8  8.000000
9  C10  C  0.1  10  8.000000

3
创建要排除的行的掩码,然后执行乘法和 groupby + transform 求和操作。如果需要在出现多个行并列极值时仅排除一个极值,那么只需要修改 m 即可。
# Exclude all rows that are max or min within group
m = (df['V'].eq(df.groupby('B')['V'].transform('max'))
     | df['V'].eq(df.groupby('B')['V'].transform('min')))

# For ties, if only need to exclude the single largest/smallest:
#df = df.sort_values('V')
#m = ~df.duplicated('B') | ~df.duplicated('B', keep='last')

df['avg'] = (df['W'].mul(df['V']).mask(m).groupby(df['B']).transform('sum')
                .div(df['W'].mask(m).groupby(df['B']).transform('sum')))

     A  B    W   V       avg
0   C1  A  0.5   9  7.000000
1   C2  A  0.2   1  7.000000
2   C3  A  0.3   7  7.000000
3   C4  B  0.2   4  3.666667
4   C5  B  0.1   3  3.666667
5   C6  B  0.4   5  3.666667
6   C7  B  0.3   2  3.666667
7   C8  C  0.4   6  8.000000
8   C9  C  0.5   8  8.000000
9  C10  C  0.1  10  8.000000

0

另外,再加上几行代码,我认为这也可以工作 :)

max_per_group = df.index.isin(df.groupby("B")['V'].idxmax().values.tolist())
min_per_group = df.index.isin(df.groupby("B")['V'].idxmin().values.tolist())
df["mean"] = df.loc[(~df.index.isin(max_per_group)) & (~df.index.isin(min_per_group))].groupby('B').apply(lambda x: (x.V * (x.W / x.W.sum())).sum()).reindex(df.B).values

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