Cómo iterar sobre filas en un DataFrame en Pandas

2928

Tengo un DataFramede Pandas:

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

Producción:

   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é esta pregunta similar . Pero no me da la respuesta que necesito. Por ejemplo, se sugiere utilizar:

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

o

for row in df.iterrows():

Pero no entiendo qué es el rowobjeto y cómo puedo trabajar con él.

8
  • 18
    Df.iteritems () itera sobre columnas y no filas. Por lo tanto, para hacerlo iterar sobre filas, debe transponer (la "T"), lo que significa que cambia filas y columnas entre sí (refleja en diagonal). Como resultado, itera de manera efectiva el marco de datos original sobre sus filas cuando usa df.T.iteritems ()Stefan Gruenwald 14 de diciembre de 2017 a las 23:41
  • 85
    En contraste con lo que dice cs95, existen muy buenas razones para querer iterar sobre un marco de datos, por lo que los nuevos usuarios no deben sentirse desanimados. 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. oulenz 16/10/19 a las 8:53
  • 2
    @ cs95 Me parece que los marcos de datos son el formato de tabla de referencia en Python. Entonces, siempre que desee leer en un csv, o tenga una lista de dictados cuyos valores desee manipular, o desee realizar operaciones simples de combinación, agrupación o ventana, use un marco de datos, incluso si sus datos son comparativamente pequeños. oulenz 16/11/19 a las 12:19
  • 3
    @ cs95 No, pero esto fue en respuesta a "usar un DataFrame". Mi punto es que esta es la razón por la 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. oulenz 16/11/19 a las 18:55
  • 28
    Yo segundo @oulenz. Por lo que puedo decir, pandases la opción preferida de leer un archivo csv incluso si el conjunto de datos es pequeño. Es simplemente más fácil programar manipular los datos con APIF.S. 18/11/19 a las 21:29
3902

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
8
  • 293
    Nota: "Debido a que iterrows devuelve una serie para cada fila, no conserva dtypes en las filas". Además, " nunca debe modificar algo sobre lo que está iterando". Según pandas 0.19.1 docsviddik13 7/12/2016 a las 16:24
  • 6
    @ viddik13 esa es una gran nota gracias. Debido a eso, me encontré con un caso en el que los valores numéricos como 431341610650donde se leen como 4.31E+11. ¿Hay alguna forma de evitar la conservación de los dtypes? Aziz Alto 5 de septiembre de 2017 a las 16:30
  • 43
    @AzizAlto use itertuples, como se explica a continuación. Véase también pandas.pydata.org/pandas-docs/stable/generated/…Axel 7 de septiembre de 2017 a las 11:45
  • 143
    No use iterrows. Itertuples es más rápido y conserva el tipo de datos. Más infoJames L. 1 dic 2017 a las 16:14
  • 17
    De la documentación : "Iterar a través de objetos pandas es generalmente lento. En muchos casos, no es necesario iterar manualmente sobre las filas [...]". Su respuesta es correcta (en el contexto de la pregunta) pero no lo menciona en ninguna parte, por lo que no es muy buena. cs95 28 de mayo de 2019 a las 17 h.
1398
+100

How to iterate over rows in a DataFrame in Pandas?

Respuesta: ¡NO * !

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 esperar mucho .

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

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

  1. Vectorización
  2. Rutinas de Cython
  3. Lista de comprensiones ( forbucle de vainilla )
  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()

iterrowsy 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 sobre la iteración tiene un enorme cuadro de advertencia rojo que dice:

Iterating through pandas objects is generally slow. In many cases, iterating manually over the rows is not needed [...].

* De hecho, 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.


Más rápido que el bucle: vectorización , Cython

Un buen número de operaciones y cálculos básicos son "vectorizados" por pandas (ya sea mediante NumPy o mediante funciones Cythonized). Esto incluye aritmética, comparaciones, (la mayoría) de reducciones, remodelación (como pivotar), uniones y operaciones de grupo. Consulte la documentación sobre la 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 personalizadas .


Siguiente mejor opción: Lista de comprensiones *

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 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 en zip(df['A'], df['B'], ...)lugar de hacerlo, df[['A', 'B']].to_numpy()ya que este último envía 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, hacer zipping a sus columnas juntas es la solución más sencilla para esto.

* Su millaje puede variar por las razones descritas 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 desempeño 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. Sobre el inicio del API donde se puede (es decir, prefieren vecmás 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 se hace algo dentro de un forbucle. 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.

dieciséis
  • 3
    Tenga en cuenta que hay advertencias importantes con iterrowsy itertuples. Consulte esta respuesta y los documentos de pandas para obtener más detalles. viddik13 30 de mayo de 2019 a las 11:56
  • 90
    Esta es la única respuesta que se centra en las técnicas idiomáticas que se deben usar con los pandas, por lo que es la mejor respuesta para esta pregunta. Aprender a obtener la respuesta correcta con el código correcto (en lugar de la respuesta correcta con el código incorrecto , es decir, ineficiente, no se escala, se ajusta demasiado a datos específicos) es una gran parte del aprendizaje de los pandas (y los datos en general). LinkBerest 30 de mayo de 2019 a las 14:26
  • 11
    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 la lista en mis pruebas. El truco consiste en hacer un bucle en zip(df['A'], df['B'])lugar de df.iterrows(). Imperishable Night 24 de junio de 2019 a las 0:58
  • 3
    En Lista de comprensiones, el ejemplo de "iteración sobre varias columnas" necesita una advertencia: DataFrame.valuesconvertirá cada columna en un tipo de datos común. DataFrame.to_numpy()hace esto también. Afortunadamente podemos usarlo zipcon cualquier número de columnas. David Wasserman 16/01/20 a las 20:44
  • 2
    @ Dean Recibo esta respuesta con bastante frecuencia y honestamente 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 el uso de este antipatrón" ...? Cuando el rendimiento realmente importe algún día, se agradecerá por haber preparado las herramientas adecuadas de antemano. cs95 26 de julio de 2020 a las 4:46
471

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

Si aún necesita iterar sobre las filas, puede usar los métodos a continuación. Tenga en cuenta algunas 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: dtypepuede que no coincida de una fila a otra

    Because iterrows returns a Series for each row, it does not preserve dtypes across the rows (dtypes are preserved across columns for DataFrames). To preserve dtypes while iterating over the rows, it is better to use itertuples() which returns namedtuples of the values and which is generally much faster than iterrows()

  • iterrows: no modificar filas

    You should never modify something you are iterating over. This is not guaranteed to work in all cases. Depending on the data types, the iterator returns a copy and not a view, and writing to it will have no effect.

    Utilice DataFrame.apply () en su lugar:

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

    The column names will be renamed to positional names if they are invalid Python identifiers, repeated, or start with an underscore. With a large number of columns (>255), regular tuples are returned.

Consulte los documentos de pandas sobre la iteración para obtener más detalles.

9
  • 5
    Solo una pequeña pregunta de alguien que lee este hilo tanto tiempo después de su finalización: ¿cómo se compara df.apply () con itertuples en términos de eficiencia? Raul Guarini 26/01/18 a las 13:16
  • 6
    Nota: también puede decir algo como for row in df[['c1','c2']].itertuples(index=True, name=None):incluir solo ciertas columnas en el iterador de fila. Brian Burns 29 de junio de 2018 a las 7:29
  • 13
    En lugar de getattr(row, "c1"), puede usar solo row.c1. viraptor 13/08/18 a las 6:20
  • 1
    Estoy aproximadamente un 90% seguro de que si usa en getattr(row, "c1")lugar de row.c1, pierde cualquier ventaja de rendimiento itertuples, y si realmente necesita llegar a la propiedad a través de una cadena, debe usar iterrows en su lugar. Noctiphobia 24/08/18 a las 10:34
  • 3
    Me he topado con esta pregunta porque, aunque sabía que hay división-aplicación-combinación, todavía necesitaba iterar sobre un DataFrame (como dice la pregunta). No todo el mundo tiene el lujo de mejorar con numbay 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 decir "eso es lo correcto" nunca fue mi intención. He mejorado la respuesta. viddik13 30 de mayo de 2019 a las 12:32
226

Deberías usar df.iterrows(). Aunque la iteración fila por fila no es especialmente eficiente, ya que los Seriesobjetos deben crearse.

3
  • 13
    ¿Es esto más rápido que convertir el DataFrame en una matriz numérica (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 7/10/12 a las 12:26
  • 12
    @vgoklani Si iterar fila por fila es ineficiente y tiene una matriz numpy que no es un objeto, entonces 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 necesarioPhillip Cloud 15 de junio de 2013 a las 21:06
  • 8
    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/2142098Richard Wong 16/12/15 a las 11:41
170

Si bien 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
9
  • 8
    Gran parte de la diferencia de tiempo en sus dos ejemplos parece deberse al hecho de que parece que está utilizando la indexación basada en etiquetas para el comando .iterrows () y la indexación basada en números enteros para el comando .itertuples (). Alex 20 de septiembre de 2015 a las 17:00
  • 3
    Para un marco de datos basado en datos financieros (marca de tiempo y 4x flotante), itertuples es 19,57 veces más rápido que en mi máquina. Solo for a,b,c in izip(df["a"],df["b"],df["c"]:es casi igualmente rápido. harbun 19/10/15 a las 13:03
  • 9
    ¿Puedes explicar por qué es más rápido? Abe Miessler 10 de ene. De 2017 a las 22:05
  • 6
    @AbeMiessler agrupa iterrows()cada fila de datos en una serie, mientras itertuples()que no lo hace. miradulo 13 feb 2017 a las 17:30
  • 5
    Tenga en cuenta que el orden de las columnas es en realidad indeterminado, porque dfse crea a partir de un diccionario, por lo que row[1]podría hacer referencia a cualquiera de las columnas. Sin embargo, resulta que los tiempos son aproximadamente los mismos para las columnas enteras que para las columnas flotantes. Brian Burns 5/11/2017 a las 17:29
112

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

docs: 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)
7
  • ¿El df ['precio'] 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 indica TypeError: ("Los objetos 'Series' son mutables, por lo que no se pueden aplicar hash", u'occurred en el índice 0 ')SRS 1 de julio de 2015 a las 17:55
  • Código: df ['Workclass'] = df.apply (fila lambda: dic_update (fila), eje = 1) fin de línea id = 0 fin de línea def dic_update (fila): si la fila no está en dic: dic [fila] = id id = id + 1SRS 1 de julio de 2015 a las 17:57
  • 2
    Tener el eje predeterminado en 0 es lo peorzthomas.nc 29 de noviembre de 2017 a las 23:58
  • 9
    Tenga en cuenta que applyno se "itera" sobre las filas, sino que aplica una función por filas. El código anterior no funcionaría si realmente haces iteraciones necesidad y Indices de, por ejemplo, al comparar los valores a través de diferentes filas (en ese caso se puede hacer nada más que la iteración). gented 4/04/18 a las 13:44
  • 1
    esta es la respuesta apropiada para pandasdhruvm 25 jul.20 a las 20:14
109

Puede utilizar la función df.iloc de la siguiente manera:

for i in range(0, len(df)):
    print df.iloc[i]['c1'], df.iloc[i]['c2']
5
  • 1
    Sé que uno debería evitar esto a favor de iterrows o itertuples, pero sería interesante saber por qué. ¿Alguna idea? rocarvaj 5 oct 2017 a las 14:50
  • 15
    Esta es la única técnica válida que conozco si desea conservar los tipos de datos y también hacer referencia a las columnas por su nombre. itertuplesconserva los tipos de datos, pero elimina cualquier nombre que no le guste. iterrowshace lo contrario. Ken Williams 18/01/18 a las 19:22
  • 6
    Pasé horas tratando de atravesar la idiosincrasia de las estructuras de datos de los pandas para hacer algo simple Y expresivo. Esto da como resultado un código legible. Sean Anderson 19 de septiembre de 2018 a las 12:13
  • Si bien for i in range(df.shape[0])puede acelerar un poco este enfoque, sigue siendo aproximadamente 3,5 veces más lento que el enfoque iterrows () anterior para mi aplicación. Kim Miller 14/12/18 a las 18:18
  • 1
    En Datafrmes grandes, esto parece mejor, ya que se my_iter = df.itertuples()necesita el doble de memoria y mucho tiempo para copiarlo. lo mismo para iterrows(). Bastiaan 3 de ene. De 2019 a las 22:07
56

Cómo iterar de manera eficiente

Si realmente tiene que iterar un marco de datos de Pandas, probablemente querrá evitar usar iterrows () . Existen diferentes métodos y el habitual 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 usarlo itertuples()incluso si su marco de datos tiene columnas extrañas usando el último ejemplo. Ver punto (4)
  • Solo use iterrows()si no puede las soluciones anteriores. Ver punto (1)

Diferentes métodos para iterar sobre filas en un marco de datos 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 muy 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 a 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 itertuples()uso predeterminado de name = None es incluso más rápido, pero no es realmente conveniente, ya que debe 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 named itertuples()es más lento que el punto anterior, pero no es necesario 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))

Producción:

         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

0
45

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)
1
22

Puede escribir su propio iterador que implemente 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 haciendo que todas las columnas estén disponibles y subconjuntando 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);

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

1
  • 4
    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. interrowsno se compara. James L. 1 dic 2017 a las 16:06
20

Para recorrer todas las filas en un dataframepuedes usar:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]
3
  • 1
    Esto es indexación encadenada. No recomiendo hacer esto. cs95 18/04/19 a las 23:20
  • @ cs95 ¿Qué recomendarías en su lugar? Pedro Lobito 19/04/19 a las 1:42
  • Si desea que esto funcione, llame a df.columns.get_loc para obtener la posición del índice entero de la columna de fecha (fuera del ciclo), luego use una única llamada de indexación iloc dentro. cs95 19/04/19 a las 1:57
20
 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]
3
  • 1
    ¿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 10 de septiembre de 2018 a las 12:41
  • Honestamente, no sé exactamente, creo que en comparación con la mejor respuesta, el tiempo transcurrido será aproximadamente el mismo, porque ambos casos usan "para" -construcción. Pero la memoria puede ser diferente en algunos casos. Grag2015 25 oct 2018 a las 13:52
  • 4
    Esto es indexación encadenada. ¡No utilices esto! cs95 18/04/19 a las 23:19
14

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}
12

En breve

  • Utilice la vectorización si es posible
  • Si una operación no se puede vectorizar, use listas por 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.
  • Si todavía es demasiado lento, pruebe una rutina de Cython

Punto de referencia

Benchmark de iteración sobre filas en un Pandas DataFrame

10

Para bucle todas las filas de una dataframey de uso de los valores de cada fila convenientemente , namedtuplespueden ser convertidos 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, se añade el índice como el primer elemento de la tupla , que puede ser indeseable para algunas aplicaciones.

10

Hay una forma de iterar filas de lanzamiento 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 DataFrame:

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.

1
  • Esto fue muy útil para obtener la enésima fila más grande en un marco de datos después de ordenar. ¡Gracias! Jason Harrison 3 de diciembre de 2019 a las 5:23
10

Tanto para ver como para modificar valores, usaría iterrows(). En un bucle for y al usar el desempaquetado de tuplas (ver el ejemplo :) i, row, utilizo rowsolo para ver el valor y lo uso icon el locmétodo cuando quiero modificar valores. Como se indicó en las 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í, rowen el bucle es una copia de esa fila, y no una vista de ella. Por lo tanto, NO debe escribir algo como row['A'] = 'New_Value', no modificará el DataFrame. Sin embargo, puede utilizar iy loce indique la trama de datos para hacer el trabajo.

10

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


cs95 muestra que la vectorización de Pandas supera con creces a otros métodos de Pandas para calcular 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 convertirlo nuevamente 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í

1
  • ¿Cómo trazaste esto? wwnde el dia de ayer
7

Hay tantas formas de iterar sobre las filas en el marco de datos de Pandas. Una forma muy sencilla 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]])
5

La forma más fácil, usa la applyfunción

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

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

También puede hacer indexación NumPy para acelerar aún más. Realmente no es iterativo, 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 encontré problemas y necesitaba transmitir

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

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])
2

Algunas bibliotecas (por ejemplo, una biblioteca de interoperabilidad de Java que yo 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.

2

Como muchas respuestas aquí señalan de manera correcta y clara, generalmente no debe intentar hacer un bucle en Pandas, sino que debe escribir código vectorizado. Pero la pregunta sigue siendo si alguna vez debería escribir bucles en Pandas y, de ser así, cuál es la mejor manera de hacerlo en esas situaciones.

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

Intentaré mostrar esto con un ejemplo. Suponga que desea tomar una suma acumulativa de una columna, pero restablecerla siempre 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ía 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 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ápido que el código vectorizado. En algunas pruebas rápidas con 100.000 filas, lo anterior es de aproximadamente 10 veces más rápido que el GroupBy enfoque. 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 enfoque groupby .

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

1

Junto con las grandes respuestas en esta publicación, voy a proponer el enfoque Divide y Conquista , no estoy escribiendo esta respuesta para abolir las otras grandes respuestas, sino para cumplirlas con otro enfoque que me estaba funcionando de manera eficiente. Tiene dos pasos de splittingy mergingel marco de datos de pandas:

PROS de Divide and Conquer:

  • No necesita usar vectorización ni ningún otro método para convertir el tipo de su marco de datos en otro tipo
  • No es necesario que 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
  • Dependiendo de su elección de corte index, podrá acelerar exponencialmente la iteración. Cuanto más alto index, más rápido será el proceso de iteración.

CONTRAS de Divide y vencerás:

  • No debería tener dependencia sobre el proceso de iteración del mismo marco de datos y un segmento diferente . Lo que significa que si desea leer o escribir desde otro segmento , tal vez sea difícil hacerlo.

=================== Enfoque de dividir y conquistar =================

Paso 1: dividir / rebanar

En este paso, vamos a dividir la iteración en todo el marco de datos. Piense que va a leer un archivo csv en pandas df y luego iterar sobre él. En tal caso, tengo 5.000.000 de registros y los voy a dividir en 100.000 registros.

NOTA: Necesito reiterar que otro análisis de tiempo de ejecución explicado en las otras soluciones de esta página, "número de registros" tiene una proporción exponencial de "tiempo de ejecución" en la búsqueda en el df. Según el punto de referencia de 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: Fusionar

Este será un paso fácil, simplemente combine todos los archivos csv escritos en un marco de datos y escríbalos 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:

Manera eficiente de iteración sobre datafreame

Concatenar archivos csv en un marco de datos de Pandas

1

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

0

Utilice df.iloc[]. Por ejemplo, usando el marco de datos 'rows_df':

Ingrese la descripción de la imagen aquí

O

Para obtener valores de una fila específica, puede convertir el marco de datos en ndarray.

Luego seleccione los valores de fila y columna como este:

Ingrese la descripción de la imagen aquí

1
  • 4
    Considere no publicar código en imágenes, sino como texto en un bloque de código. Scratte 7 de marzo a las 9:31
0

Solo agrego mis dos centavos

Como dice la respuesta aceptada, la forma más rápida de aplicar una función sobre filas es usar una función vectorizada , las llamadas numpy ufuncs(funciones universales)

Pero, ¿qué debe hacer cuando la función que desea aplicar no está ya implementada numpy?

Bueno, usando el vectorizedecorador 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í: https://numba.pydata.org/numba-doc/latest/user/vectorize.html

-1

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:

  • la documentación recomienda explícitamente utilizar .to_numpy()en su lugar
  • la matriz NumPy producida tendrá un tipo d que se ajusta a todas las columnas, en el peor de los casos object
  • hay buenas razones para 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 problema (uno debería pensar) trivial.