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

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 los pandas, asumiría que debe haber alguna función especial para iterar a través de los valores de manera que uno también recupere el índice (posiblemente a través de un generador para que sea eficiente en memoria). df.iteritems desafortunadamente solo itera columna por columna.

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.

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 la matriz completa de cambios porcentuales como una statement, 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 dataframe) como un todo, en lugar de fila por fila.

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 dataframe 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

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 la respuesta de Nick Crawford , 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).

Tienes tres opciones:

Por índice (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

Solo como una pequeña adición, también puede hacer una aplicación si tiene una función compleja que aplica a una sola columna:

http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html

 df[b] = df[a].apply(lambda col: do stuff with col here) 

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 del bucle for es un timbre nombrado, por lo tanto, 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 

Por supuesto, la manera más rápida de iterar sobre un dataframe 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 . Ya que también quiere 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 pythonico? Por supuesto. Pero rápido.

Si quieres exprimir más jugo del ciclo, querrás ver el 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 .

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