Tengo un DataFrame de Pandas:
import pandas as pd inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}] df = pd.DataFrame(inp) print dfProducción:
c1 c2 0 10 100 1 11 110 2 12 120Ahora 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 utilizar:
for date, row in df.T.iteritems():o
for row in df.iterrows(): Pero no entiendo qué es el objeto de row y cómo puedo trabajar con él.
Tanto para ver como para modificar valores, usaría iterrows() . En un bucle for y usando el desempaquetado de tuplas (ver el ejemplo: i, row ), uso la row solo para ver el valor y uso i con el método 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í, la row en el ciclo 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 i y loc y especificar el DataFrame para hacer el trabajo.
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])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.
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]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 selfQue 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.
Para hacer un bucle de todas las filas en un dataframe de datos y usar los valores de cada fila convenientemente , las namedtuples con nombre se pueden convertir en ndarray s. 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 si index=True , el índice se agrega como el primer elemento de la tupla , lo que puede no ser deseable para algunas aplicaciones.
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}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]])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] } ) # xy 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 15Este 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á aún 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 ciclo 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.
Utilice df.iloc[] . Por ejemplo, usando el marco de datos 'rows_df':
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:
En breve
La forma más fácil, usa la función de apply
def print_row(row): print row['c1'], row['c2'] df.apply(lambda row: print_row(row), axis=1)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 aún no está implementada en NumPy?
Bueno, usando el decorador vectorize de numba , puedes crear fácilmente ufuncs directamente en Python como este:
from numba import vectorize, float64 @vectorize([float64(float64)]) def f(x): #x is your line, do something with it, and return a floatLa documentación para esta función está aquí: Creando funciones universales NumPy
df.iterrows() devuelve tupla(a, b) donde a es el índice y b es la fila.
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.
for index, row in df.iterrows(): print(row["c1"], row["c2"]) for row in df.itertuples(index=True, name='Pandas'): print(row.c1, row.c2) 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):
dtype no coincida de una fila a otraDebido a que iterrows devuelve una serie para cada fila, no conserva los tipos de datos en las filas (los tipos de datos se conservan en las columnas para los marcos de datos). 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()
Nunca debe modificar algo sobre lo que está iterando. No se garantiza que funcione en todos los casos. Dependiendo de los tipos de datos, el iterador devuelve una copia y no una vista, y escribir en ella no tendrá ningún efecto.
Use DataFrame.apply() en su lugar:
new_df = df.apply(lambda x: x * 2)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.
Consulte los documentos de pandas sobre la iteración para obtener más detalles.
Para recorrer todas las filas en un dataframe de datos, puede usar:
for x in range(len(date_example.index)): print date_example['Date'].iloc[x]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)También puede hacer la indexación de NumPy para acelerar aún más. Realmente no es 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 for ind in df.index: print df['c1'][ind], df['c2'][ind]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 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);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 loopTambién puede usar df.apply() para iterar sobre filas y acceder a varias columnas para una función.
def valuation_formula(x, y): return x * y * 0.5 df['price'] = df.apply(lambda row: valuation_formula(row['x'], row['y']), axis=1)DataFrame.iterrows es un generador que genera 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 120Deberías usar df.iterrows() . Aunque iterar fila por fila no es especialmente eficiente, ya que se deben crear objetos de Series .
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:
Supongamos que tiene un marco de datos grande que contiene datos de usuario incompletos. Ahora debe ampliar estos datos con columnas adicionales, por ejemplo, la age y gender del usuario.
Ambos valores deben obtenerse de una API de back-end. 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.
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 :
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')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:
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()
Una mejor manera es convertir el marco de datos en un diccionario usando zip , creando un par de valores clave y luego accediendo 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)Producción:
c1 10 c2 100 c1 11 c2 110 c1 12 c2 120Probablemente 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:
.to_numpy() en su lugarobjectAún así, creo que esta opción debería incluirse aquí, como una solución directa a un (uno debería pensar) problema trivial.