我想知道,当我有预定义的组并且元素可能同时属于多个组时,如何有效地执行类似groupby
之类的事情。
假设我有以下DataFrame
:
df = pd.DataFrame({'value': [0, 2, 4]}, index=['A', 'B', 'C'])
value
A 0
B 2
C 4
,我有以下预定义的组,它们可能重叠并且大小不同:
groups = {'group 1': ['A', 'B'],
'group 2': ['A', 'B', 'C']}
现在,我想对DataFrame
组执行功能。例如,我要计算每个组的value
的平均值。
我当时正在考虑创建一个中间的“扩展的” DataFrame
,可以在其上运行groupby
:
intermediate_df = pd.DataFrame(columns=['id', 'group', 'value'])
intermediate_df['value'] = intermediate_df['value'].astype(float)
for group, members in groups.items():
for id_ in members:
row = pd.Series([id_, group, df.at[id_, 'value']],
index=['id', 'group', 'value'])
intermediate_df = intermediate_df.append(row, ignore_index=True)
id group value
0 A group 1 0.0
1 B group 1 2.0
2 A group 2 0.0
3 B group 2 2.0
4 C group 2 4.0
那我可以做
intermediate_df.groupby('group').mean()
这将给我想要的结果:
value
group
group 1 1.0
group 2 2.0
当然,我创建此中间DataFrame
的方式绝对没有效率。对于我的问题,什么是有效的解决方案?
答案 0 :(得分:2)
您可以使用Pandas.concat
和列表理解来创建intermediate_df
:
intermediate_df = pd.concat([df.loc[v].assign(group=k) for k, v in groups.items()])
[OUT]
value group
A 0 group 1
B 2 group 1
A 0 group 2
C 4 group 2
答案 1 :(得分:1)
修改不平衡群体的尝试:
pd.DataFrame().from_dict(groups, orient='index').T.stack().map(df.squeeze()).mean(level=1)
您也可以通过这种方式做到这一点:
pd.DataFrame(groups).stack().map(df.squeeze()).mean(level=1)
输出:
group 1 1
group 2 2
dtype: int64
答案 2 :(得分:0)
基于先前的答案,我对intermediate_df
使用列表理解
intermediate_df = pd.DataFrame([[group, id_] for group, members in groups.items() for id_ in members],
columns=['group', 'id']).merge(df, left_on='id', right_index=True)
与其他答案相比,这似乎是最快的解决方案:
n=10000
m=1000
df = pd.DataFrame({'value': np.random.normal(size=n)}, index=np.arange(n).astype(str))
groups = {str(i): list(df.sample(5).index) for i in range(m)}
%%timeit
intermediate_df = pd.concat([df.loc[members].assign(group=group) for group, members in groups.items()])
intermediate_df.groupby('group').mean()
948 ms ± 63.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%%timeit
pd.DataFrame(groups).stack().map(df.squeeze()).mean(level=1)
42.4 ms ± 183 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%%timeit
intermediate_df = pd.DataFrame([[group, id_] for group, members in groups.items() for id_ in members],
columns=['group', 'id']).merge(df, left_on='id', right_index=True)
intermediate_df.groupby('group').mean()
6.13 ms ± 50.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)