我想从另一个groupby
的结果中创建一个新的数据框。结果应该每组有一行(基本上是矢量化map-reduce),新列名与现有名称无关。这似乎是agg
的自然用途,但它似乎只产生现有列。
d = pd.DataFrame({'a': [0,0,1,1], 'b': [3,4,5,6], 'c': [7,8,9,0]})
a b c
0 0 3 7
1 0 4 8
2 1 5 9
3 1 6 0
agg()
将使用系列创建新列:
d.groupby('a')['b'].agg({'x': lambda g: g.sum()})
x
a
0 7
1 11
但令人沮丧的是没有使用DataFrame:
d.groupby('a').agg({'x': lambda g: g.b.sum()})
KeyError: 'x'
我可以通过从apply()
返回一行DataFrame来实现:
d.groupby('a').apply(lambda g: pd.DataFrame([{'x': g.b.mean(), 'y': (g.b * g.c).sum()}])).reset_index(level=1, drop=True)
x y
a
0 3.5 53
1 5.5 45
但是这很丑陋,正如您可以想象的那样,为每一行创建一个新的dict,list和DataFrame对于即使是适度大小的输入也是很慢的。
答案 0 :(得分:0)
也许这可以帮助
df = d.groupby('a')[['b','c']].sum()
df.index.name = None
df.columns=['b_sum','c_sum']
或者如果您想计算单个列的单独统计信息,您可以执行类似这样的操作
df = d.groupby('a')[['b','c']].apply(lambda x: (x.b.mean(),x.c.sum())).apply(pd.Series)
答案 1 :(得分:0)
以下是几种不同方法的比较。我更喜欢回归系列赛;合理简洁,清晰,高效。感谢@Siraj S的灵感。
df = pd.DataFrame(np.random.rand(1000000, 5), columns=list('abcde'))
grp = df.groupby((df.a * 100).astype(int))
%timeit grp.apply(lambda g: pd.DataFrame([{'n': g.e.count(), 'x': (g.b * g.c).sum() / g.c.sum(), 'y': g.d.mean(), 'z': g.e.std()}])).reset_index(level=1, drop=True)
1 loop, best of 3: 328 ms per loop
%timeit grp.apply(lambda g: (g.e.count(), (g.b * g.c).sum() / g.c.sum(), g.d.mean(), g.e.std())).apply(pd.Series)
1 loop, best of 3: 266 ms per loop
%timeit grp.apply(lambda g: pd.Series({'n': g.e.count(), 'x': (g.b * g.c).sum() / g.c.sum(), 'y': g.d.mean(), 'z': g.e.std()}))
1 loop, best of 3: 265 ms per loop
%timeit grp.apply(lambda g: {'n': g.e.count(), 'x': (g.b * g.c).sum() / g.c.sum(), 'y': g.d.mean(), 'z': g.e.std()}).apply(pd.Series)
1 loop, best of 3: 273 ms per loop
%timeit pd.concat([grp.apply(lambda g: g.e.count()), grp.apply(lambda g: (g.b * g.c).sum() / g.c.sum()), grp.apply(lambda g: g.d.mean()), grp.apply(lambda g: g.e.std())], axis=1)
1 loop, best of 3: 708 ms per loop