使用多列的Pandas DataFrame聚合函数
有没有办法像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 ...
是; 使用.apply(...)
函数,它将在每个子DataFrame
上DataFrame
。 例如:
grouped = df.groupby(keys) def wavg(group): d = group['data'] w = group['weights'] return (d * w).sum() / w.sum() grouped.apply(wavg)
以下(基于Wes McKinney的答案)完成了我正在寻找的东西。 我很高兴知道是否有更简单的方式在pandas
内部做到这一点。
def wavg_func(datacol, weightscol): def wavg(group): dd = group[datacol] ww = group[weightscol] * 1.0 return (dd * ww).sum() / ww.sum() return wavg def df_wavg(df, groupbycol, weightscol): grouped = df.groupby(groupbycol) df_ret = grouped.agg({weightscol:sum}) datacols = [cc for cc in df.columns if cc not in [groupbycol, weightscol]] for dcol in datacols: try: wavg_f = wavg_func(dcol, weightscol) df_ret[dcol] = grouped.apply(wavg_f) except TypeError: # handle non-numeric columns df_ret[dcol] = grouped.agg({dcol:min}) return df_ret
函数df_wavg()
返回一个按“groupby”列分组的dataframe,并返回权值列的权重总和。 其他列是加权平均值,如果是非数值,则使用min()
函数进行聚合。
我做了很多,发现以下非常方便:
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)
这将计算df
中所有数字列的加权平均值,并删除非数字列。
通过groupby(...).apply(...)
实现这个groupby(...).apply(...)
是非高性能的。 这是我一直使用的解决scheme(主要是使用卡鲁的逻辑)。
def grouped_weighted_average(self, values, weights, *groupby_args, **groupby_kwargs): """ :param values: column(s) to take the average of :param weights_col: column to weight on :param group_args: args to pass into groupby (eg the level you want to group on) :param group_kwargs: kwargs to pass into groupby :return: pandas.Series or pandas.DataFrame """ if isinstance(values, str): values = [values] ss = [] for value_col in values: df = self.copy() prod_name = 'prod_{v}_{w}'.format(v=value_col, w=weights) weights_name = 'weights_{w}'.format(w=weights) df[prod_name] = df[value_col] * df[weights] df[weights_name] = df[weights].where(~df[prod_name].isnull()) df = df.groupby(*groupby_args, **groupby_kwargs).sum() s = df[prod_name] / df[weights_name] s.name = value_col ss.append(s) df = pd.concat(ss, axis=1) if len(ss) > 1 else ss[0] return df pandas.DataFrame.grouped_weighted_average = grouped_weighted_average
我的解决scheme与Nathaniel的解决scheme类似,只是用于单列,并且我不会每次深度复制整个dataframe,这可能会非常慢。 (…)。apply(…)在解决scheme组上的性能增益大约是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
可以使用apply
从groupby对象返回任意数量的聚合值。 简单地说,返回一个系列,索引值将成为新的列名称。
我们来看一个简单的例子:
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
定义一个将被传递来apply
的自定义函数。 它隐含地接受一个DataFrame – 意味着data
参数是一个DataFrame。 注意它是如何使用多个列的,这在agg
groupby方法中是不可能的:
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)
用我们的自定义函数调用groupby apply
方法:
df.groupby('group').apply(weighted_average) d1_wa d2_wa group a 9.0 2.2 b 58.0 13.2
按照其他答案的说明,通过将加权合计计算到新的DataFrame列中,可以获得更好的性能,并避免使用全部apply
。