将多个函数应用于多个groupby列
该文档展示了如何在输出列名称作为关键字的情况下,使用dict一次对groupby对象应用多个函数:
In [563]: grouped['D'].agg({'result1' : np.sum, .....: 'result2' : np.mean}) .....: Out[563]: result2 result1 A bar -0.579846 -1.739537 foo -0.280588 -1.402938
但是,这只适用于系列groupby对象。 而且当一个字典同样通过DataFrame传递给一个组时,它期望这个键是该函数将被应用到的列名。
我想要做的是将多个函数应用于多个列(但某些列将被多次操作)。 此外, 一些函数将取决于groupby对象中的其他列 (如sumif函数)。 我目前的解决scheme是逐列,并执行类似上面的代码,使用lambdas函数依赖于其他行。 但是这需要很长时间(我认为遍历groupby对象需要很长时间)。 我将不得不改变它,这样我遍历整个groupby对象在一次运行,但我想知道是否有一个在pandas内置的方式做到这一点干净。
例如,我试过类似的东西
grouped.agg({'C_sum' : lambda x: x['C'].sum(), 'C_std': lambda x: x['C'].std(), 'D_sum' : lambda x: x['D'].sum()}, 'D_sumifC3': lambda x: x['D'][x['C'] == 3].sum(), ...)
但正如所料,我得到一个KeyError(因为如果从DataFrame调用agg
,键必须是一个列)。
是否有任何内置的方法来做我想做的事情,或者可能会添加这个function,或者我只需要手动遍历groupby?
谢谢
对于第一部分,您可以传递键的列名称的字典和值的函数列表:
In [28]: df Out[28]: ABCDE GRP 0 0.395670 0.219560 0.600644 0.613445 0.242893 0 1 0.323911 0.464584 0.107215 0.204072 0.927325 0 2 0.321358 0.076037 0.166946 0.439661 0.914612 1 3 0.133466 0.447946 0.014815 0.130781 0.268290 1 In [26]: f = {'A':['sum','mean'], 'B':['prod']} In [27]: df.groupby('GRP').agg(f) Out[27]: AB sum mean prod GRP 0 0.719580 0.359790 0.102004 1 0.454824 0.227412 0.034060
更新1:
由于聚合函数在Series上起作用,所以对其他列名的引用将丢失。 为了解决这个问题,可以引用完整的数据框,并使用lambda函数中的组索引对其进行索引。
这是一个hacky的解决方法:
In [67]: f = {'A':['sum','mean'], 'B':['prod'], 'D': lambda g: df.ix[g.index].E.sum()} In [69]: df.groupby('GRP').agg(f) Out[69]: ABD sum mean prod <lambda> GRP 0 0.719580 0.359790 0.102004 1.170219 1 0.454824 0.227412 0.034060 1.182901
在这里,结果的“D”列由总和的“E”值组成。
更新2:
这里有一个方法,我认为会做你所要求的一切。 首先制作一个自定义的lambda函数。 下面,g引用组。 汇总时,g将是一个系列。 将df.ix[]
传递给df.ix[]
将从df中select当前组。 然后我testingC列是否小于0.5。 返回的布尔序列被传递给g[]
,它只select那些符合条件的行。
In [95]: cust = lambda g: g[df.ix[g.index]['C'] < 0.5].sum() In [96]: f = {'A':['sum','mean'], 'B':['prod'], 'D': {'my name': cust}} In [97]: df.groupby('GRP').agg(f) Out[97]: ABD sum mean prod my name GRP 0 0.719580 0.359790 0.102004 0.204072 1 0.454824 0.227412 0.034060 0.570441
目前接受的答案的后半部分已经过时,并有两个贬低。 首先也是最重要的,你不能再把字典的字典传给agg
groupby方法。 其次,不要使用.ix
。
如果您希望同时使用两个单独的列,我会build议使用apply
方法,将数据框传递给应用函数。 我们使用与上面类似的数据框
df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd')) df['group'] = [0, 0, 1, 1] df abcd group 0 0.418500 0.030955 0.874869 0.145641 0 1 0.446069 0.901153 0.095052 0.487040 0 2 0.843026 0.936169 0.926090 0.041722 1 3 0.635846 0.439175 0.828787 0.714123 1
从列名映射到聚合函数的字典仍然是执行聚合的完美方式。
df.groupby('group').agg({'a':['sum', 'max'], 'b':'mean', 'c':'sum', 'd': lambda x: x.max() - x.min()}) abcd sum max mean sum <lambda> group 0 0.560541 0.507058 0.418546 1.707651 0.129667 1 0.187757 0.157958 0.887315 0.533531 0.652427
如果你不喜欢这个丑陋的lambda列名称,你可以使用一个普通的函数,并提供一个自定义的名字,像这样的特殊的__name__
属性:
def max_min(x): return x.max() - x.min() max_min.__name__ = 'Max minus Min' df.groupby('group').agg({'a':['sum', 'max'], 'b':'mean', 'c':'sum', 'd': max_min}) abcd sum max mean sum Max minus Min group 0 0.560541 0.507058 0.418546 1.707651 0.129667 1 0.187757 0.157958 0.887315 0.533531 0.652427
使用apply
和返回一个系列
现在,如果你有多个需要一起交互的列,那么你不能使用agg
,这会隐式地将一个Series传递给聚合函数。 当使用apply
整个组作为一个DataFrame传递到函数。
我build议制作一个自定义函数,返回一系列所有的聚合。 使用Series索引作为新列的标签:
def f(x): d = {} d['a_sum'] = x['a'].sum() d['a_max'] = x['a'].max() d['b_mean'] = x['b'].mean() d['c_d_prodsum'] = (x['c'] * x['d']).sum() return pd.Series(d, index=['a_sum', 'a_max', 'b_mean', 'c_d_prodsum']) df.groupby('group').apply(f) a_sum a_max b_mean c_d_prodsum group 0 0.560541 0.507058 0.418546 0.118106 1 0.187757 0.157958 0.887315 0.276808
如果你爱上了MultiIndexes,你仍然可以像这样返回一个Series:
def f_mi(x): d = [] d.append(x['a'].sum()) d.append(x['a'].max()) d.append(x['b'].mean()) d.append((x['c'] * x['d']).sum()) return pd.Series(d, index=[['a', 'a', 'b', 'c_d'], ['sum', 'max', 'mean', 'prodsum']]) df.groupby('group').apply(f_mi) ab c_d sum max mean prodsum group 0 0.560541 0.507058 0.418546 0.118106 1 0.187757 0.157958 0.887315 0.276808