Creo que esto necesita una evaluación comparativa. Usando el DataFrame original de 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 comentó su respuesta, Andy aprovecha al máximo la vectorización y la indexación 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 bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)
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 bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)
Esta es la respuesta más lenta ya que calcula x.sum()
para cada uno x
en el nivel 0.
Para mí, esta sigue siendo una respuesta útil, aunque no en su forma actual. Para un EDA rápido en conjuntos de datos más pequeños, le apply
permite usar el método de encadenamiento para escribir esto en una sola línea. Por lo tanto, eliminamos la necesidad de decidir el nombre de una variable, que en realidad es computacionalmente costoso para su recurso más valioso (¡su cerebro!).
Aquí está la modificación,
(
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 bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)
Así que a nadie le importan unos 6 ms en un pequeño conjunto de datos. Sin embargo, esto es una velocidad 3 veces mayor y, en un conjunto de datos más grande con un grupo de cardinalidad alto, esto hará una gran diferencia.
Agregando al código anterior, creamos un DataFrame con forma (12,000,000, 3) con 14412 categorías estatales y 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 ciclo
(media ± estándar de desarrollo de 7 carreras, 1 ciclo cada una)
y exp1orer
19 s ± 77.1 ms por ciclo
(media ± estándar de desarrollo de 7 carreras, 1 ciclo cada una)
Así que ahora vemos que x10 se acelera en grandes conjuntos de datos de alta cardinalidad.
¡Asegúrate de usar estas tres respuestas en UV si estas es UV!
df['sales'] / df.groupby('state')['sales'].transform('sum')
Parece ser la respuesta más clara.