groupby的替代方法,用于从整洁的熊猫DataFrame生成汇总表

时间:2018-07-24 16:41:20

标签: python pandas

我想从tidy熊猫DataFrame生成一个摘要表。我现在使用groupby和两个for循环,这似乎效率不高。似乎可以通过堆放来完成任务,但是我失败了。

样本数据

import pandas as pd
import numpy as np
import copy
import random

df_tidy = pd.DataFrame(columns = ['Stage', 'Exc', 'Cat', 'Score'])
for _ in range(10):
    df_tidy = df_tidy.append(
    {
        'Stage': random.choice(['OP', 'FUEL', 'EOL']),
        'Exc': str(np.random.randint(low=0, high=1000)),
        'Cat': random.choice(['CC', 'HT', 'PM']),
        'Score': np.random.random(),
    }, ignore_index=True
)
df_tidy

返回

    Stage   Exc Cat Score
0   OP      929 HT  0.946234
1   OP      813 CC  0.829522
2   FUEL    114 PM  0.868605
3   OP      896 CC  0.382077
4   FUEL    10  CC  0.832246
5   FUEL    515 HT  0.632220
6   EOL     970 PM  0.532310
7   FUEL    198 CC  0.209856
8   FUEL    848 CC  0.479470
9   OP      968 HT  0.348093

我想要一个新的DataFrame,其舞台为列,猫为行,分数总和为值。我是这样实现的:

有效但可能效率不高的方法

new_df = pd.DataFrame(columns=list(df_tidy['Stage'].unique()))
for cat, small_df in df_tidy.groupby('Cat'):
    for lcs, smaller_df in small_df.groupby('Stage'):
        new_df.loc[cat, lcs] = smaller_df['Score'].sum()
new_df['Total'] = new_df.sum(axis=1)
new_df

哪个返回我想要的内容:

    OP      FUEL        EOL     Total
CC  1.2116  1.52157     NaN     2.733170
HT  1.29433 0.63222     NaN     1.926548
PM  NaN     0.868605    0.53231 1.400915

但是我不敢相信这是最简单或最有效的途径。

问题

我错过了什么大熊猫魔术?

更新-计时建议的解决方案

要了解下面提出的pivot_tablecrosstab之间的区别,我对这三种解决方案的时间进行了计时,它们的构建方式完全是如上所述的100,000行数据帧:

我认为效率不高的

groupby解决方案

%%timeit
new_df = pd.DataFrame(columns=list(df_tidy['Stage'].unique()))
for cat, small_df in df_tidy.groupby('Cat'):
    for lcs, smaller_df in small_df.groupby('Stage'):
        new_df.loc[cat, lcs] = smaller_df['Score'].sum()
new_df['Total'] = new_df.sum(axis=1)

41.2 ms ± 3.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

crosstab解决方案,即使所传递的数据已经是DataFrame格式,也需要在后台创建DataFrame:

%%timeit
pd.crosstab(index=df_tidy.Cat,columns=df_tidy.Stage, values=df_tidy.Score, aggfunc='sum', margins = True, margins_name = 'Total').iloc[:-1,:]

67.8 ms ± 1.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

pivot_table解决方案

%%timeit
pd.pivot_table(df_tidy, index=['Cat'], columns=["Stage"], margins=True, margins_name='Total', aggfunc=np.sum).iloc[:-1,:]

713 ms ± 20.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

因此,看来笨拙的groupby解决方案是最快的。

2 个答案:

答案 0 :(得分:3)

来自crosstab

的简单解决方案
pd.crosstab(index=df.Cat,columns=df.Stage,values=df.Score,aggfunc='sum', margins = True, margins_name = 'Total').iloc[:-1,:]
Out[342]: 
Stage      EOL      FUEL        OP     Total
Cat                                         
CC         NaN  1.521572  1.211599  2.733171
HT         NaN  0.632220  1.294327  1.926547
PM     0.53231  0.868605       NaN  1.400915

答案 1 :(得分:1)

我想知道是否比使用pd.crosstab更简单的解决方案是使用pd.pivot

pd.pivot_table(df_tidy, index=['Cat'], columns=["Stage"], margins=True, margins_name='Total', aggfunc=np.sum).iloc[:-1,:]