¿Cómo iterar sobre filas en un DataFrame en Pandas?
Respuesta: NO HACER * !
La iteración en los pandas es un antipatrón, y es algo que solo debe hacer cuando haya agotado todas las demás opciones. No debe usar ninguna función con " iter
" en su nombre durante más de unos pocos miles de filas o tendrá que acostumbrarse a muchas esperas.
¿Quieres imprimir un DataFrame? Uso DataFrame.to_string()
.
¿Quieres calcular algo? En ese caso, busque métodos en este orden (lista modificada desde aquí ):
- Vectorización
- Rutinas de Cython
- Lista de comprensiones (
for
bucle de vainilla )
DataFrame.apply()
: i) Reducciones que se pueden realizar en cython, ii) Iteración en el espacio python
DataFrame.itertuples()
y iteritems()
DataFrame.iterrows()
iterrows
y itertuples
(ambos reciben muchos votos en respuestas a esta pregunta) deben usarse en circunstancias muy raras, como generar objetos de fila / tuplas de nombre para procesamiento secuencial, que es realmente lo único para lo que estas funciones son útiles.
Apelar a la autoridad
La página de documentos en la iteración tiene un enorme cuadro rojo de advertencia que dice:
Iterar a través de objetos de pandas es generalmente lento. En muchos casos, no es necesario iterar manualmente sobre las filas [...].
* En realidad es un poco más complicado que "no". df.iterrows()
es la respuesta correcta a esta pregunta, pero "vectorizar sus operaciones" es la mejor. Reconoceré que hay circunstancias en las que no se puede evitar la iteración (por ejemplo, algunas operaciones en las que el resultado depende del valor calculado para la fila anterior). Sin embargo, se necesita cierta familiaridad con la biblioteca para saber cuándo. Si no está seguro de si necesita una solución iterativa, probablemente no. PD: Para saber más sobre mi justificación para escribir esta respuesta, salte al final.
Una buena cantidad de operaciones y cálculos básicos son "vectorizados" por pandas (ya sea a través de NumPy o mediante funciones Cythonized). Esto incluye aritmética, comparaciones, (la mayoría) reducciones, remodelación (como pivotar), uniones y operaciones grupales. Consulte la documentación sobre la funcionalidad básica esencial para encontrar un método vectorizado adecuado para su problema.
Si no existe ninguno, siéntase libre de escribir el suyo usando extensiones de cython personalizadas .
La lista de comprensiones debe ser su próximo puerto de escala si 1) no hay una solución vectorizada disponible, 2) el rendimiento es importante, pero no lo suficientemente importante como para pasar por la molestia de cifrar su código, y 3) está tratando de realizar una transformación de elementos en tu código Hay una buena cantidad de evidencia que sugiere que las comprensiones de listas son lo suficientemente rápidas (e incluso a veces más rápidas) para muchas tareas comunes de pandas.
La formula es simple,
# iterating over one column - `f` is some function that processes your data
result = [f(x) for x in df['col']]
# iterating over two columns, use `zip`
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
# iterating over multiple columns - same data type
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
# iterating over multiple columns - differing data type
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]
Si puede encapsular su lógica de negocios en una función, puede usar una comprensión de lista que la llame. Puede hacer que las cosas arbitrariamente complejas funcionen a través de la simplicidad y la velocidad de Python sin procesar.
Las
comprensiones de la lista de advertencias suponen que es fácil trabajar con sus datos; lo que eso significa es que sus tipos de datos son consistentes y no tiene NaN, pero esto no siempre se puede garantizar.
- El primero es más obvio, pero cuando se trata de NaNs, prefiera los métodos pandas incorporados si existen (porque tienen una lógica de manejo de casos de esquina mucho mejor), o asegúrese de que su lógica comercial incluya la lógica de manejo de NaN adecuada.
- Cuando se trata de tipos de datos mixtos, debe repetir en
zip(df['A'], df['B'], ...)
lugar de hacerlo, df[['A', 'B']].to_numpy()
ya que este último implícitamente eleva los datos al tipo más común. Como ejemplo, si A es numérico y B es una cadena, to_numpy()
convertirá toda la matriz en una cadena, que puede no ser lo que desea. Afortunadamente, hacer zip
ping en las columnas es la solución más sencilla para esto.
* YMMV por los motivos descritos en la sección Advertencias anterior.
Un ejemplo obvio
Demostremos la diferencia con un ejemplo simple de agregar dos columnas de pandas A + B
. Esta es una operación vectorizable, por lo que será fácil contrastar el rendimiento de los métodos discutidos anteriormente.
Código de evaluación comparativa, para su referencia.
Sin embargo, debo mencionar que no siempre es así de seco. A veces, la respuesta a "cuál es el mejor método para una operación" es "depende de sus datos". Mi consejo es probar diferentes enfoques en sus datos antes de decidirse por uno.
Otras lecturas
* Los métodos de cadena de pandas están "vectorizados" en el sentido de que se especifican en la serie pero operan en cada elemento. Los mecanismos subyacentes siguen siendo iterativos, porque las operaciones de cadena son inherentemente difíciles de vectorizar.
¿Por qué escribí esta respuesta?
Una tendencia común que noto de los nuevos usuarios es hacer preguntas de la forma "¿cómo puedo iterar sobre mi df para hacer X?". Mostrando código que llama iterrows()
mientras hace algo dentro de un bucle for. Aquí es por qué. Un nuevo usuario de la biblioteca que no haya sido introducido al concepto de vectorización probablemente visualizará el código que resuelve su problema al iterar sobre sus datos para hacer algo. Sin saber cómo iterar sobre un DataFrame, lo primero que hacen es buscarlo en Google y terminar aquí, en esta pregunta. Luego ven la respuesta aceptada que les dice cómo hacerlo, y cierran los ojos y ejecutan este código sin preguntarse primero si la iteración no es lo correcto.
El objetivo de esta respuesta es ayudar a los nuevos usuarios a comprender que la iteración no es necesariamente la solución a cada problema, y que podrían existir soluciones mejores, más rápidas y más idiomáticas, y que vale la pena invertir tiempo en explorarlas. No estoy tratando de comenzar una guerra de iteración contra vectorización, pero quiero que los nuevos usuarios estén informados cuando desarrollen soluciones a sus problemas con esta biblioteca.