2012-11-01 13 views
6

Possiedo un DataFrame costituito da molte serie temporali sovrapposte. L'indice è (poolId, month) dove entrambi sono numeri interi, il "mese" è il numero di mesi dal 2000. Qual è il modo migliore per calcolare versioni ritardate di un mese di più variabili?Il modo più efficace per spostare le serie temporali MultiIndex

In questo momento, mi fare qualcosa di simile:

cols_to_shift = ["bal", ...5 more columns...] 
df_shift = df[cols_to_shift].groupby(level=0).transform(lambda x: x.shift(-1)) 

Per i miei dati, questo mi ha portato un pieno 60 s per l'esecuzione. (Ho 48k piscine differenti per un totale di 718k righe.)

sto convertendo questo dal codice R e la chiamata data.table equivalente:

dt.shift <- dt[, list(bal=myshift(bal), ...), by=list(poolId)] 

richiede solo 9 s per l'esecuzione. (Qui "myshift" è qualcosa di simile a "function (x) c (x [-1], NA)".)

C'è un modo per far sì che la versione di panda sia di nuovo in linea? Ho provato questo su 0.8.1.

Edit: Ecco un esempio di generare una serie di dati Close-sufficiente, in modo da poter avere un'idea di ciò che intendo:

ids = np.arange(48000) 
lens = np.maximum(np.round(15+9.5*np.random.randn(48000)), 1.0).astype(int) 
id_vec = np.repeat(ids, lens) 
lens_shift = np.concatenate(([0], lens[:-1])) 
mon_vec = np.arange(lens.sum()) - np.repeat(np.cumsum(lens_shift), lens) 
n = len(mon_vec) 
df = pd.DataFrame.from_items([('pool', id_vec), ('month', mon_vec)] + [(c, np.random.rand(n)) for c in 'abcde']) 
df = df.set_index(['pool', 'month']) 
%time df_shift = df.groupby(level=0).transform(lambda x: x.shift(-1)) 

che ha avuto 64 s quando l'ho provato. Questi dati hanno tutte le serie a partire dal mese 0; in realtà, dovrebbero finire tutti al mese np.max (obiettivo), con date di inizio irregolari, ma abbastanza buone.

Modifica 2: ecco qualche codice R di confronto. Questo richiede 0,8 s. Fattore di 80, non va bene.

library(data.table) 
ids <- 1:48000 
lens <- as.integer(pmax(1, round(rnorm(ids, mean=15, sd=9.5)))) 
id.vec <- rep(ids, times=lens) 
lens.shift <- c(0, lens[-length(lens)]) 
mon.vec <- (1:sum(lens)) - rep(cumsum(lens.shift), times=lens) 
n <- length(id.vec) 
dt <- data.table(pool=id.vec, month=mon.vec, a=rnorm(n), b=rnorm(n), c=rnorm(n), d=rnorm(n), e=rnorm(n)) 
setkey(dt, pool, month) 
myshift <- function(x) c(x[-1], NA) 
system.time(dt.shift <- dt[, list(month=month, a=myshift(a), b=myshift(b), c=myshift(c), d=myshift(d), e=myshift(e)), by=pool]) 
+0

ho aperto un problema GitHub qui: https://github.com/pydata/pandas/temi/2162. Dare un'occhiata –

risposta

6

Vorrei suggerire di rimodellare i dati e fare un unico turno contro l'approccio groupby:

result = df.unstack(0).shift(1).stack() 

Questo cambia l'ordine dei livelli in modo che ci si vuole scambiare e riordino:

result = result.swaplevel(0, 1).sortlevel(0) 

è possibile verificare che è stato ritardato di un periodo (che si desidera spostamento (1) invece di spostamento (-1)):

In [17]: result.ix[1] 
Out[17]: 
       a   b   c   d   e 
month             
1  0.752511 0.600825 0.328796 0.852869 0.306379 
2  0.251120 0.871167 0.977606 0.509303 0.809407 
3  0.198327 0.587066 0.778885 0.565666 0.172045 
4  0.298184 0.853896 0.164485 0.169562 0.923817 
5  0.703668 0.852304 0.030534 0.415467 0.663602 
6  0.851866 0.629567 0.918303 0.205008 0.970033 
7  0.758121 0.066677 0.433014 0.005454 0.338596 
8  0.561382 0.968078 0.586736 0.817569 0.842106 
9  0.246986 0.829720 0.522371 0.854840 0.887886 
10  0.709550 0.591733 0.919168 0.568988 0.849380 
11  0.997787 0.084709 0.664845 0.808106 0.872628 
12  0.008661 0.449826 0.841896 0.307360 0.092581 
13  0.727409 0.791167 0.518371 0.691875 0.095718 
14  0.928342 0.247725 0.754204 0.468484 0.663773 
15  0.934902 0.692837 0.367644 0.061359 0.381885 
16  0.828492 0.026166 0.050765 0.524551 0.296122 
17  0.589907 0.775721 0.061765 0.033213 0.793401 
18  0.532189 0.678184 0.747391 0.199283 0.349949 

In [18]: df.ix[1] 
Out[18]: 
       a   b   c   d   e 
month             
0  0.752511 0.600825 0.328796 0.852869 0.306379 
1  0.251120 0.871167 0.977606 0.509303 0.809407 
2  0.198327 0.587066 0.778885 0.565666 0.172045 
3  0.298184 0.853896 0.164485 0.169562 0.923817 
4  0.703668 0.852304 0.030534 0.415467 0.663602 
5  0.851866 0.629567 0.918303 0.205008 0.970033 
6  0.758121 0.066677 0.433014 0.005454 0.338596 
7  0.561382 0.968078 0.586736 0.817569 0.842106 
8  0.246986 0.829720 0.522371 0.854840 0.887886 
9  0.709550 0.591733 0.919168 0.568988 0.849380 
10  0.997787 0.084709 0.664845 0.808106 0.872628 
11  0.008661 0.449826 0.841896 0.307360 0.092581 
12  0.727409 0.791167 0.518371 0.691875 0.095718 
13  0.928342 0.247725 0.754204 0.468484 0.663773 
14  0.934902 0.692837 0.367644 0.061359 0.381885 
15  0.828492 0.026166 0.050765 0.524551 0.296122 
16  0.589907 0.775721 0.061765 0.033213 0.793401 
17  0.532189 0.678184 0.747391 0.199283 0.349949 

Perf non è troppo male con questo metodo (che potrebbe essere un tocco più lento in 0.9.0):

In [19]: %time result = df.unstack(0).shift(1).stack() 
CPU times: user 1.46 s, sys: 0.24 s, total: 1.70 s 
Wall time: 1.71 s 
+0

Questo è un grande miglioramento! Ci sono voluti 6,6 secondi per me sotto 0,8,1; speriamo che la prossima settimana avremo 0.9.0 installato così posso provarlo. –

+0

Una differenza è che poiché elimina le voci NaN generate da shift() nello stack(), ha un numero di righe inferiore rispetto al precedente, ma questo è curato nel join(). (E volevo dire shift (-1), è un calcolo del tasso di pericolo, quindi è lungimirante.) –

+0

I miglioramenti delle prestazioni che ho fatto erano nel bagagliaio. Presto lavorerò per ottenere una nuova versione –

Problemi correlati