python - groupby 的替代方案,用于从整洁的 pandas DataFrame 生成汇总表
问题描述
我想从一个整洁的pandas DataFrame 生成一个汇总表。我现在使用groupby
and 两个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,其中 Stages 作为列,Cats 作为行,Scores 作为值的总和。我是这样实现的:
有效但可能效率低下的方法
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
,我使用与上面完全相同的 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
解决方案是最快的。
解决方案
一个简单的解决方案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
推荐阅读
- reactjs - 用graphql和strapi-'return'在函数外反应突变
- sql - SQL 列表每月出现次数高于 15
- python - Selenium ChromeDriver 不与 Chrome 打印对话框交互
- javascript - 添加和显示多个图像
- shell - 在 Cloud Scheduler 中运行 Shell 脚本
- css - 使用引导程序设计 .NET Core Angular 模板 (VS)
- ruby-on-rails - 突然我完全无法执行所有命令(例如:$ ruby -v、$ mysql --version 等)
- postgresql - 在 PostgreSQL 中,如何将提取的日期插入到查询中?
- sql - Oracle过程增加字符串数据类型并将其存储为表中的列之一
- azure-data-factory - Azure 数据工厂 Web 活动截断不记名令牌