2014-04-25 11 views
5

Voglio fare un calcolo "strano" su un DataFrame (potrebbe essere pensato come una serie) nei panda. DataFrame deve essere considerato come una serie temporale o simile (l'ordine degli elementi è importante).vectorizing forward-looking function pandas dataframe

  • dato un valore di indice [i] (valore [i])
  • Dato un gradino (ad esempio 1) [un numero intero o reale numero]
  • Data una rr moltiplicatore (ad esempio 2) [un numero intero o reale]

Aspetto avanti a elementi [i:] e assegnare valore [i] una "classe" di:

  • +1 se la en valori fanno causa raggiungono un livello di valore [i] + step * rr prima raggiungimento del valore [i] - passo
  • -1 se i valori successivi raggiungono un livello di valore [i] - passo * rr prima valore raggiungimento [i] + step
  • 0 negli altri casi (es. quando i valori risultanti toccano il valore [i] - passaggio e quindi valore [i] + passaggio o viceversa.

So che sembra assurdo. Immagina una passeggiata casuale con passi + 1/-1. Una sequenza come:

  • 0, 1, verrà assegnato alla classe 1 2 (che può anche essere 0, 1, 0, 0, 1, 1, 0, 1, 1, 2)
  • 0, -1, -2 saranno assegnati alla classe -1 (può anche essere 0, -1, 0, 0, 0, -1, -1, -1, -2)
  • 0, + 1, 0, -1 o 0, -1, 0, 0, -1, 0, 1 e simili saranno di classe 0.

L'ho risolto il "classico" (e forse non così-pitonico) modo definendo una funzione:

import numpy as np 
import pandas as pd 

def FindClass(inarr, i=0, step=0.001, rr=2): 
    j = 0 
    foundClass = None 
    while i+j < len(inarr) - 1: 
     j += 1 
     if inarr[i+j] >= inarr[i] + step: 
      direction = 1 
      break 
     if inarr[i+j] <= inarr[i] - step: 
      direction = -1 
      break 

    while i+j < len(inarr)-1: 
     j += 1 
     if direction == 1 and inarr[i+j] >= inarr[i] + (step * rr): 
      foundClass = 1 
      break 
     elif direction == 1 and inarr[i+j] <= inarr[i] - step: 
      foundClass = 0 
      break 
     elif direction == -1 and inarr[i+j] <= inarr[i] - (step * rr): 
      foundClass = -1 
      break 
     elif direction == -1 and inarr[i+j] >= inarr[i] + step: 
      foundClass = 0 
      break 
    if foundClass is None: 
     foundClass = np.nan 

    return foundClass 

e poi iterando su di esso:

if __name__ == "__main__": 
    steps = np.random.randint(-1, 2, size= 10000) 

    randomwalk = steps.cumsum(0) 
    rc = pd.DataFrame({'rw':randomwalk, 'result': np.nan}) 

    for c in range(0, len(rc)-1): 
     rc.result[c] = FindClass(rc.rw, i=c, step=1) 

    print rc 

sul mio portatile (e l'esecuzione di python 2.7) ottengo un profiling, che non è "troppo" male per una serie 10000 elementi:

python -m cProfile -s cumulative fbmk.py 
<class 'pandas.core.frame.DataFrame'> 
Int64Index: 10000 entries, 0 to 9999 
Data columns (total 2 columns): 
result 9996 non-null values 
rw  10000 non-null values 
dtypes: float64(1), int32(1) 
     932265 function calls (929764 primitive calls) in 2.643 seconds 

    Ordered by: cumulative time 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.106 0.106 2.646 2.646 fbmk.py:1(<module>) 
    9999 0.549 0.000 1.226 0.000 fbmk.py:4(FindClass) 
    158062 0.222 0.000 0.665 0.000 series.py:616(__getitem__) 
     2 0.029 0.014 0.561 0.281 __init__.py:3(<module>) 
    158062 0.226 0.000 0.443 0.000 index.py:718(get_value) 
    19998 0.070 0.000 0.442 0.000 frame.py:2082(__getattr__) 
    19998 0.111 0.000 0.331 0.000 frame.py:1986(__getitem__) 

La domanda è:

Qualcuno vede una possibilità di vettorizzazione di questa funzione in panda/numpy in un modo che sta migliorando le prestazioni?

Se la cosa è fattibile con meno sforzi in R, sarebbe anche bello!

Grazie mille in anticipo!

+1

Non è vettorializzare, ma forse è possibile scrivere la funzione 'findClass' in cython? – joris

+0

Sì, certo che questa è una possibilità. La domanda qui è dovuta principalmente al fatto che si tratta di un'operazione ripetitiva riga per fila, e la gente di solito dice che con i panda e simili devi "pensare in vettoriale", evitando i loop ... Ho provato, ma non ho gestito ! – user3562348

+0

Oltre alla mia idea nella mia risposta, penso che otterresti una notevole velocità dalla riscrittura delle tue funzioni per sfruttare i condizionali. Stai inserendo condizionali lunghi in un ciclo while, ma la tua logica ti consente di escludere molte delle opzioni il più delle volte. Ciò causerà molto meno codice da eseguire e probabilmente ti porterà un fattore 2-4 nel tempo di esecuzione. –

risposta

0

A seconda delle proprietà del problema, è possibile utilizzare np.where per individuare i livelli attraversati e classificare le serie temporali.

Il grande svantaggio qui è il np.where vi darà ogni indice in cui la serie temporale è superiore a value[i] + step, ecc., Che potenzialmente trasformerà un algoritmo di tempo lineare in un algoritmo di tempo quadratico. A seconda delle dimensioni dei problemi che verranno elaborati, mi aspetto che guadagnerai molto nel prefactor; potreste persino finire con la soluzione numpy quadratica.

Da frugare, l'equivalente "trova il primo indice" di np.where è ancora una funzionalità richiesta.