Saltar al contenido

python – Cómo iterar sobre filas en un DataFrame en Pandas

septiembre 23, 2021
apple touch icon@2

¿Cómo iterar sobre filas en un DataFrame en Pandas?

La iteración en Pandas es un anti-patrón y es algo que solo debe hacer cuando haya agotado todas las demás opciones. No debe utilizar ninguna función con «iter«en su nombre durante más de unos pocos miles de filas o tendrá que acostumbrarse a lote de esperar.

¿Quieres imprimir un DataFrame? Usar DataFrame.to_string().

¿Quieres calcular algo? En ese caso, busque métodos en este orden (lista modificada desde aquí):

  1. Vectorización
  2. Cython rutinas
  3. Lista de comprensiones (vainilla for círculo)
  4. DataFrame.apply(): i) Reducciones que se pueden realizar en Cython, ii) Iteración en el espacio Python
  5. DataFrame.itertuples() y iteritems()
  6. DataFrame.iterrows()

iterrows y itertuples (ambos reciben muchos votos en las respuestas a esta pregunta) deben usarse en circunstancias muy raras, como generar objetos de fila / nombres de tuplas para el procesamiento secuencial, que es realmente para lo único que son útiles estas funciones.

Apelar a la autoridad

La página de documentación en iteración tiene un enorme cuadro de advertencia rojo que dice:

La iteración a través de los objetos pandas suele ser lenta. 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 algo de familiaridad con la biblioteca para saber cuándo. Si no está seguro de si necesita una solución iterativa, probablemente no la necesite. PD: Para saber más sobre mi razón de ser para escribir esta respuesta, vaya al final.


Un buen número 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) de reducciones, remodelación (como pivotar), uniones y operaciones de grupo. Mire la documentación en Funcionalidad básica esencial para encontrar un método vectorizado adecuado para su problema.

Si no existe ninguno, no dude en escribir el suyo propio utilizando Extensiones Cython.


Las listas por comprensión deberían 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 cythonizar su código, y 3) está tratando de realizar una transformación de elementos en su código. Existe 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 fórmula 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 empresarial en una función, puede usar una lista de comprensión que la llame. Puede hacer que las cosas arbitrariamente complejas funcionen a través de la simplicidad y la velocidad del código Python sin procesar.

Advertencias

Las listas por comprensión asumen 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.

  1. El primero es más obvio, pero cuando se trata de NaN, prefiera los métodos pandas integrados si existen (porque tienen una lógica de manejo de casos de esquina mucho mejor), o asegúrese de que su lógica empresarial incluya la lógica de manejo de NaN adecuada.
  2. Cuando se trata de tipos de datos mixtos, debe iterar zip(df['A'], df['B'], ...) en lugar de df[['A', 'B']].to_numpy() ya que este último eleva implícitamente 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 zipHacer ping sus columnas juntas es la solución más sencilla para esto.

* Su millaje puede variar por las razones descritas en el Advertencias sección 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 desempeño de los métodos discutidos anteriormente.

y44RJ

Código de evaluación comparativa, para su referencia. La línea en la parte inferior mide una función escrita en numpandas, un estilo de Pandas que se mezcla fuertemente con NumPy para exprimir el máximo rendimiento. Se debe evitar escribir código numpandas a menos que sepa lo que está haciendo. Apéguese a la API donde pueda (es decir, prefiera vec sobre vec_numpy).

Debo mencionar, sin embargo, 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 sobre 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 con cadenas son intrínsecamente difíciles de vectorizar.


Por qué escribí esta respuesta

Una tendencia común que noto de los nuevos usuarios es hacer preguntas del tipo «¿Cómo puedo iterar sobre mi gl para hacer X?». Mostrando código que llama iterrows() mientras haces algo dentro de un for círculo. He aquí por qué. Un nuevo usuario de la biblioteca que no haya sido introducido al concepto de vectorización probablemente imaginará el código que resuelve su problema como iterando sobre sus datos para hacer algo. Sin saber cómo iterar sobre un DataFrame, lo primero que hacen es buscar 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 siquiera 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 todos los problemas, 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 iniciar una guerra de iteración versus vectorización, pero quiero que los nuevos usuarios estén informados cuando desarrollen soluciones a sus problemas con esta biblioteca.

close