我认为这需要基准测试。使用OP的原始数据帧,
df = pd.DataFrame({
'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': list(range(1, 7)) * 2,
'sales': [np.random.randint(100000, 999999) for _ in range(12)]
})
第0个癌症
新熊猫变形看起来快多了。
df['sales'] / df.groupby('state')['sales'].transform('sum')
每回路1.32 ms±352µs
(7次运行的平均值±标准度,每次100次循环)
安迪·海登
正如他的回答所评论的那样,安迪充分利用了向量化和熊猫索引。
c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
c / c.groupby(level=0).sum()
每回路3.42 ms±16.7µs
(7次运行的平均值±标准度,每次100次循环)
保罗·H
state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
state = df.groupby(['state']).agg({'sales': 'sum'})
state_office.div(state, level='state') * 100
每回路4.66 ms±24.4µs
(7次运行的平均值±标准度,每次100次循环)
第三名 exp1orer
这是最慢的答案,因为它为级别0中的每个x计算x.sum()。
对我来说,这仍然是一个有用的答案,尽管不是目前的形式。对于小型数据集的快速EDA, apply允许您使用方法链接将其写在一行中。因此,我们不需要决定变量的名称,这对于你最有价值的资源(你的大脑!!)来说,实际上是非常昂贵的计算。
这是修改,
(
df.groupby(['state', 'office_id'])
.agg({'sales': 'sum'})
.groupby(level=0)
.apply(lambda x: 100 * x / float(x.sum()))
)
10.6 ms±81.5µs /回路
(7次运行的平均值±标准度,每次100次循环)
所以没有人会关心小数据集上的6毫秒。然而,这是3倍的速度,在一个更大的数据集上,这将产生巨大的差异。
加上上面的代码,我们创建了一个形状为(12,000,000,3)的DataFrame,其中包含14412个状态类别和600个office_ids,
import string
import numpy as np
import pandas as pd
np.random.seed(0)
groups = [
''.join(i) for i in zip(
np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
)
]
df = pd.DataFrame({'state': groups * 400,
'office_id': list(range(1, 601)) * 20000,
'sales': [np.random.randint(100000, 999999)
for _ in range(12)] * 1000000
})
使用癌症的,
0.791 s±19.4 ms /循环
(7次运行的平均值±标准值,每个循环1次)
使用安迪的,
2 s±10.4 ms每循环
(7次运行的平均值±标准值,每个循环1次)
和exp1orer
每回路19 s±77.1 ms
(7次运行的平均值±标准值,每个循环1次)
所以现在我们看到,用Andy的x10加速了大型、高基数数据集,而用Caner的x20加快了令人印象深刻的速度。
如果你要UV这个答案,一定要UV这三个答案!!
编辑:添加了Caner基准