如何高效地从 Pandas 中的 groupby().agg() 或 groupby().apply() 创建全新的数据框?

3

我希望从另一个数据框的 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()会使用Series创建新的列:

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

但这样做很丑陋,而且你可以想象,为每一行创建一个新的字典、列表和数据框对于即使是中等规模的输入来说都很慢。


1
也许这里parfait的回答对你也有帮助:https://dev59.com/f5Tfa4cB1Zd3GeqPYPh8#35944697 - JohnE
2个回答

1
也许这可以帮到你。
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)

即使您想在不同的列上执行不同的函数(例如 b.mean() 和 c.sum()),以下代码似乎也可以正常工作:df = d.groupby('a')[['b','c']].apply(lambda x: (x.b.mean(), x.c.sum()))df = df.apply(pd.Series) - Siraj S.
更直接地说,df = d.groupby('a')[['b','c']].apply(lambda x: (x.b.mean(), x.c.sum())).apply(pd.Series)。 - Siraj S.
你真的应该将额外的信息编辑到你的答案中,而不是添加难以阅读的评论。 - Ajean
Series应用于字典列是个很好的技巧。感谢你向我展示。Pandas不应该让我们这样来应对问题,但它还是可行的。 - Doctor J

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接