Eu acho que isso precisa de benchmarking. Usando o DataFrame original do OP,
df = pd.DataFrame({
'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': range(1, 7) * 2,
'sales': [np.random.randint(100000, 999999) for _ in range(12)]
})
Como comentado em sua resposta, Andy aproveita ao máximo a vetorização e a indexação de pandas.
c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
c / c.groupby(level=0).sum()
3,42 ms ± 16,7 µs por loop
(média ± desvio padrão de 7 corridas, 100 loops cada)
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 por loop
(média ± desvio padrão de 7 corridas, 100 loops cada)
Essa é a resposta mais lenta, pois calcula x.sum()
para cada uma x
no nível 0.
Para mim, essa ainda é uma resposta útil, embora não em sua forma atual. Para um rápido EDA em conjuntos de dados menores, apply
você pode usar o encadeamento de métodos para escrever isso em uma única linha. Portanto, removemos a necessidade de decidir o nome de uma variável, que na verdade é muito caro para o seu recurso mais valioso (seu cérebro !!).
Aqui está a modificação,
(
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 por loop
(média ± desvio padrão de 7 corridas, 100 loops cada)
Portanto, ninguém vai se importar com 6ms em um pequeno conjunto de dados. No entanto, isso é 3x mais rápido e, em um conjunto de dados maior com grupos de alta cardinalidade, isso fará uma diferença enorme.
Adicionando ao código acima, criamos um DataFrame com a forma (12.000.000, 3) com 14412 categorias de estado e 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
})
Usando Andy,
2 s ± 10,4 ms por loop
(média ± desvio padrão de 7 corridas, 1 loop cada)
e exp1orer
19 s ± 77,1 ms por loop
(média ± desvio padrão de 7 corridas, 1 loop cada)
Então agora vemos x10 acelerar em conjuntos de dados grandes e de alta cardinalidade.
Certifique-se de UV estas três respostas se você UV este !!
df['sales'] / df.groupby('state')['sales'].transform('sum')
parece ser a resposta mais clara.