¿Tiene pandas iterrows problemas de rendimiento?

He notado un rendimiento muy bajo al usar iterrows from pandas.

¿Es esto algo que es experimentado por otros? ¿Es específico para las ventanas y debería evitarse esta función para datos de un tamaño determinado (estoy trabajando con 2-3 millones de filas)?

Esta discusión sobre GitHub me llevó a creer que se debe a la combinación de tipos de datos en el dataframe, sin embargo, el simple ejemplo a continuación muestra que existe incluso cuando se usa un tipo de dtype (float64). Esto lleva 36 segundos en mi máquina:

import pandas as pd import numpy as np import time s1 = np.random.randn(2000000) s2 = np.random.randn(2000000) dfa = pd.DataFrame({'s1': s1, 's2': s2}) start = time.time() i=0 for rowindex, row in dfa.iterrows(): i+=1 end = time.time() print end - start 

¿Por qué son las operaciones vectorizadas como aplicar mucho más rápido? Me imagino que también debe haber una iteración fila por fila.

No puedo averiguar cómo no usarlo en mi caso (esto lo guardaré para una pregunta futura). Por lo tanto, agradecería escuchar si usted ha podido evitar constantemente esta iteración. Estoy haciendo cálculos basados ​​en datos en marcos de datos separados. ¡Gracias!

— Edición: la versión simplificada de lo que quiero ejecutar se ha agregado a continuación —

 import pandas as pd import numpy as np #%% Create the original tables t1 = {'letter':['a','b'], 'number1':[50,-10]} t2 = {'letter':['a','a','b','b'], 'number2':[0.2,0.5,0.1,0.4]} table1 = pd.DataFrame(t1) table2 = pd.DataFrame(t2) #%% Create the body of the new table table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=[0]) #%% Iterate through filtering relevant data, optimizing, returning info for row_index, row in table1.iterrows(): t2info = table2[table2.letter == row['letter']].reset_index() table3.ix[row_index,] = optimize(t2info,row['number1']) #%% Define optimization def optimize(t2info, t1info): calculation = [] for index, r in t2info.iterrows(): calculation.append(r['number2']*t1info) maxrow = calculation.index(max(calculation)) return t2info.ix[maxrow] 

Related of "¿Tiene pandas iterrows problemas de rendimiento?"

En general, las iterrows solo deben usarse en casos muy específicos. Este es el orden general de precedencia para el desempeño de varias operaciones:

 1) vectorization 2) using a custom cython routine 3) apply a) reductions that can be performed in cython b) iteration in python space 4) itertuples 5) iterrows 6) updating an empty frame (eg using loc one-row-at-a-time) 

El uso de una rutina de cython personalizada suele ser demasiado complicado, así que saltemos eso por ahora.

1) La vectorización es SIEMPRE SIEMPRE la primera y la mejor opción. Sin embargo, hay un pequeño conjunto de casos que no pueden ser vectorizados de manera obvia (en su mayoría involucrando una recurrencia). Además, en un marco más pequeño, puede ser más rápido hacer otros métodos.

3) Los iteradores en el espacio de Cython (esto se realiza internamente en pandas) (esto es a) un caso itreator.

Esto depende de lo que sucede dentro de la expresión de aplicación. por ejemplo, df.apply(lambda x: np.sum(x)) se ejecutará bastante rápido (por supuesto, df.sum(1) es incluso mejor). Sin embargo, algo como: df.apply(lambda x: x['b'] + 1) se ejecutará en el espacio de python, y en consecuencia es más lento.

4) itertuples no itertuples los datos en una serie, solo los devuelve como una tupla

5) iterrows los datos en una serie. A menos que realmente necesites esto, usa otro método.

6) actualizando un marco vacío a-single-row-at-a-time. He visto este método utilizado demasiado. Es, con mucho, el más lento. Probablemente sea un lugar común (y razonablemente rápido para algunas estructuras de Python), pero un DataFrame hace un buen número de controles en la indexación, por lo que siempre será muy lento actualizar una fila a la vez. Mucho mejor crear nuevas estructuras y concat .

Las operaciones de vectores en Numpy y pandas son mucho más rápidas que las operaciones escalares en Python de vainilla por varias razones:

  • Búsqueda de tipo amortizado : Python es un lenguaje de tipo dynamic, por lo que hay una sobrecarga de tiempo de ejecución para cada elemento en una matriz. Sin embargo, Numpy (y por lo tanto los pandas) realizan cálculos en C (a menudo a través de Cython). El tipo de la matriz se determina solo al inicio de la iteración; solo este ahorro es uno de los mayores triunfos.

  • Mejor almacenamiento en caché : iterar sobre una matriz de C es fácil de almacenar en caché y, por lo tanto, es muy rápido. Un pandas DataFrame es una “tabla orientada a columnas”, lo que significa que cada columna es en realidad solo una matriz. Por lo tanto, las acciones nativas que puede realizar en un DataFrame (como sumr todos los elementos en una columna) tendrán pocos errores de caché.

  • Más oportunidades para el paralelismo : una simple matriz de C puede ser operada a través de instrucciones SIMD. Algunas partes de Numpy habilitan SIMD, dependiendo de su CPU y proceso de instalación. Los beneficios del paralelismo no serán tan dramáticos como la escritura estática y un mejor almacenamiento en caché, pero siguen siendo una ganancia sólida.

Moraleja de la historia: usa las operaciones vectoriales en Numpy y pandas. Son más rápidas que las operaciones escalares en Python por la sencilla razón de que estas operaciones son exactamente lo que un progtwigdor de C habría escrito a mano de todos modos. (Excepto que la noción de matriz es mucho más fácil de leer que los bucles explícitos con instrucciones SIMD incrustadas).

Aquí está la manera de resolver su problema. Todo esto está vectorizado.

 In [58]: df = table1.merge(table2,on='letter') In [59]: df['calc'] = df['number1']*df['number2'] In [60]: df Out[60]: letter number1 number2 calc 0 a 50 0.2 10 1 a 50 0.5 25 2 b -10 0.1 -1 3 b -10 0.4 -4 In [61]: df.groupby('letter')['calc'].max() Out[61]: letter a 25 b -1 Name: calc, dtype: float64 In [62]: df.groupby('letter')['calc'].idxmax() Out[62]: letter a 1 b 2 Name: calc, dtype: int64 In [63]: df.loc[df.groupby('letter')['calc'].idxmax()] Out[63]: letter number1 number2 calc 1 a 50 0.5 25 2 b -10 0.1 -1 

Otra opción es usar to_records() , que es más rápido que itertuples y iterrows .

Pero para su caso, hay mucho espacio para otros tipos de mejoras.

Aquí está mi versión optimizada final

 def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) t2info = table2.to_records() for index, letter, n1 in table1.to_records(): t2 = t2info[grouped.groups[letter].values] # np.multiply is in general faster than "x * y" maxrow = np.multiply(t2.number2, n1).argmax() # `[1:]` removes the index column ret.append(t2[maxrow].tolist()[1:]) global table3 table3 = pd.DataFrame(ret, columns=('letter', 'number2')) 

Prueba de referencia:

 -- iterrows() -- 100 loops, best of 3: 12.7 ms per loop letter number2 0 a 0.5 1 b 0.1 2 c 5.0 3 d 4.0 -- itertuple() -- 100 loops, best of 3: 12.3 ms per loop -- to_records() -- 100 loops, best of 3: 7.29 ms per loop -- Use group by -- 100 loops, best of 3: 4.07 ms per loop letter number2 1 a 0.5 2 b 0.1 4 c 5.0 5 d 4.0 -- Avoid multiplication -- 1000 loops, best of 3: 1.39 ms per loop letter number2 0 a 0.5 1 b 0.1 2 c 5.0 3 d 4.0 

Código completo:

 import pandas as pd import numpy as np #%% Create the original tables t1 = {'letter':['a','b','c','d'], 'number1':[50,-10,.5,3]} t2 = {'letter':['a','a','b','b','c','d','c'], 'number2':[0.2,0.5,0.1,0.4,5,4,1]} table1 = pd.DataFrame(t1) table2 = pd.DataFrame(t2) #%% Create the body of the new table table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=table1.index) print('\n-- iterrows() --') def optimize(t2info, t1info): calculation = [] for index, r in t2info.iterrows(): calculation.append(r['number2'] * t1info) maxrow_in_t2 = calculation.index(max(calculation)) return t2info.loc[maxrow_in_t2] #%% Iterate through filtering relevant data, optimizing, returning info def iterthrough(): for row_index, row in table1.iterrows(): t2info = table2[table2.letter == row['letter']].reset_index() table3.iloc[row_index,:] = optimize(t2info, row['number1']) %timeit iterthrough() print(table3) print('\n-- itertuple() --') def optimize(t2info, n1): calculation = [] for index, letter, n2 in t2info.itertuples(): calculation.append(n2 * n1) maxrow = calculation.index(max(calculation)) return t2info.iloc[maxrow] def iterthrough(): for row_index, letter, n1 in table1.itertuples(): t2info = table2[table2.letter == letter] table3.iloc[row_index,:] = optimize(t2info, n1) %timeit iterthrough() print('\n-- to_records() --') def optimize(t2info, n1): calculation = [] for index, letter, n2 in t2info.to_records(): calculation.append(n2 * n1) maxrow = calculation.index(max(calculation)) return t2info.iloc[maxrow] def iterthrough(): for row_index, letter, n1 in table1.to_records(): t2info = table2[table2.letter == letter] table3.iloc[row_index,:] = optimize(t2info, n1) %timeit iterthrough() print('\n-- Use group by --') def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) for index, letter, n1 in table1.to_records(): t2 = table2.iloc[grouped.groups[letter]] calculation = t2.number2 * n1 maxrow = calculation.argsort().iloc[-1] ret.append(t2.iloc[maxrow]) global table3 table3 = pd.DataFrame(ret) %timeit iterthrough() print(table3) print('\n-- Even Faster --') def iterthrough(): ret = [] grouped = table2.groupby('letter', sort=False) t2info = table2.to_records() for index, letter, n1 in table1.to_records(): t2 = t2info[grouped.groups[letter].values] maxrow = np.multiply(t2.number2, n1).argmax() # `[1:]` removes the index column ret.append(t2[maxrow].tolist()[1:]) global table3 table3 = pd.DataFrame(ret, columns=('letter', 'number2')) %timeit iterthrough() print(table3) 

La versión final es casi 10 veces más rápida que el código original. La estrategia es:

  1. Use groupby para evitar la comparación repetida de valores.
  2. Utilice to_records para acceder a los objetos raw numpy.records.
  3. No opere en DataFrame hasta que haya comstackdo todos los datos.

Sí, Pandas itertuples () es más rápido que iterrows (). puede consultar la documentación: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.iterrows.html

“Para conservar los tipos de texto mientras se iteran sobre las filas, es mejor usar itertuples () que devuelve el nombre de los valores de los valores y que generalmente es más rápido que iterrows”.