在Pandas数据框的多个列上应用多个聚合

时间:2019-08-27 15:39:14

标签: python pandas

我有一个如下数据框:

+-----------------------+
|  id  | weight | value |
+-----------------------+
|  i1  |   1    |   0   |
|  i1  |   2    |   3   |
|  i1  |   3    |   6   |
|  i2  |   1    |   2   |
|  i2  |   2    |   2   |
|  i3  |   2    |   2   |
+-----------------------+

,我想做几个汇总,以根据id计算以下内容:

  • 平均加权值
  • 总价值
  • 不为零的值数

预期输出如下:

+------------------------------------------+
|  new_id  | avg_val | val_sum | val_count |
+------------------------------------------+
|    i1    |    4    |    9    |     2     |
|    i2    |    2    |    4    |     2     |
|    i3    |    2    |    2    |     1     |
+------------------------------------------+

请注意,id列名称是输入,可能与旧输入不同或相似。

我知道我可以通过多种方法实现这一目标,但是知道我们正在处理的数据量非常大时,推荐的最快的方法是什么?

我想到的可能的解决方案:

  1. 为每个聚合分组并合并

    in_df = pd.DataFrame({
        'id': ['i1', 'i1', 'i1', 'i2', 'i2', 'i3'],
        'weight': [1, 2, 3, 1, 2, 2],
        'value': [0, 3, 6, 2, 2, 2]
    })
    
    out_df = pd.DataFrame()
    out_df['new_id'] = in_df['id'].unique()
    
    grouped_df = in_df.groupby('id').apply(lambda group: (group['weight'] * group['value']).sum() / max(group['weight'].sum(), 0.001)).reset_index(name='avg_val')
    out_df = pd.merge(out_df,
                      grouped_df,
                      left_on='new_id',
                      right_on='id',
                      how='left')
    out_df.drop('id')  # Dangerous if the `new_id` name is similar to `id`
    
    # Go on like this for every aggregation ...
    
    print(out_df)
    
  2. 分组并为每个聚合更新

    in_df = pd.DataFrame({
        'id': ['i1', 'i1', 'i1', 'i2', 'i2', 'i3'],
        'weight': [1, 2, 3, 1, 2, 2],
        'value': [0, 3, 6, 2, 2, 2]
    })
    
    out_df = pd.DataFrame(columns=['new_id', 'avg_val', 'val_sum', 'val_count'])
    out_df['new_id'] = in_df['id'].unique()
    out_df = out_df.set_index('new_id')
    
    grouped_df = in_df.groupby('id').apply(lambda group: (group['weight'] * group['value']).sum() / max(group['weight'].sum(), 0.001)).reset_index(name='avg_val')
    grouped_df = grouped_df.set_index('id')
    
    out_df.update(grouped_df)
    
    # Go on like this for every aggregation ...
    
    print(out_df)
    

1 个答案:

答案 0 :(得分:2)

您需要:

res = df.assign(wv = df['weight'].mul(df['value'])).groupby('id').agg({
    'wv': 'sum',
    'weight': 'sum',
    'value':['sum', np.count_nonzero]
})
res['avg_val'] = res['wv'] / res['weight']
res = res.drop(['wv', 'weight'],1) 
res.columns = ['val_sum', 'val_count', 'avg_val']

输出:

        val_sum  val_count  avg_val
  id                                 
  i1          9          2      4.0
  i2          4          2      2.0
  i3          2          1      2.0

您的两个问题都使用apply + lambda,这样您就可以遍历每个组(在许多其他事物中),而此代码则无此作用。这就是为什么它更快,更有效。