Pandas DataFrame使用多列聚合函数

use*_*817 63 python pandas

有没有办法编写DataFrame.agg方法中使用的聚合函数,可以访问聚合的多个数据列?典型的用例是加权平均加权标准偏差函数.

我希望能够写出类似的东西

def wAvg(c, w):
    return ((c * w).sum() / w.sum())

df = DataFrame(....) # df has columns c and w, i want weighted average
                     # of c using w as weight.
df.aggregate ({"c": wAvg}) # and somehow tell it to use w column as weights ...
Run Code Online (Sandbox Code Playgroud)

Wes*_*ney 85

是; 使用该.apply(...)函数,该函数将在每个子函数上调用DataFrame.例如:

grouped = df.groupby(keys)

def wavg(group):
    d = group['data']
    w = group['weights']
    return (d * w).sum() / w.sum()

grouped.apply(wavg)
Run Code Online (Sandbox Code Playgroud)

  • 如果我们想要计算许多变量(列)的wavg,例如除了df ['weights']之外的所有内容,该怎么办? (4认同)
  • @Wes McKinney:在你的书中,你建议采用这种方法:`get_wavg = lambda g:np.average(g ['data'],weights = g ['weights'])`; `grouped.apply(wavg)`这两个可以互换吗? (3认同)
  • @Wes,有没有办法可以用`agg()`和`lambda`围绕`np.average(... weights = ...)`建立,或者在pandas中用于加权均值的任何新原生支持既然这篇文章首次出现? (2认同)

Iya*_*Lin 12

这是一个具有以下优点的解决方案:

  1. 您不需要提前定义函数
  2. 您可以在管道中使用它(因为它使用 lambda)
  3. 您可以命名结果列

df.groupby('group')
  .apply(lambda x: pd.Series({
'weighted_average': np.average(x.data, weights = x.weights)})
Run Code Online (Sandbox Code Playgroud)

您还可以使用相同的代码来执行多个聚合:

df.groupby('group')
  .apply(lambda x: pd.Series({
'weighted_average': np.average(x.data, weights = x.weights), 
'regular_average': np.average(x.data)}))
Run Code Online (Sandbox Code Playgroud)


Ted*_*rou 10

可以从 groupby 对象返回任意数量的聚合值apply。简单地,返回一个系列,索引值将成为新的列名。

让我们看一个简单的例子:

df = pd.DataFrame({'group':['a','a','b','b'],
                   'd1':[5,10,100,30],
                   'd2':[7,1,3,20],
                   'weights':[.2,.8, .4, .6]},
                 columns=['group', 'd1', 'd2', 'weights'])
df

  group   d1  d2  weights
0     a    5   7      0.2
1     a   10   1      0.8
2     b  100   3      0.4
3     b   30  20      0.6
Run Code Online (Sandbox Code Playgroud)

定义将传递给apply. 它隐式地接受一个 DataFrame - 这意味着data参数是一个 DataFrame。请注意它如何使用多列,这在agggroupby 方法中是不可能的:

def weighted_average(data):
    d = {}
    d['d1_wa'] = np.average(data['d1'], weights=data['weights'])
    d['d2_wa'] = np.average(data['d2'], weights=data['weights'])
    return pd.Series(d)
Run Code Online (Sandbox Code Playgroud)

apply使用我们的自定义函数调用 groupby方法:

df.groupby('group').apply(weighted_average)

       d1_wa  d2_wa
group              
a        9.0    2.2
b       58.0   13.2
Run Code Online (Sandbox Code Playgroud)

您可以通过将加权总数预先计算到新的 DataFrame 列中来获得更好的性能,如其他答案中所述,并避免apply完全使用。


Ern*_*ler 6

我的解决方案与Nathaniel的解决方案类似,只是用于单个列,并且我不会每次都深度复制整个数据帧,这可能会非常慢。解决方案groupby(...)。apply(...)的性能增益约为100x(!)

def weighted_average(df, data_col, weight_col, by_col):
    df['_data_times_weight'] = df[data_col] * df[weight_col]
    df['_weight_where_notnull'] = df[weight_col] * pd.notnull(df[data_col])
    g = df.groupby(by_col)
    result = g['_data_times_weight'].sum() / g['_weight_where_notnull'].sum()
    del df['_data_times_weight'], df['_weight_where_notnull']
    return result
Run Code Online (Sandbox Code Playgroud)

  • 但请注意 df 不是内部对象。它是函数的一个参数,只要你从不给它赋值(`df=something`),它仍然是一个浅拷贝并就地更改。在这种情况下,列将被添加到 DataFrame 中。尝试复制粘贴此函数并在没有“del”行的情况下运行它,并查看它通过添加列来更改给定的 DataFrame。 (2认同)

san*_*ton 5

我经常这样做,发现以下内容非常方便:

def weighed_average(grp):
    return grp._get_numeric_data().multiply(grp['COUNT'], axis=0).sum()/grp['COUNT'].sum()
df.groupby('SOME_COL').apply(weighed_average)
Run Code Online (Sandbox Code Playgroud)

这将计算 中所有数字列的加权平均值,df并删除非数字列。