用预先定义的组对pandas DataFrame进行分组

时间:2019-03-07 18:35:37

标签: python pandas dataframe pandas-groupby

我想知道,当我有预定义的组并且元素可能同时属于多个组时,如何有效地执行类似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的方式绝对没有效率。对于我的问题,什么是有效的解决方案?

3 个答案:

答案 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)