Myślę, że to wymaga analizy porównawczej. Używając oryginalnej ramki DataFrame 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)]
})
Jak skomentował swoją odpowiedź, Andy w pełni wykorzystuje wektoryzację i indeksowanie pand.
c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
c / c.groupby(level=0).sum()
3,42 ms ± 16,7 μs na pętlę
(średnia ± odchylenie standardowe z 7 cykli po 100 pętli)
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 na pętlę
(średnia ± odchylenie standardowe z 7 przebiegów, po 100 pętli każda)
Jest to najwolniejsza odpowiedź, obliczana x.sum()
dla każdego x
na poziomie 0.
Dla mnie jest to nadal przydatna odpowiedź, choć nie w obecnej formie. W celu szybkiego EDA dla mniejszych zestawów danych, apply
umożliwia użycie łańcuchów metod do zapisania tego w jednym wierszu. Dlatego usuwamy potrzebę decydowania o nazwie zmiennej, która w rzeczywistości jest bardzo kosztowna obliczeniowo dla Twojego najcenniejszego zasobu (Twojego mózgu !!).
Oto modyfikacja,
(
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 na pętlę
(średnia ± odchylenie standardowe z 7 przebiegów, po 100 pętli każda)
Więc nikogo nie obchodzi 6 ms na małym zestawie danych. Jest to jednak 3-krotnie szybsze i na większym zbiorze danych z grupami o wysokiej kardynalności będzie to miało ogromne znaczenie.
Dodając do powyższego kodu, tworzymy ramkę DataFrame o kształcie (12 000 000, 3) z 14412 kategoriami stanu i 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
})
Używając Andy's,
2 s ± 10,4 ms na pętlę
(średnia ± odchylenie standardowe 7 przebiegów, po 1 pętli)
i exp1orer
19 s ± 77,1 ms na pętlę
(średnia ± odchylenie standardowe z 7 przebiegów, po 1 pętli)
Teraz widzimy przyspieszenie x10 na dużych zestawach danych o wysokiej kardynalności.
Upewnij się, że te trzy odpowiedzi zostały poddane promieniowaniu UV, jeśli tę jedną!
df['sales'] / df.groupby('state')['sales'].transform('sum')
wydaje się być najjaśniejszą odpowiedzią.