Tengo uno 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, allí se sugiere usar:

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

o

for row in df.iterrows():

Pero no entiendo qué rowes el objeto y cómo puedo trabajar con él.

respuesta

DataFrame.iterrowses 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]})
df = df.reset_index()  # make sure indexes pair with number of rows
for index, row in df.iterrows():
    print(row['c1'], row['c2'])
10 100
11 110
12 120

How to iterate over rows in a DataFrame in 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 esperar mucho .

¿Quieres imprimir un DataFrame? uso DataFrame.to_string()_

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

  1. Vectorización
  2. rutinas Cython
  3. forLista de comprensiones ( bucle de vainilla )
  4. DataFrame.apply(): i) Reducciones que se pueden realizar en Cython, ii) Iteración en el espacio de Python
  5. DataFrame.itertuples()yiteritems()
  6. DataFrame.iterrows()

iterrowsy 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 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 [...].

* 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 Looping: Vectorización , Cython

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. Consulte la documentación sobre la funcionalidad básica esencial para encontrar un método vectorizado adecuado para su problema.

Si no existe ninguno, siéntase libre de escribir el suyo usando extensiones de Cython personalizadas .


Siguiente mejor cosa: Comprensiones de lista *

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. Existe una buena cantidad de evidencia que sugiere 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 en zip(df['A'], df['B'], ...)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, lo que puede no ser lo que desea. Afortunadamente zip, hacer ping 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 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. Apéguese a la API donde pueda (es decir, prefiera vecsobre 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 000 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:

Vectorization (when possible); apply(); List Comprehensions; itertuples()/iteritems(); iterrows(); Cython

Para los más experimentados:

Vectorization (when possible); apply(); List Comprehensions; Cython; 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 con lo malo que applyes 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 que se applyhan 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!


Otras lecturas

* Los métodos de cadena de Pandas están "vectorizados" en el sentido de que se especifican en la serie pero operan en cada elemento. Los mecanismos subyacentes siguen siendo iterativos, porque las operaciones de 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 se hace algo dentro de un forbucle. Aquí está el por qué. Un nuevo usuario de la biblioteca que no ha 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 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.

Primero considere si realmente necesita iterar sobre filas en un DataFrame. Vea esta respuesta para 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 queiterrows()

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

  • iterrows: dtypepodría no coincidir 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.

Use DataFrame.apply() en su lugar:

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

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.

Deberías usar df.iterrows(). Aunque iterar fila por fila no es especialmente eficiente ya Seriesque se deben crear objetos.

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

Puede utilizar la df.ilocfunción de la siguiente manera:

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

También puede usar df.apply()para iterar filas y acceder a varias 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)

Cómo iterar eficientemente

Si realmente tiene que iterar un dataframe de Pandas, probablemente querrá evitar usar iterrows() . Existen diferentes métodos y el habitual iterrows()dista mucho 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, use df.itertuples()excepto si sus columnas tienen caracteres especiales como espacios o '-'. Ver punto (2)
  • Es posible usar 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 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 un nombre de 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 uso predeterminado itertuples()de 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) Por último, el named itertuples()es más lento que el punto anterior, pero no hay 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))

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

Estaba buscando Cómo iterar en filas y columnas y terminé aquí, así que:

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

Puede escribir su propio iterador que implementanamedtuple

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 exhaustiva
Probamos 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);

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Para recorrer todas las filas en un dataframepuede usar:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]
 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]

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()

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}

Actualización : 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 tramas 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í

En breve

  • 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, pruebe con swifter.apply
  • Si todavía es demasiado lento, pruebe una rutina de Cython

Punto de referencia

Benchmark de iteración sobre filas en un Pandas DataFrame

Para hacer un bucle de todas las filas en a dataframey usar los valores de cada fila convenientemente , namedtuplesse 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)

resultados en:

[ 1.   0.1]
[ 2.   0.2]

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

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.

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), uso rowsolo para ver el valor y lo uso icon el locmétodo 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 rowin the loop 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 usar iy locespecificar el marco de datos para hacer el trabajo.

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

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)

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 alguna vez debe 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 necesita calcular alguna función que depende de los valores en otras filas de una manera un tanto 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ápido que el código vectorizado. En algunas pruebas rápidas con 100 000 filas, lo anterior es aproximadamente 10 veces más rápido que el enfoque de grupo . 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 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.

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

df.iterrows()devuelve tuple(a, b)dónde aestá el indexy bes el row.

Descargo de responsabilidad: aunque aquí hay tantas respuestas que recomiendan no usar un enfoque iterativo (bucle) (y en su mayoría estoy de acuerdo), aún lo vería como un enfoque razonable para la siguiente situación:

Ampliar marco de datos con datos de la API

Supongamos que tiene un marco de datos grande que contiene datos de usuario incompletos. Ahora debe ampliar estos datos con columnas adicionales, por ejemplo, el usuario agey gender.

Ambos valores deben obtenerse de una API de back-end. Supongo que la API no proporciona un punto final de "lote" (que aceptaría varias ID de usuario a la vez). De lo contrario, debería llamar a la API solo una vez.

Los costos (tiempo de espera) para la solicitud de red superan con creces la iteración del marco de datos. Estamos hablando de tiempos de ida y vuelta de la red de cientos de milisegundos en comparación con las ganancias insignificantemente pequeñas al usar enfoques alternativos para las iteraciones.

1 solicitud de red costosa para cada fila

Entonces, en este caso, preferiría absolutamente usar un enfoque iterativo. Aunque la solicitud de red es costosa, se garantiza que solo se activará una vez por cada fila en el marco de datos. Aquí hay un ejemplo usando DataFrame.iterrows :

Ejemplo

for index, row in users_df.iterrows():
  user_id = row['user_id']
  # trigger expensive network request once for each row
  response_dict = backend_api.get(f'/api/user-data/{user_id}')
  # extend dataframe with multiple data from response
  users_df.at[index, 'age'] = response_dict.get('age')
  users_df.at[index, 'gender'] = response_dict.get('gender')

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

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.

Junto con las excelentes respuestas en esta publicación, voy a proponer el enfoque Divide y vencerás , 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 splittingy mergingel 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
  • Dependiendo de su elección de corte index, podrá acelerar exponencialmente la iteración. Cuanto mayor indexsea, 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 un segmento diferente . Es decir, si desea leer o escribir desde otro segmento , tal vez sea difícil hacerlo.

=================== 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: Debo reiterar que, como se explica en otros análisis de tiempo de ejecución en las otras soluciones de esta página, el "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

Como dice la respuesta aceptada , la forma más rápida de aplicar una función sobre filas es usar una función vectorizada , la llamada 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 vectorizedecorador de numba, puedes 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

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 usar en su .to_numpy()lugar
  • la matriz NumPy producida tendrá un dtype que se ajuste a todas las columnas, en el peor de los casosobject
  • 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 (uno debería pensar) problema trivial.