Python groupby的替代方案,用于从数据帧生成汇总表

Python groupby的替代方案,用于从数据帧生成汇总表,python,pandas,Python,Pandas,我想从一个数据帧生成一个汇总表。现在,我使用groupby和两个来进行循环,这似乎效率不高。似乎堆垛和卸垛都能让我达到目的,但我失败了 样本数据 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.ap

我想从一个数据帧生成一个汇总表。现在,我使用
groupby
和两个
来进行
循环,这似乎效率不高。似乎堆垛和卸垛都能让我达到目的,但我失败了

样本数据

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
我想要一个新的数据框架,其中stage作为列,Cats作为行,分数总和作为值。我是这样做的:

有效但可能效率低下的方法

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_table
crosstab
之间的区别,我使用与上面完全相同的100000行数据帧对三种解决方案进行了计时:

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)
交叉表
解决方案,需要在后台创建数据帧,即使传递的数据已经是数据帧格式:

%%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\u表
解决方案:


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

一个来自
交叉表的简单解决方案

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

来自
交叉表的简单解决方案

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

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

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

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

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

因为,这正是我想要的。但是请注意,这个解决方案比我的groupby解决方案稍微慢一点(虽然不那么笨重)-请参阅我问题编辑中的性能检查。因为,这正是我想要的。但是请注意,此解决方案比我使用的groupby解决方案稍微慢一点(虽然不那么笨重)-请参见我问题编辑中的性能检查。确实,为什么不呢?有什么不同吗?这个问题的答案在这里:([)。因此,在这种特殊情况下,
pivot\u table
实际上更好(在链接状态下回答“一般来说,如果您已经有一个数据帧,请使用pivot\u table,这样您就不会有再次创建相同数据帧的额外开销。”)。我认为这需要进行性能检查:pivot_table实际上是我测试的三种方法中速度最慢的。很难理解为什么…@MPa:你能粘贴性能比较结果吗?确实。很抱歉以前没有注意到。确实,为什么没有?有什么区别吗?这个问题的答案在这里:([)因此,在这种特殊情况下,
pivot\u table
实际上更好(在链接状态下回答“一般来说,如果您已经有了数据帧,请使用pivot\u table,这样您就不会有再次创建相同数据帧的额外开销。”)。我认为这是性能检查的保证:pivot_表实际上是我测试的三种方法中最慢的。很难理解为什么…@MPa:你能粘贴性能比较结果吗?确实如此。很抱歉以前没有注意到。这似乎不合逻辑,当第一个调用h下的第二个时,交叉表比pivot_表性能更好ood.我运行了你的代码,得到了groupby:“7.26 ms±351µs/循环(平均±标准偏差7次,每个循环100次)”,crosstab:“25.3 ms±303µs/循环(平均±标准偏差7次,每个循环10次)”和pivot_表:“21.8 ms±283µs/循环(平均±标准偏差7次,每个循环10次)”。这似乎不合逻辑,当第一个调用引擎盖下的第二个时,交叉表的性能比pivot_表更高。我运行了你的代码,得到了groupby:“7.26 ms±351µs/循环(平均±标准偏差7次,每个循环100次)”,交叉表:“25.3 ms±303µs/循环(平均±标准偏差7次,每个循环10次)”和pivot_表:“每个回路21.8 ms±283µs(7次运行的平均值±标准偏差,每个10个回路)”。