在 Pandas 中进行分组、加权、列均值

5

我有一个Pandas DataFrame,其中包含两个数值列和两个权重列。我想生成第三列,该列是这两个数值列加权平均后得到的结果,并按组进行分组。

例如:

df = pd.DataFrame({'category':['a','a','b','b'],
  'var1':np.random.randint(0,100,4),
  'var2':np.random.randint(0,100,4),
  'weights1':np.random.random(4),
  'weights2':np.random.random(4)})
df
  category  var1  var2  weights1  weights2
0        a    84    45  0.955234  0.729862
1        a    49     5  0.225470  0.159662
2        b    77    95  0.957212  0.991960
3        b    27    65  0.491877  0.195680

我希望你能够帮我完成以下事项:

df
  category  var1  var2  weights1  weights2    average
0        a    84    45  0.955234  0.729862  67.108023
1        a    49     5  0.225470  0.159662  30.759124
2        b    77    95  0.957212  0.991960  86.160443
3        b    27    65  0.491877  0.195680  37.814851

我已经使用算术运算符完成了这个操作,就像这样:


df['average'] = df.groupby('category', group_keys=False) \
  .apply(lambda g: (g.weights1 * g.var1 + g.weights2 * g.var2) / (g.weights1 + g.weights2))

但我希望将其推广到使用numpy.average,这样我可以例如取3列或更多列的加权平均值。

我正在尝试类似于以下内容,但似乎不起作用:

df['average'] = df.groupby('category', group_keys=False) \
  .apply(lambda g: np.average([g.var1, g.var2], axis=0, weights=[g.weights1, g.weights2]))

返回

TypeError: incompatible index of inserted column with frame index

有人可以帮我完成这个任务吗?


1
你想让这个新列与原始数据框如何对齐?由于分组后的值比原始数据框少,Pandas不知道如何将新列与原始数据框对齐。你能提供一个数据框的示例和期望的输出吗? - Edeki Okoh
值的数量应该相同,因为它只是在每个组中已经存在的列上进行加权平均。我会添加一个示例数据框和输出。 - jtanman
3个回答

4

我认为这里甚至不需要使用 groupby。请注意,这与使用 apply + lambda 的输出匹配。

尝试这个:

col=df.drop('category',1)
s=col.groupby(col.columns.str.findall(r'\d+').str[0],axis=1).prod().sum(1)
s/df.filter(like='weight').sum(1)
Out[33]: 
0    67.108014
1    30.759168
2    86.160444
3    37.814871
dtype: float64

在这种情况下使用“like”是一个好方法。我过滤了非加权列,但我没有意识到那是一个选项。 - Edeki Okoh
如果您只是使用权重和变量列的最后一个字符来确定要乘以哪个,那么这仅适用于多达9个变量和权重列。 - danielR9

0

由于数据框中每行的平均值列都有一个值,因此您实际上不需要进行分组。您只需要一种动态计算可变数量的'varXXX'列的平均值的方法。

下面的答案依赖于相同数量的'var'列和'weights'列,并具有一致的命名模式,因为它构造了列名称字符串

df = pd.DataFrame({'category': ['a', 'a', 'b', 'b'],
                   'var1': np.random.randint(0, 100, 4),
                   'var2': np.random.randint(0, 100, 4),
                   'var3': np.random.randint(0, 100, 4),
                   'weights1': np.random.random(4),
                   'weights2': np.random.random(4),
                   'weights3': np.random.random(4)
                   })

n_cols = len([1 for i in df.columns if i[:3] == 'var'])

def weighted_av_func(x):
    numerator = 0
    denominator = 0
    for i in range(1, n_cols + 1):
        numerator += x['var{}'.format(i)] * x['weights{}'.format(i)]
        denominator += x['weights{}'.format(i)]
    return numerator / denominator

df['average'] = df.apply(weighted_av_func, axis=1)

print(df)

  category  var1  var2  var3  weights1  weights2  weights3    average
0        a    53    58     2  0.101798  0.073881  0.919632  10.517238
1        a    52     0    26  0.073988  0.816425  0.888792  15.150578
2        b    30    78    46  0.641875  0.029402  0.370237  37.042735
3        b    36    72    92  0.186941  0.663270  0.774427  77.391136

编辑: 如果您想使用np.average,并且可以保证数据框中var列和weights列的顺序,则可以执行以下操作:

df['np_average'] = df.apply(
lambda x: np.average(a=x[1:1 + n_cols], 
                     weights=x[n_cols + 1:2 * n_cols + 1]), 
                     axis=1)

0
这是一种方法:
import numpy as np
import pandas as pd

df = pd.DataFrame({'category': ['a', 'a', 'b', 'b'],
                   'var1': np.random.randint(0, 100, 4),
                   'var2': np.random.randint(0, 100, 4),
                   'weights1': np.random.random(4),
                   'weights2': np.random.random(4)})

df_averages = df[df.columns.difference(['category', 'var1', 'var2'])]

输出:

    weights1    weights2
0   0.002812    0.483088
1   0.159774    0.818346
2   0.285366    0.586706
3   0.427240    0.428667

df_averages['Average'] = df_averages.mean(axis=1)

输出:

    weights1    weights2    Average
0   0.002812    0.483088    0.242950
1   0.159774    0.818346    0.489060
2   0.285366    0.586706    0.436036
3   0.427240    0.428667    0.427954

df['Averages'] = df_averages['Average'].astype(float)

输出:

  category  var1    var2    weights1    weights2    Averages
0   a        60      22     0.002812    0.483088    0.242950
1   a        66      63     0.159774    0.818346    0.489060
2   b        18      10     0.285366    0.586706    0.436036
3   b        68      32     0.427240    0.428667    0.427954

从数据框中删除非加权列并将加权列移动到新的数据框中。然后,您可以在该数据框的行上应用平均值,并将其合并回去,因为索引仍将保持不变。


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