python - una - ¿Cuál es la forma más eficiente de recorrer los marcos de datos con pandas?




seleccionar una fila de un dataframe pandas (7)

Quiero realizar mis propias operaciones complejas sobre datos financieros en marcos de datos de manera secuencial.

Por ejemplo, estoy usando el siguiente archivo MSFT CSV tomado de Yahoo Finance :

Date,Open,High,Low,Close,Volume,Adj Close
2011-10-19,27.37,27.47,27.01,27.13,42880000,27.13
2011-10-18,26.94,27.40,26.80,27.31,52487900,27.31
2011-10-17,27.11,27.42,26.85,26.98,39433400,26.98
2011-10-14,27.31,27.50,27.02,27.27,50947700,27.27

....

Entonces hago lo siguiente:

#!/usr/bin/env python
from pandas import *

df = read_csv('table.csv')

for i, row in enumerate(df.values):
    date = df.index[i]
    open, high, low, close, adjclose = row
    #now perform analysis on open/close based on date, etc..

¿Es esa la forma más eficiente? Dado el enfoque en la velocidad en pandas, supongo que debe haber alguna función especial para iterar a través de los valores de una manera en que uno también recupera el índice (posiblemente a través de un generador para que sea eficiente en memoria). df.iteritems desafortunadamente solo itera columna por columna.


Al igual que lo que se ha mencionado anteriormente, el objeto pandas es más eficiente cuando se procesa toda la matriz a la vez. Sin embargo, para aquellos que realmente necesitan recorrer un marco de datos de pandas para realizar algo, como yo, encontré al menos tres formas de hacerlo. He hecho una breve prueba para ver cuál de los tres consume menos tiempo.

t = pd.DataFrame({'a': range(0, 10000), 'b': range(10000, 20000)})
B = []
C = []
A = time.time()
for i,r in t.iterrows():
    C.append((r['a'], r['b']))
B.append(time.time()-A)

C = []
A = time.time()
for ir in t.itertuples():
    C.append((ir[1], ir[2]))    
B.append(time.time()-A)

C = []
A = time.time()
for r in zip(t['a'], t['b']):
    C.append((r[0], r[1]))
B.append(time.time()-A)

print B

Resultado:

[0.5639059543609619, 0.017839908599853516, 0.005645036697387695]

Probablemente esta no sea la mejor manera de medir el consumo de tiempo, pero es rápido para mí.

Aquí hay algunos pros y contras IMHO:

  • .iterrows (): devuelve elementos de índice y fila en variables separadas, pero significativamente más lento
  • .itertuples (): más rápido que .iterrows (), pero el índice de retorno junto con los elementos de fila, ir [0] es el índice
  • zip: el más rápido, pero sin acceso al índice de la fila

Como joris señaló, iterrows es mucho más lento que itertuples y itertuples es aproximadamente 100 veces mayor que iterrows , y probé la velocidad de ambos métodos en un DataFrame con 5027505 registros. El resultado es para iterrows , es 1200it / s, e 120000it / s.

Si usa itertuples , tenga en cuenta que cada elemento en el bucle for es un timbre nombrado, por lo que para obtener el valor en cada columna, puede consultar el siguiente código de ejemplo

>>> df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]},
                      index=['a', 'b'])
>>> df
   col1  col2
a     1   0.1
b     2   0.2
>>> for row in df.itertuples():
...     print(row.col1, row.col2)
...
1, 0.1
2, 0.2

Otra sugerencia sería combinar groupby con cálculos vectorizados si los subconjuntos de las filas compartieran características que le permitieran hacerlo.


Pandas se basa en matrices NumPy. La clave para acelerar con las matrices NumPy es realizar sus operaciones en toda la matriz a la vez, nunca fila por fila o elemento por elemento.

Por ejemplo, si close es una matriz 1-d, y desea el cambio porcentual del día a día,

pct_change = close[1:]/close[:-1]

Esto calcula el conjunto completo de cambios porcentuales como una declaración, en lugar de

pct_change = []
for row in close:
    pct_change.append(...)

Así que trate de evitar el bucle de Python for i, row in enumerate(...) completo, y piense en cómo realizar sus cálculos con operaciones en toda la matriz (o marco de datos) como un todo, en lugar de fila por fila.


Puede recorrer las filas transponiendo y luego llamando a iteritems:

for date, row in df.T.iteritems():
   # do some logic here

No estoy seguro de la eficiencia en ese caso. Para obtener el mejor rendimiento posible en un algoritmo iterativo, es posible que desee explorar escribirlo en cython , por lo que podría hacer algo como:

def my_algo(ndarray[object] dates, ndarray[float64_t] open,
            ndarray[float64_t] low, ndarray[float64_t] high,
            ndarray[float64_t] close, ndarray[float64_t] volume):
    cdef:
        Py_ssize_t i, n
        float64_t foo
    n = len(dates)

    for i from 0 <= i < n:
        foo = close[i] - open[i] # will be extremely fast

Recomendaría escribir el algoritmo en Python puro primero, asegúrese de que funcione y vea qué tan rápido es: si no es lo suficientemente rápido, convierta las cosas a Cython de esta manera con un trabajo mínimo para obtener algo que sea tan rápido como el código C a mano. / C ++.



iterrows después de notar respuesta de , pero encontré que produce tuplas (índice, serie). No estoy seguro de cuál funcionaría mejor para ti, pero terminé usando el método itertuples para mi problema, que produce tuplas (index, row_value1 ...).

También hay iterkv , que itera a través de tuplas (columna, serie).





pandas