Basato il tuo codice (il tuo groupby/apply
), sembra (nonostante il tuo esempio ... ma forse ho frainteso quello che vuoi e poi quello che Andy ha fatto sarebbe stata la migliore idea) che stai lavorando con un 'appuntamento' colonna che è un dtype datetime64
e non un dtype integer
nei dati effettivi. Inoltre sembra che tu voglia calcolare il cambiamento in giorni misurato dalla prima osservazione di un dato group/stage
. Penso che questo sia un insieme meglio di dati di esempio (se ho ben capito il tuo obiettivo in modo corretto):
>>> df
group date stage dur
0 A 2014-01-01 one 0
1 A 2014-01-03 one 2
2 A 2014-01-04 one 3
3 A 2014-01-05 two 0
4 B 2014-01-02 four 0
5 B 2014-01-06 five 0
6 B 2014-01-10 five 4
7 C 2014-01-03 two 0
8 C 2014-01-05 two 2
Dato che si dovrebbe ottenere una certa velocità-up da solo modificando la applicano (come suggerisce Jeff nel suo commento) dividendo attraverso il timedelta64
in modo vectorized dopo la applicano (o si potrebbe farlo nel pagamento):
>>> df['dur'] = df.groupby(['group','stage']).date.apply(lambda x: x - x.iloc[0])
>>> df['dur'] /= np.timedelta64(1,'D')
>>> df
group date stage dur
0 A 2014-01-01 one 0
1 A 2014-01-03 one 2
2 A 2014-01-04 one 3
3 A 2014-01-05 two 0
4 B 2014-01-02 four 0
5 B 2014-01-06 five 0
6 B 2014-01-10 five 4
7 C 2014-01-03 two 0
8 C 2014-01-05 two 2
Ma si può anche evitare il groupby/apply
dato i dati sono in gruppo, stage, ordine di data. La prima data per ogni raggruppamento ['group','stage']
si verifica quando il gruppo cambia o lo stage cambia. Quindi penso che si può fare qualcosa di simile alla seguente:
>>> beg = (df.group != df.group.shift(1)) | (df.stage != df.stage.shift(1))
>>> df['dur'] = (df['date'] - df['date'].where(beg).ffill())/np.timedelta64(1,'D')
>>> df
group date stage dur
0 A 2014-01-01 one 0
1 A 2014-01-03 one 2
2 A 2014-01-04 one 3
3 A 2014-01-05 two 0
4 B 2014-01-02 four 0
5 B 2014-01-06 five 0
6 B 2014-01-10 five 4
7 C 2014-01-03 two 0
8 C 2014-01-05 two 2
Spiegazione: Si noti che cosa df['date'].where(beg)
crea:
>>> beg = (df.group != df.group.shift(1)) | (df.stage != df.stage.shift(1))
>>> df['date'].where(beg)
0 2014-01-01
1 NaT
2 NaT
3 2014-01-05
4 2014-01-02
5 2014-01-06
6 NaT
7 2014-01-03
8 NaT
E poi ho ffill
i valori e prendere la differenza con la colonna 'data'.
Edit: Come Andy fa notare che si potrebbe anche usare transform
:
>>> df['dur'] = df.date - df.groupby(['group','stage']).date.transform(lambda x: x.iloc[0])
>>> df['dur'] /= np.timedelta64(1,'D')
group date stage dur
0 A 2014-01-01 one 0
1 A 2014-01-03 one 2
2 A 2014-01-04 one 3
3 A 2014-01-05 two 0
4 B 2014-01-02 four 0
5 B 2014-01-06 five 0
6 B 2014-01-10 five 4
7 C 2014-01-03 two 0
8 C 2014-01-05 two 2
Velocità: ho cronometrato i due metodo che utilizza un dataframe simile con 400.000 osservazioni:
metodo apply:
1 loops, best of 3: 18.3 s per loop
Metodo di non applicazione:
1 loops, best of 3: 1.64 s per loop
Quindi penso evitando la applicano potrebbe dare alcune accelerazioni significative
Non è necessario applicare la finale, vedere qui: http://pandas-docs.github.io/pandas-docs -travis/timeseries.html # time-delta-conversioni, puoi semplicemente '' astype ('timedelta64 [D]') '' o dividi per '' np.timedelta64 (1, 'D') '' (sono sciatti diverso nel modo in cui girano. – Jeff