Cómo iterar sobre filas en un DataFrame en Pandas

Tengo un DataFrame de los pandas:

import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df

Salida:

   c1   c2
0  10  100
1  11  110
2  12  120

Ahora quiero iterar sobre las filas de este marco. Para cada fila, quiero poder acceder a sus elementos (valores en celdas) por el nombre de las columnas. Por ejemplo:

for row in df.rows:
   print row['c1'], row['c2']

¿Es posible hacer eso en Pandas?

Encontré este pregunta similar. Pero no me da la respuesta que necesito. Por ejemplo, allí se sugiere usar:

for date, row in df.T.iteritems():

or

for row in df.iterrows():

Pero no entiendo lo que el row es el objeto y cómo puedo trabajar con él.

preguntado el 10 de mayo de 13 a las 08:05

El df.iteritems() itera sobre columnas y no sobre filas. Por lo tanto, para que itere sobre las filas, debe transponer (la "T"), lo que significa que cambia las filas y las columnas entre sí (reflejar sobre la diagonal). Como resultado, itera efectivamente el marco de datos original sobre sus filas cuando usa df.T.iteritems() -

En contraste con lo que dice cs95, existen razones perfectamente válidas para querer iterar sobre un marco de datos, por lo que los nuevos usuarios no deben sentirse desalentados. Un ejemplo es si desea ejecutar algún código utilizando los valores de cada fila como entrada. Además, si su marco de datos es razonablemente pequeño (por ejemplo, menos de 1000 elementos), el rendimiento no es realmente un problema. -

@ cs95 Me parece que los marcos de datos son el formato de tabla de referencia en Python. Entonces, cada vez que desee leer en un csv, o tenga una lista de dictados cuyos valores desee manipular, o desee realizar operaciones simples de unión, agrupación o ventana, utilice un marco de datos, incluso si sus datos son comparativamente pequeños. -

@ cs95 No, pero esto fue en respuesta a "usar un DataFrame en absoluto". Mi punto es que es por eso que uno puede tener sus datos en un marco de datos. Si luego desea, por ejemplo, ejecutar un script para cada línea de sus datos, debe iterar sobre ese marco de datos. -

Secundo a @oulenz. Por lo que yo puedo decir pandas es la opción preferida para leer un archivo csv incluso si el conjunto de datos es pequeño. Es simplemente más fácil programar manipular los datos con API:

30 Respuestas

DataFrame.iterrows es un generador que produce tanto el índice como la fila (como una serie):

import pandas as pd

df = pd.DataFrame({'c1': [10, 11, 12], 'c2': [100, 110, 120]})

for index, row in df.iterrows():
    print(row['c1'], row['c2'])
10 100
11 110
12 120

Respondido el 11 de diciembre de 20 a las 08:12

Nota: "Debido a que iterrows devuelve una serie para cada fila, no conservar los tipos de d en las filas". Además, "usted nunca debe modificar algo sobre lo que estás iterando". De acuerdo con pandas 0.19.1 documentos - viddik13

@ viddik13 esa es una gran nota, gracias. Por eso me encontré con un caso en el que valores numéricos como 431341610650 donde se lee como 4.31E+11. ¿Hay alguna forma de evitar la preservación de los dtypes? - Aziz Alto

@AzizAlto uso itertuples, como se explica a continuación. Ver también pandas.pydata.org/pandas-docs/stable/generated/… - Axel

No use iterrows. Itertuples es más rápido y conserva el tipo de datos. MÁS INFORMACIÓN - James L.

Desde la documentación: "La iteración a través de objetos pandas es generalmente lenta. En muchos casos, no es necesario iterar manualmente sobre las filas [...]". Su respuesta es correcta (en el contexto de la pregunta) pero no menciona esto en ninguna parte, por lo que no es muy buena. - cs95

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

Respuesta: NO*!

La iteración en 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 por más de unas pocas miles de filas o tendrá que acostumbrarse a un montón de esperar.

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

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

  1. Vectorización
  2. Citón 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 de Python
  5. DataFrame.itertuples() y iteritems()
  6. DataFrame.iterrows()

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

Apelar a la autoridad

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

La iteración a través de objetos pandas es generalmente 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. Admitiré 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 la necesite. PD: Para saber más sobre mi razón para escribir esta respuesta, salte hasta el final.


Más rápido que el bucle: Vectorización, Citón

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 pivote), uniones y operaciones de agrupación. Mire a través de la documentación en 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 propio usando Extensiones Cython.


Siguiente mejor cosa: Lista de comprensiones*

La comprensión de listas 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 citonizar su código, y 3) está tratando de realizar una transformación por elementos en tu código. Hay un buena cantidad de evidencia para sugerir que las listas de comprensión son lo suficientemente rápidas (e incluso a veces más rápidas) para muchas tareas comunes de Pandas.

La fórmula es sencilla,

# 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 comercial en una función, puede usar una lista de comprensión que la llame. Puede hacer que cosas arbitrariamente complejas funcionen a través de la simplicidad y la velocidad del código Python sin procesar.

Advertencias

Las comprensiones de listas asumen que es fácil trabajar con sus datos, lo que significa 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 integrados de pandas 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.
  2. Cuando se trata de tipos de datos mixtos, debe iterar sobre zip(df['A'], df['B'], ...) en lugar de df[['A', 'B']].to_numpy() ya que este último convierte 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. por suerte ziphacer ping a sus columnas juntas es la solución más sencilla a esto.

*Su millaje puede variar por las razones descritas en el Advertencias la sección de arriba.


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. 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. Limítese a la API donde pueda (es decir, prefiera vec encima vec_numpy).

Debo mencionar, sin embargo, que no siempre es así de claro. 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.


Mi opinión personal *

La mayoría de los análisis realizados sobre las diversas alternativas a la familia iter se han realizado a través de la lente del rendimiento. Sin embargo, en la mayoría de las situaciones, normalmente trabajará en un conjunto de datos de tamaño razonable (nada más allá de unos pocos miles o 100 XNUMX filas) y el rendimiento será secundario a la simplicidad/legibilidad de la solución.

Esta es mi preferencia personal al seleccionar un método para usar en un problema.

Para el novato:

Vectorización (cuando sea posible); apply(); Lista de comprensiones; itertuples()/iteritems(); iterrows(); Citón

Para los más experimentados:

Vectorización (cuando sea posible); apply(); Lista de comprensiones; citón; itertuples()/iteritems(); iterrows()

La vectorización prevalece como el método más idiomático para cualquier problema que pueda ser vectorizado. ¡Busca siempre vectorizar! En caso de duda, consulte los documentos o busque en Stack Overflow una pregunta existente sobre su tarea en particular.

Tiendo a continuar sobre lo malo apply está en muchas de mis publicaciones, pero reconozco que es más fácil para un principiante comprender lo que está haciendo. Además, hay bastantes casos de uso para apply ha explicado en esta publicación mía.

Cython ocupa un lugar más bajo en la lista porque requiere más tiempo y esfuerzo para lograrlo correctamente. Por lo general, nunca necesitará escribir código con pandas que exija este nivel de rendimiento que incluso la comprensión de una lista no puede satisfacer.

* Como con cualquier opinión personal, ¡tómelo con mucha sal!


Para leer más

* 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 cadenas son intrínsecamente difíciles de vectorizar.


Por qué escribí esta respuesta

Una tendencia común que noto entre los nuevos usuarios es hacer preguntas del tipo "¿Cómo puedo iterar sobre mi df para hacer X?". Mostrando código que llama iterrows() mientras hace algo dentro de un for círculo. Aquí está el 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 iterando 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 siquiera preguntarse 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.

Respondido 04 Oct 21, 10:10

Tenga en cuenta que hay advertencias importantes con iterrows y itertuples. Vea esta respuesta y pandas documentos para más detalles. - viddik13

Esta es la única respuesta que se centra en las técnicas idiomáticas que se deben usar con los pandas, lo que la convierte en la mejor respuesta para esta pregunta. Aprendiendo a conseguir el Derecho responde con el Derecho código (en vez de Derecho responde con el Mal código - es decir, ineficiente, no se escala, se ajusta demasiado a datos específicos) es una gran parte del aprendizaje de pandas (y datos en general). - EnlaceBerest

Sin embargo, creo que está siendo injusto con el ciclo for, ya que son solo un poco más lentos que la comprensión de listas en mis pruebas. El truco es hacer un bucle zip(df['A'], df['B']) en lugar de df.iterrows(). - Noche imperecedera

En Comprensiones de lista, el ejemplo de "iteración en varias columnas" necesita una advertencia: DataFrame.values convertirá cada columna a un tipo de datos común. DataFrame.to_numpy() hace esto también. Afortunadamente podemos usar zip con cualquier número de columnas. - David Wassermann

@Dean Recibo esta respuesta con bastante frecuencia y, sinceramente, me confunde. Se trata de formar buenos hábitos. "Mis datos son pequeños y el rendimiento no importa, por lo que se puede excusar mi uso de este antipatrón" ..? Cuando el rendimiento realmente importe algún día, se agradecerá haber preparado las herramientas adecuadas con anticipación. - cs95

Primero considere si realmente necesita iterar sobre filas en un DataFrame. Ver esta respuesta para alternativas

Si aún necesita iterar sobre las filas, puede usar los métodos a continuación. nota algunos advertencias importantes que no se mencionan en ninguna de las otras respuestas.

itertuples() se supone que es más rápido que iterrows()

Pero tenga en cuenta, de acuerdo con los documentos (pandas 0.24.2 en este momento):

  • iterrows: dtype podría no coincidir de una fila a otra

Como iterrows devuelve una serie para cada fila, no conserva dtypes en las filas (los dtypes se conservan en las columnas para DataFrames). Para conservar dtypes mientras se itera sobre las filas, es mejor usar itertuples() que devuelve tuplas con nombre de los valores y que generalmente es mucho más rápido que iterrows()

  • iterrows: No modificar filas

Debes nunca modificar algo sobre lo que estás iterando. No se garantiza que funcione en todos los casos. Según los tipos de datos, el iterador devuelve una copia y no una vista, y escribir en ella no tendrá ningún efecto.

Utiliza la DataFrame.apply () en lugar:

    new_df = df.apply(lambda x: x * 2)
  • itertuplica:

Los nombres de las columnas se cambiarán a nombres posicionales si son identificadores de Python no válidos, repetidos o comienzan con un guión bajo. Con un gran número de columnas (>255), se devuelven tuplas regulares.

Ver pandas docs en iteración para más información.

Respondido el 30 de Septiembre de 21 a las 17:09

Solo una pequeña pregunta de alguien que lee este hilo mucho tiempo después de su finalización: ¿cómo se compara df.apply() con itertuples en términos de eficiencia? - Raúl Guarini

Nota: también puedes decir algo como for row in df[['c1','c2']].itertuples(index=True, name=None): para incluir solo ciertas columnas en el iterador de fila. - Brian Burns,

En lugar de getattr(row, "c1"), puedes usar solo row.c1. - viraptor

Estoy un 90% seguro de que si usas getattr(row, "c1") en lugar de row.c1, pierde cualquier ventaja de rendimiento de itertuples, y si realmente necesita llegar a la propiedad a través de una cadena, debe usar iterrows en su lugar. - noctifobia

Me he topado con esta pregunta porque, aunque sabía que hay división-aplicación-combinación, todavía realmente necesitaba iterar sobre un DataFrame (como dice la pregunta). No todo el mundo tiene el lujo de mejorar con numba y cython (los mismos documentos dicen que "Siempre vale la pena optimizar primero en Python"). Escribí esta respuesta para ayudar a otros a evitar problemas (a veces frustrantes) ya que ninguna de las otras respuestas menciona estas advertencias. Engañar a alguien o decirle "eso es lo correcto" nunca fue mi intención. He mejorado la respuesta. - viddik13

Deberías usar df.iterrows(). Aunque iterar fila por fila no es especialmente eficiente ya que Series hay que crear objetos.

Respondido el 11 de diciembre de 19 a las 18:12

¿Es esto más rápido que convertir DataFrame en una matriz numpy (a través de .values) y operar en la matriz directamente? Tengo el mismo problema, pero terminé convirtiéndome en una matriz numpy y luego usando cython. - vgoklani

@vgoklani Si iterar fila por fila es ineficiente y tiene una matriz numpy que no es un objeto, es casi seguro que usar la matriz numpy sin procesar será más rápido, especialmente para matrices con muchas filas. debe evitar iterar sobre filas a menos que sea absolutamente necesario: phillip nube

Hice algunas pruebas sobre el consumo de tiempo para df.iterrows(), df.itertuples() y zip(df['a'], df['b']) y publiqué el resultado en la respuesta de otro pregunta: stackoverflow.com/a/34311080/2142098 - ricardo wong

Aunque la iterrows() es una buena opción, a veces itertuples() puede ser mucho más rápido:

df = pd.DataFrame({'a': randn(1000), 'b': randn(1000),'N': randint(100, 1000, (1000)), 'x': 'x'})

%timeit [row.a * 2 for idx, row in df.iterrows()]
# => 10 loops, best of 3: 50.3 ms per loop

%timeit [row[1] * 2 for row in df.itertuples()]
# => 1000 loops, best of 3: 541 µs per loop

Respondido el 01 de junio de 16 a las 10:06

Gran parte de la diferencia de tiempo en sus dos ejemplos parece deberse al hecho de que parece estar usando la indexación basada en etiquetas para el comando .iterrows() y la indexación basada en enteros para el comando .itertuples(). - Alex

Para un marco de datos basado en datos financieros (marca de tiempo y flotación 4x), itertuples es 19,57 veces más rápido que iterrows en mi máquina. Solo for a,b,c in izip(df["a"],df["b"],df["c"]: es casi igual de rápido. - Harbun

¿Puedes explicar por qué es más rápido? - abe miessler

@AbeMiessler iterrows() encuadra cada fila de datos en una serie, mientras que itertuples()no es. - miradulo

Tenga en cuenta que el orden de las columnas es en realidad indeterminado, porque df se crea a partir de un diccionario, por lo que row[1] podría referirse a cualquiera de las columnas. Resulta que los tiempos son más o menos los mismos para las columnas enteras y flotantes. - Brian Burns,

También puedes usar df.apply() para iterar sobre filas y acceder a múltiples columnas para una función.

documentos: DataFrame.apply()

def valuation_formula(x, y):
    return x * y * 0.5

df['price'] = df.apply(lambda row: valuation_formula(row['x'], row['y']), axis=1)

Respondido el 01 de junio de 15 a las 07:06

¿El df['price'] se refiere a un nombre de columna en el marco de datos? Estoy tratando de crear un diccionario con valores únicos de varias columnas en un archivo csv. Usé su lógica para crear un diccionario con claves y valores únicos y obtuve un error que decía TypeError: ("Los objetos 'Series' son mutables, por lo que no se pueden codificar", u'ocurrieron en el índice 0') - SRS

Código: df['Clase de trabajo'] = df.apply(fila lambda: dic_update(fila), eje=1) fin de la línea id = 0 fin de la línea def dic_update(fila): si la fila no está en dic: dic[fila] = id id = id + 1 - SRS

Tener el eje predeterminado en 0 es lo peor: zthomas.nc

Darse cuenta de apply no "iteratiza" las filas, sino que aplica una función por filas. El código anterior no funcionaría si realmente do necesita iteraciones e índices, por ejemplo, al comparar valores en diferentes filas (en ese caso, no puede hacer nada más que iterar). - gentil

esta es la respuesta apropiada para pandas - dhruvm

Puede utilizar el df.iloc funciona de la siguiente manera:

for i in range(0, len(df)):
    print(df.iloc[i]['c1'], df.iloc[i]['c2'])

Respondido el 08 de enero de 22 a las 22:01

Sé que uno debería evitar esto a favor de iterrows o itertuples, pero sería interesante saber por qué. ¿Alguna idea? - rocacarvaj

Esta es la única técnica válida que conozco si desea conservar los tipos de datos y también referirse a las columnas por su nombre. itertuples conserva los tipos de datos, pero elimina cualquier nombre que no le guste. iterrows hace lo contrario. - Ken Williams

Pasé horas tratando de navegar a través de las idiosincrasias de las estructuras de datos de pandas para hacer algo simple Y expresivo. Esto da como resultado un código legible. - Sean Anderson

Aunque la for i in range(df.shape[0]) podría acelerar un poco este enfoque, todavía es aproximadamente 3.5 veces más lento que el enfoque iterrows() anterior para mi aplicación. - Kim Miller

En grandes marcos de datos, esto parece mejor ya que my_iter = df.itertuples() se necesita el doble de memoria y mucho tiempo para copiarlo. igual por iterrows(). - Bastiaan

Cómo iterar eficientemente

Si realmente tiene que iterar un marco de datos de Pandas, probablemente querrá evitar usar iterrows(). Existen diferentes métodos y los habituales iterrows() está lejos de ser el mejor. itertuples() puede ser 100 veces más rápido.

En breve:

  • Como regla general, utilice df.itertuples(name=None). En particular, cuando tiene un número fijo de columnas y menos de 255 columnas. Ver punto (3)
  • De lo contrario, utilice df.itertuples() excepto si sus columnas tienen caracteres especiales como espacios o '-'. Ver punto (2)
  • Es posible utilizar itertuples() incluso si su marco de datos tiene columnas extrañas usando el último ejemplo. Ver punto (4)
  • Uso único iterrows() si no puede las soluciones anteriores. Ver punto (1)

Diferentes métodos para iterar sobre filas en un dataframe de Pandas:

Genere un marco de datos aleatorio con un millón de filas y 4 columnas:

    df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
    print(df)

1) Lo habitual iterrows() es conveniente, pero condenadamente lento:

start_time = time.clock()
result = 0
for _, row in df.iterrows():
    result += max(row['B'], row['C'])

total_elapsed_time = round(time.clock() - start_time, 2)
print("1. Iterrows done in {} seconds, result = {}".format(total_elapsed_time, result))

2) El valor predeterminado itertuples() ya es mucho más rápido, pero no funciona con nombres de columna como My Col-Name is very Strange (Debe evitar este método si sus columnas se repiten o si el nombre de una columna no se puede convertir simplemente en un nombre de variable de Python).:

start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
    result += max(row.B, row.C)

total_elapsed_time = round(time.clock() - start_time, 2)
print("2. Named Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

3) El valor predeterminado itertuples() usar name=None es aún más rápido pero no muy conveniente ya que tiene que definir una variable por columna.

start_time = time.clock()
result = 0
for(_, col1, col2, col3, col4) in df.itertuples(name=None):
    result += max(col2, col3)

total_elapsed_time = round(time.clock() - start_time, 2)
print("3. Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

4) Finalmente, el nombrado itertuples() es más lento que el punto anterior, pero no tienes que definir una variable por columna y funciona con nombres de columna como My Col-Name is very Strange.

start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
    result += max(row[df.columns.get_loc('B')], row[df.columns.get_loc('C')])

total_elapsed_time = round(time.clock() - start_time, 2)
print("4. Polyvalent Itertuples working even with special characters in the column name done in {} seconds, result = {}".format(total_elapsed_time, result))

Salida:

         A   B   C   D
0       41  63  42  23
1       54   9  24  65
2       15  34  10   9
3       39  94  82  97
4        4  88  79  54
...     ..  ..  ..  ..
999995  48  27   4  25
999996  16  51  34  28
999997   1  39  61  14
999998  66  51  27  70
999999  51  53  47  99

[1000000 rows x 4 columns]

1. Iterrows done in 104.96 seconds, result = 66151519
2. Named Itertuples done in 1.26 seconds, result = 66151519
3. Itertuples done in 0.94 seconds, result = 66151519
4. Polyvalent Itertuples working even with special characters in the column name done in 2.94 seconds, result = 66151519

Este artículo es una comparación muy interesante entre iterrows e itertuples

Respondido el 11 de junio de 20 a las 14:06

Entonces, ¿POR QUÉ están disponibles estos métodos ineficientes en Pandas en primer lugar? antecedentes por parte de los mantenedores de Pandas? - Monty

@Monty, no siempre es posible vectorizar todas las operaciones. - Romain Caprón

estaba buscando Cómo iterar en filas y columnas y terminó aquí así:

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)

Respondido el 11 de junio de 20 a las 14:06

Cuando sea posible, debe evitar usar iterrows(). Explico por qué en la respuesta. Cómo iterar eficientemente - Romain Caprón

Puede escribir su propio iterador que implementa namedtuple

from collections import namedtuple

def myiter(d, cols=None):
    if cols is None:
        v = d.values.tolist()
        cols = d.columns.values.tolist()
    else:
        j = [d.columns.get_loc(c) for c in cols]
        v = d.values[:, j].tolist()

    n = namedtuple('MyTuple', cols)

    for line in iter(v):
        yield n(*line)

Esto es directamente comparable a pd.DataFrame.itertuples. Mi objetivo es realizar la misma tarea con más eficiencia.


Para el marco de datos dado con mi función:

list(myiter(df))

[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]

O con pd.DataFrame.itertuples:

list(df.itertuples(index=False))

[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]

Una prueba completa
Probamos hacer que todas las columnas estén disponibles y subdividir las columnas.

def iterfullA(d):
    return list(myiter(d))

def iterfullB(d):
    return list(d.itertuples(index=False))

def itersubA(d):
    return list(myiter(d, ['col3', 'col4', 'col5', 'col6', 'col7']))

def itersubB(d):
    return list(d[['col3', 'col4', 'col5', 'col6', 'col7']].itertuples(index=False))

res = pd.DataFrame(
    index=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    columns='iterfullA iterfullB itersubA itersubB'.split(),
    dtype=float
)

for i in res.index:
    d = pd.DataFrame(np.random.randint(10, size=(i, 10))).add_prefix('col')
    for j in res.columns:
        stmt = '{}(d)'.format(j)
        setp = 'from __main__ import d, {}'.format(j)
        res.at[i, j] = timeit(stmt, setp, number=100)

res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True);

enter image description here

enter image description here

respondido 07 nov., 17:04

Para las personas que no quieren leer el código: la línea azul es intertuples, la línea naranja es una lista de un iterador a través de un bloque de rendimiento. interrows no se compara. - James L.

Para recorrer todas las filas en un dataframe puedes usar:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]

Respondido 04 Abr '17, 21:04

Esto es indexación encadenada. No recomiendo hacer esto. - cs95

@cs95 ¿Qué recomendarías en su lugar? - Pedro Lobito

Si desea que esto funcione, llame a df.columns.get_loc para obtener la posición de índice entero de la columna de fecha (fuera del ciclo), luego use una única llamada de indexación de iloc en el interior. - cs95

 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]

contestado el 07 de mayo de 19 a las 07:05

¿Cómo es el rendimiento de esta opción cuando se usa en un marco de datos grande (millones de filas, por ejemplo)? - Bazyli Debowski

Honestamente, no lo sé exactamente, creo que en comparación con la mejor respuesta, el tiempo transcurrido será más o menos el mismo, porque ambos casos usan "for"-construcción. Pero la memoria puede ser diferente en algunos casos. - Grag2015

Esto es indexación encadenada. ¡No uses esto! - cs95

A veces, un patrón útil es:

# Borrowing @KutalmisB df example
df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
# The to_dict call results in a list of dicts
# where each row_dict is a dictionary with k:v pairs of columns:value for that row
for row_dict in df.to_dict(orient='records'):
    print(row_dict)

Lo que resulta en:

{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}

Respondido 14 Abr '19, 00:04

Tenemos múltiples opciones para hacer lo mismo, muchas personas han compartido sus respuestas.

Encontré a continuación dos métodos fáciles y eficientes de hacer:

  1. Marco de datos.iterrows()
  2. DataFrame.itertuples()

Ejemplo:

 import pandas as pd
 inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
 df = pd.DataFrame(inp)
 print (df)

 #With iterrows method 

 for index, row in df.iterrows():
     print(row["c1"], row["c2"])

 #With itertuples method

 for row in df.itertuples(index=True, name='Pandas'):
     print(row.c1, row.c2)

Nota: se supone que itertuples() es más rápido que iterrows()

Respondido el 06 de enero de 22 a las 09:01

Esto en realidad responde a la pregunta. +1 - codificador joe

En Pocas Palabras

  • Usa la vectorización si es posible
  • Si una operación no se puede vectorizar, use listas de comprensión
  • Si necesita un solo objeto que represente toda la fila, use itertuples
  • Si lo anterior es demasiado lento, intente más rápido.aplicar
  • Si sigue siendo demasiado lento, pruebe con un Citón rutina

punto de referencia

Benchmark de iteración sobre filas en un Pandas DataFrame

Respondido 21 Abr '21, 17:04

Noticias: cs95 ha actualizado su respuesta para incluir vectorización numpy simple. Simplemente puede referirse a su respuesta.


cs95 muestra que la vectorización de Pandas supera con creces a otros métodos de Pandas para computar cosas con marcos de datos.

Quería agregar que si primero convierte el marco de datos en una matriz NumPy y luego usa la vectorización, es incluso más rápido que la vectorización de marcos de datos de Pandas (y eso incluye el tiempo para volver a convertirlo en una serie de marcos de datos).

Si agrega las siguientes funciones al código de referencia de cs95, esto se vuelve bastante evidente:

def np_vectorization(df):
    np_arr = df.to_numpy()
    return pd.Series(np_arr[:,0] + np_arr[:,1], index=df.index)

def just_np_vectorization(df):
    np_arr = df.to_numpy()
    return np_arr[:,0] + np_arr[:,1]

Ingrese la descripción de la imagen aquí

Respondido 27 ago 21, 06:08

Para recorrer todas las filas en un dataframe y utilizan el valores de cada fila convenientemente, namedtuples se puede convertir a ndarrays. Por ejemplo:

df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])

Iterando sobre las filas:

for row in df.itertuples(index=False, name='Pandas'):
    print np.asarray(row)

resulta en:

[ 1.   0.1]
[ 2.   0.2]

Tenga en cuenta que si index=True, el índice se agrega como el primer elemento de la tupla, que puede ser indeseable para algunas aplicaciones.

Respondido 24 Abr '18, 09:04

Hay una manera de iterar tirar filas mientras se obtiene un DataFrame a cambio, y no una Serie. No veo que nadie mencione que puede pasar el índice como una lista para que la fila se devuelva como un marco de datos:

for i in range(len(df)):
    row = df.iloc[[i]]

Tenga en cuenta el uso de corchetes dobles. Esto devuelve un DataFrame con una sola fila.

Respondido 17 Oct 19, 16:10

Esto fue muy útil para obtener la enésima fila más grande en un marco de datos después de la clasificación. ¡Gracias! - Jason Harrison

Tanto para ver como para modificar valores, usaría iterrows(). En un bucle for y utilizando el desempaquetado de tuplas (ver el ejemplo: i, row), Yo uso el row por solo ver el valor y usar i con loc cuando quiero modificar valores. Como se indicó en respuestas anteriores, aquí no debe modificar algo sobre lo que está iterando.

for i, row in df.iterrows():
    df_column_A = df.loc[i, 'A']
    if df_column_A == 'Old_Value':
        df_column_A = 'New_value'  

Aquí el row en el ciclo hay una copia de esa fila, y no una vista de ella. Por lo tanto, NO deberías escribir algo como row['A'] = 'New_Value', no modificará el DataFrame. Sin embargo, puedes usar i y loc y especifique el DataFrame para hacer el trabajo.

Respondido 28 Feb 20, 17:02

Hay tantas formas de iterar sobre las filas en el marco de datos de Pandas. Una manera muy simple e intuitiva es:

df = pd.DataFrame({'A':[1, 2, 3], 'B':[4, 5, 6], 'C':[7, 8, 9]})
print(df)
for i in range(df.shape[0]):
    # For printing the second column
    print(df.iloc[i, 1])

    # For printing more than one columns
    print(df.iloc[i, [0, 2]])

Respondido el 11 de junio de 20 a las 14:06

La forma más fácil, usa el apply función

def print_row(row):
   print row['c1'], row['c2']

df.apply(lambda row: print_row(row), axis=1)

respondido 02 nov., 20:21

Como muchas respuestas aquí señalan correcta y claramente, generalmente no debe intentar hacer un bucle en Pandas, sino que debe escribir código vectorizado. Pero la pregunta sigue siendo si debe vez escriba bucles en Pandas y, de ser así, la mejor forma de crear bucles en esas situaciones.

Creo que hay al menos una situación general en la que los bucles son apropiados: cuando necesita calcular alguna función que depende de los valores en otros filas de una manera algo compleja. En este caso, el código de bucle suele ser más simple, más legible y menos propenso a errores que el código vectorizado. El código de bucle podría incluso ser más rápido también.

Intentaré mostrar esto con un ejemplo. Suponga que desea tomar una suma acumulativa de una columna, pero restablecerla cada vez que alguna otra columna sea igual a cero:

import pandas as pd
import numpy as np

df = pd.DataFrame( { 'x':[1,2,3,4,5,6], 'y':[1,1,1,0,1,1]  } )

#   x  y  desired_result
#0  1  1               1
#1  2  1               3
#2  3  1               6
#3  4  0               4
#4  5  1               9
#5  6  1              15

Este es un buen ejemplo en el que ciertamente podría escribir una línea de Pandas para lograr esto, aunque no es especialmente legible, especialmente si aún no tiene bastante experiencia con Pandas:

df.groupby( (df.y==0).cumsum() )['x'].cumsum()

Eso será lo suficientemente rápido para la mayoría de las situaciones, aunque también podría escribir código más rápido evitando el groupby, pero probablemente será incluso menos legible.

Alternativamente, ¿qué pasa si escribimos esto como un bucle? Podrías hacer algo como lo siguiente con NumPy:

import numba as nb

@nb.jit(nopython=True)  # Optional
def custom_sum(x,y):
    x_sum = x.copy()
    for i in range(1,len(df)):
        if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]
    return x_sum

df['desired_result'] = custom_sum( df.x.to_numpy(), df.y.to_numpy() )

Es cierto que se requiere un poco de sobrecarga para convertir las columnas DataFrame en matrices NumPy, pero la parte central del código es solo una línea de código que puede leer incluso si no sabe nada sobre Pandas o NumPy:

if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]

Y este código es en realidad Más rápida que el código vectorizado. En algunas pruebas rápidas con 100,000 10 filas, lo anterior es unas XNUMX veces más rápido que el agrupar por Acercarse. Tenga en cuenta que una clave para la velocidad es numba, que es opcional. Sin la línea "@nb.jit", el código de bucle es en realidad unas 10 veces más lento que el agrupar por enfoque.

Claramente, este ejemplo es lo suficientemente simple como para que prefiera una línea de pandas a escribir un bucle con su sobrecarga asociada. Sin embargo, hay versiones más complejas de este problema para las que la legibilidad o la velocidad del enfoque de bucle NumPy/numba probablemente tenga sentido.

Respondido 23 Jul 21, 19:07

También puede hacer la indexación de NumPy para acelerar aún más. No es realmente una iteración, pero funciona mucho mejor que la iteración para ciertas aplicaciones.

subset = row['c1'][0:5]
all = row['c1'][:]

También puede convertirlo en una matriz. Se supone que estos índices/selecciones ya actúan como matrices NumPy, pero me encontré con problemas y necesitaba emitir

np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) # Resize every image in an hdf5 file

Respondido el 11 de junio de 20 a las 14:06

Este ejemplo usa iloc para aislar cada dígito en el marco de datos.

import pandas as pd

 a = [1, 2, 3, 4]
 b = [5, 6, 7, 8]

 mjr = pd.DataFrame({'a':a, 'b':b})

 size = mjr.shape

 for i in range(size[0]):
     for j in range(size[1]):
         print(mjr.iloc[i, j])

respondido 16 mar '19, 22:03

Algunas bibliotecas (por ejemplo, una biblioteca de interoperabilidad de Java que uso) requieren que los valores se pasen en una fila a la vez, por ejemplo, si se transmiten datos. Para replicar la naturaleza de la transmisión, "transmito" los valores de mi marco de datos uno por uno, escribí lo siguiente, que es útil de vez en cuando.

class DataFrameReader:
  def __init__(self, df):
    self._df = df
    self._row = None
    self._columns = df.columns.tolist()
    self.reset()
    self.row_index = 0

  def __getattr__(self, key):
    return self.__getitem__(key)

  def read(self) -> bool:
    self._row = next(self._iterator, None)
    self.row_index += 1
    return self._row is not None

  def columns(self):
    return self._columns

  def reset(self) -> None:
    self._iterator = self._df.itertuples()

  def get_index(self):
    return self._row[0]

  def index(self):
    return self._row[0]

  def to_dict(self, columns: List[str] = None):
    return self.row(columns=columns)

  def tolist(self, cols) -> List[object]:
    return [self.__getitem__(c) for c in cols]

  def row(self, columns: List[str] = None) -> Dict[str, object]:
    cols = set(self._columns if columns is None else columns)
    return {c : self.__getitem__(c) for c in self._columns if c in cols}

  def __getitem__(self, key) -> object:
    # the df index of the row is at index 0
    try:
        if type(key) is list:
            ix = [self._columns.index(key) + 1 for k in key]
        else:
            ix = self._columns.index(key) + 1
        return self._row[ix]
    except BaseException as e:
        return None

  def __next__(self) -> 'DataFrameReader':
    if self.read():
        return self
    else:
        raise StopIteration

  def __iter__(self) -> 'DataFrameReader':
    return self

Que se puede utilizar:

for row in DataFrameReader(df):
  print(row.my_column_name)
  print(row.to_dict())
  print(row['my_column_name'])
  print(row.tolist())

Y conserva la asignación de valores/nombres para las filas que se iteran. Obviamente, es mucho más lento que usar apply y Cython como se indicó anteriormente, pero es necesario en algunas circunstancias.

Respondido el 10 de diciembre de 19 a las 09:12

df.iterrows() devuelve tupla(a, b) donde a es el índice y b es la fila.

Respondido el 05 de enero de 22 a las 20:01

Junto con las excelentes respuestas en esta publicación, voy a proponer Divide y vencerás enfoque, no estoy escribiendo esta respuesta para abolir las otras grandes respuestas, sino para cumplirlas con otro enfoque que funcionó de manera eficiente para mí. Tiene dos pasos de splitting y merging el marco de datos de pandas:

PROS de divide y vencerás:

  • No necesita usar la vectorización ni ningún otro método para convertir el tipo de su marco de datos en otro tipo
  • No necesita Cythonize su código, lo que normalmente le lleva más tiempo.
  • Ambos iterrows() y itertuples() en mi caso, tenían el mismo rendimiento en todo el marco de datos
  • Depende de tu elección de corte index, podrá acelerar exponencialmente la iteración. Lo mas alto index, más rápido será el proceso de iteración.

CONTRAS de divide y vencerás:

  • No debe tener dependencia sobre el proceso de iteración para el mismo marco de datos y diferentes rebanada. Es decir, si desea leer o escribir de otros rebanada, tal vez sea difícil hacer eso.

=================== Enfoque divide y vencerás =================

Paso 1: dividir/rebanar

En este paso, dividiremos la iteración en todo el marco de datos. Piense que va a leer un archivo csv en pandas df y luego repetirlo. En tal caso, tengo 5,000,000 de registros y los voy a dividir en 100,000 registros.

NOTA: Necesito reiterar como otro análisis de tiempo de ejecución explicado en las otras soluciones en esta página, "número de registros" tiene una proporción exponencial de "tiempo de ejecución" en la búsqueda en el df. Basado en el punto de referencia en mis datos aquí están los resultados:

Number of records | Iteration per second
========================================
100,000           | 500 it/s
500,000           | 200 it/s
1,000,000         | 50 it/s
5,000,000         | 20 it/s

Paso 2: Fusión

Este será un paso fácil, simplemente combine todos los archivos csv escritos en un marco de datos y escríbalo en un archivo csv más grande.

Aquí está el código de ejemplo:

# Step 1 (Splitting/Slicing)
import pandas as pd
df_all = pd.read_csv('C:/KtV.csv')
df_index = 100000
df_len = len(df)
for i in range(df_len // df_index + 1):
    lower_bound = i * df_index 
    higher_bound = min(lower_bound + df_index, df_len)
    # splitting/slicing df (make sure to copy() otherwise it will be a view
    df = df_all[lower_bound:higher_bound].copy()
    '''
    write your iteration over the sliced df here
    using iterrows() or intertuples() or ...
    '''
    # writing into csv files
    df.to_csv('C:/KtV_prep_'+str(i)+'.csv')



# Step 2 (Merging)
filename='C:/KtV_prep_'
df = (pd.read_csv(f) for f in [filename+str(i)+'.csv' for i in range(ktv_len // ktv_index + 1)])
df_prep_all = pd.concat(df)
df_prep_all.to_csv('C:/KtV_prep_all.csv')

Referencia:

Forma eficiente de iteración sobre datafreame

Concatenar archivos csv en un Pandas Dataframe

Respondido 31 Oct 20, 13:10

As la respuesta aceptada estados, la manera más rápida de aplicar una función sobre las filas es usar un función vectorizada, el llamado NumPy ufuncs (funciones universales).

Pero, ¿qué debe hacer cuando la función que desea aplicar aún no está implementada en NumPy?

Bueno, usando el vectorize decorador de numba, puede crear fácilmente ufuncs directamente en Python de esta manera:

from numba import vectorize, float64

@vectorize([float64(float64)])
def f(x):
    #x is your line, do something with it, and return a float

La documentación para esta función está aquí: Creando funciones universales NumPy

Respondido el 05 de enero de 22 a las 20:01

Probablemente la solución más elegante (pero ciertamente no la más eficiente):

for row in df.values:
    c2 = row[1]
    print(row)
    # ...

for c1, c2 in df.values:
    # ...

Tenga en cuenta que:

  • el documentación recomienda explícitamente utilizar .to_numpy()
  • la matriz NumPy producida tendrá un dtype que se ajuste a todas las columnas, en el peor de los casos object
  • existen buenas razones no usar un bucle en primer lugar

Aún así, creo que esta opción debería incluirse aquí, como una solución directa a un (uno debería pensar) problema trivial.

Respondido 02 ago 21, 11:08

Una mejor manera es convertir el marco de datos en un diccionario usando Código Postal, creando un par de valores clave y luego acceda a los valores de fila por clave.

Mi respuesta muestra cómo usar un diccionario como alternativa a Pandas. Algunas personas piensan que los diccionarios y las tuplas son más eficientes. Puede reemplazar fácilmente el diccionario con una lista de tuplas con nombre.

 inp = [{'c1':10, 'c2':100}, {'c1':11, 'c2':110}, {'c1':12, 'c2':120}]
 df = pd.DataFrame(inp)
 print(df)

 for row in inp:
     for (k, v) in zip(row.keys(), row.values()):
         print(k, v)

Salida:

c1 10
c2 100
c1 11
c2 110
c1 12
c2 120

Respondido el 05 de enero de 22 a las 20:01

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.