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




seleccionar insertar (9)

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.


Answers

Por supuesto, la forma más rápida de iterar sobre un marco de datos es acceder a la ndarray numpy subyacente a través de df.values (como lo hace) o al acceder a cada columna por separado df.column_name.values . Como también desea tener acceso al índice, puede usar df.index.values para eso.

index = df.index.values
column_of_interest1 = df.column_name1.values
...
column_of_interestk = df.column_namek.values

for i in range(df.shape[0]):
   index_value = index[i]
   ...
   column_value_k = column_of_interest_k[i]

¿No es pitónico? Por supuesto. Pero rápido.

Si desea exprimir más jugo fuera del circuito, querrá ver cython . Cython te permitirá ganar grandes incrementos de velocidad (piensa 10x-100x). Para obtener el máximo rendimiento, compruebe las vistas de memoria de cython .


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.


Tienes tres opciones:

Por index (el más simple):

>>> for index in df.index:
...     print ("df[" + str(index) + "]['B']=" + str(df['B'][index]))

Con iterrows (más utilizados):

>>> for index, row in df.iterrows():
...     print ("df[" + str(index) + "]['B']=" + str(row['B']))

Con itertuples (el más rápido):

>>> for row in df.itertuples():
...     print ("df[" + str(row.Index) + "]['B']=" + str(row.B))

Tres opciones muestran algo como:

df[0]['B']=125
df[1]['B']=415
df[2]['B']=23
df[3]['B']=456
df[4]['B']=189
df[5]['B']=456
df[6]['B']=12

Fuente: neural-networks.io


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).


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


Las versiones más recientes de pandas ahora incluyen una función incorporada para iterar sobre filas.

for index, row in df.iterrows():

    # do some logic here

O, si lo quieres más rápido usa itertuples()

Pero, la sugerencia de Unutbu de usar funciones numpy para evitar la iteración en filas producirá el código más rápido.


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

Es una buena práctica usar siempre la notación [] . Una razón es que la notación de atributo ( df.column_name ) no funciona para los índices numerados:

In [1]: df = DataFrame([[1, 2, 3], [4, 5, 6]])

In [2]: df[1]
Out[2]:
0    2
1    5
Name: 1

In [3]: df.1
  File "<ipython-input-3-e4803c0d1066>", line 1
    df.1
       ^
SyntaxError: invalid syntax




python performance for-loop pandas