Penso che questo abbia bisogno di analisi comparativa. Utilizzando il DataFrame originale di 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)]
})
Come commentato sulla sua risposta, Andy sfrutta appieno la vettorializzazione e l'indicizzazione dei panda.
c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
c / c.groupby(level=0).sum()
3,42 ms ± 16,7 µs per loop
(media ± deviazione standard di 7 cicli, 100 loop ciascuno)
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 per loop
(media ± deviazione standard di 7 cicli, 100 loop ciascuno)
Questa è la risposta più lenta in quanto viene calcolata x.sum()
per ciascuna x
nel livello 0.
Per me, questa è ancora una risposta utile, sebbene non nella sua forma attuale. Per un rapido EDA su insiemi di dati più piccoli, apply
consente di utilizzare il concatenamento dei metodi per scrivere questo in un'unica riga. Pertanto eliminiamo la necessità di decidere il nome di una variabile, che in realtà è molto computazionalmente costosa per la tua risorsa più preziosa (il tuo cervello !!).
Ecco la modifica,
(
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 per loop
(media ± deviazione standard di 7 cicli, 100 loop ciascuno)
Quindi nessuno si preoccuperà di 6ms su un piccolo set di dati. Tuttavia, si tratta di una velocità 3 volte maggiore e, su un set di dati più ampio con gruppi di cardinalità elevati, questo farà una differenza enorme.
Aggiungendo al codice sopra, creiamo un DataFrame con forma (12.000.000, 3) con 14412 categorie di stato 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 per ciclo
(media ± deviazione standard di 7 cicli, 1 ciclo ciascuno)
ed exp1orer
19 s ± 77,1 ms per ciclo
(media ± deviazione standard di 7 cicli, 1 ciclo ciascuno)
Quindi ora vediamo x10 accelerare su grandi set di dati di cardinalità.
Assicurati di UV queste tre risposte se UV questo !!
df['sales'] / df.groupby('state')['sales'].transform('sum')
sembra essere la risposta più chiara.