Actualice un marco de datos en pandas mientras itera fila por fila

214

Tengo un marco de datos de pandas que se ve así (es bastante grande)

           date      exer exp     ifor         mat  
1092  2014-03-17  American   M  528.205  2014-04-19 
1093  2014-03-17  American   M  528.205  2014-04-19 
1094  2014-03-17  American   M  528.205  2014-04-19 
1095  2014-03-17  American   M  528.205  2014-04-19    
1096  2014-03-17  American   M  528.205  2014-05-17 

ahora me gustaría iterar fila por fila y a medida que avanzo por cada fila, el valor de ifor en cada fila puede cambiar dependiendo de algunas condiciones y necesito buscar otro marco de datos.

Ahora, ¿cómo actualizo esto a medida que itero? Intenté algunas cosas, ninguna de ellas funcionó.

for i, row in df.iterrows():
    if <something>:
        row['ifor'] = x
    else:
        row['ifor'] = y

    df.ix[i]['ifor'] = x

Ninguno de estos enfoques parece funcionar. No veo los valores actualizados en el marco de datos.

AMM
fuente
2
Creo que quieres df.ix[i,'ifor']. df.ix[i]['ifor']es problemático porque es una indexación encadenada (que no es confiable en pandas).
Karl D.
1
¿Puede proporcionar el otro marco, así como el <something>. Si su código puede ser vectorizado dependerá de esas cosas. En general, evite iterrows. En su caso, definitivamente debe evitarlo ya que cada fila será un tipo de objectletra Series.
Phillip Cloud
Sería mejor crear una máscara booleana para su condición, actualizar todas esas filas y luego establecer el resto en el otro valor
EdChum
Por favor no use iterrows (). Es un facilitador flagrante del peor antipatrón en la historia de los pandas.
cs95

Respuestas:

232

Puede asignar valores en el bucle usando df.set_value:

for i, row in df.iterrows():
    ifor_val = something
    if <condition>:
        ifor_val = something_else
    df.set_value(i,'ifor',ifor_val)

Si no necesita los valores de la fila, simplemente puede iterar sobre los índices de df, pero mantuve el ciclo for original en caso de que necesite el valor de la fila para algo que no se muestra aquí.

actualizar

df.set_value () ha quedado en desuso desde la versión 0.21.0, puede usar df.at () en su lugar:

for i, row in df.iterrows():
    ifor_val = something
    if <condition>:
        ifor_val = something_else
    df.at[i,'ifor'] = ifor_val
rakke
fuente
66
Ver pandas.pydata.org/pandas-docs/stable/generated/… , segunda viñeta: "2. Nunca debes modificar algo sobre lo que estás iterando"
Davor Josipovic,
32
No estoy seguro si lo leemos exactamente igual. Si miras en mi pseudocódigo, hago la modificación en el marco de datos, no en el valor del iterador. El valor del iterador solo se usa para el índice del valor / objeto. Lo que fallará es row ['ifor'] = some_thing, por las razones mencionadas en la documentación.
rakke
3
Gracias por la aclaración.
Davor Josipovic
8
ahora set_value también se desprecia, y debería usar .at (o .iat), por lo que mi bucle se ve así: para i, fila en df.iterrows (): ifor_val = algo si <condición>: ifor_val = algo_else df.at [ i, 'ifor'] = ifor_val
complexM
2
set_value está en desuso y se eliminará en una versión futura. Utilice los accesos .at [] o .iat [] en su lugar
RoyaumeIX
75

El objeto Pandas DataFrame debe considerarse como una serie de series. En otras palabras, debes pensar en términos de columnas. La razón por la que esto es importante es porque cuando lo usa pd.DataFrame.iterrows, está iterando a través de filas como Series. Pero estas no son las Series que el marco de datos está almacenando y, por lo tanto, son nuevas Series que se crean para usted mientras itera. Eso implica que cuando intentas asignarlos, esas ediciones no terminarán reflejadas en el marco de datos original.

Ok, ahora que eso está fuera del camino: ¿Qué hacemos?

Las sugerencias previas a esta publicación incluyen:

  1. pd.DataFrame.set_valueestá en desuso a partir de la versión 0.21 de Pandas
  2. pd.DataFrame.ixestá en desuso
  3. pd.DataFrame.locestá bien, pero puede funcionar en indexadores de matriz y puede hacerlo mejor

Mi recomendación
Usopd.DataFrame.at

for i in df.index:
    if <something>:
        df.at[i, 'ifor'] = x
    else:
        df.at[i, 'ifor'] = y

Incluso puedes cambiar esto a:

for i in df.index:
    df.at[i, 'ifor'] = x if <something> else y

Respuesta al comentario

¿Y si necesito usar el valor de la fila anterior para la condición if?

for i in range(1, len(df) + 1):
    j = df.columns.get_loc('ifor')
    if <something>:
        df.iat[i - 1, j] = x
    else:
        df.iat[i - 1, j] = y
piRSquared
fuente
¿Y si necesito usar el valor de la fila anterior para la condición if? agregar una columna retrasada al OG df?
Yuca
En cuanto a la eficiencia, ¿su enfoque es mejor frente a agregar una columna retrasada o el efecto es insignificante para pequeños conjuntos de datos? (<10k filas)
Yuca
Eso depende. Me gustaría usar una columna retrasada. Esta respuesta muestra qué hacer si debe realizar un bucle. Pero si no tienes que hacer un bucle, entonces no lo hagas.
piRSquared
Lo tengo, también si es posible tener sus comentarios para stackoverflow.com/q/51753001/9754169, entonces sería increíble: D
Yuca
Agradable para contrastar .at [] con las alternativas más antiguas
Justas
35

Un método que puede usar es itertuples(), itera sobre filas de DataFrame como nombradas tuplas, con el valor de índice como primer elemento de la tupla. Y es mucho más rápido en comparación con iterrows(). Para itertuples(), cada uno rowcontiene su Indexen el DataFrame, y puede utilizar locpara establecer el valor.

for row in df.itertuples():
    if <something>:
        df.at[row.Index, 'ifor'] = x
    else:
        df.at[row.Index, 'ifor'] = x

    df.loc[row.Index, 'ifor'] = x

En la mayoría de los casos, itertuples()es más rápido que iato at.

Gracias @SantiStSupery, el uso .ates mucho más rápido queloc .

GoingMyWay
fuente
3
Como solo apunta a un índice preciso, puede pensar en usar .at en lugar de .loc para mejorar su rendimiento. Vea esta pregunta para obtener más información sobre esto
SantiStSupery
extraño pensar pero df.loc[row.Index, 3] = xno funciona. Por otro lado, df.loc[row.Index, 'ifor'] = xfunciona!
seralouk
19

Debe asignar valor por df.ix[i, 'exp']=Xo en df.loc[i, 'exp']=Xlugar de df.ix[i]['ifor'] = x.

De lo contrario, está trabajando en una vista y debería obtener un calentamiento:

-c:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead

Pero ciertamente, el bucle probablemente debería reemplazarse mejor por algún algoritmo vectorizado para hacer un uso completo DataFramecomo sugirió @Phillip Cloud.

CT Zhu
fuente
10

Bueno, si vas a iterar de todos modos, ¿por qué no usar el método más simple de todos? df['Column'].values[i]

df['Column'] = ''

for i in range(len(df)):
    df['Column'].values[i] = something/update/new_value

O si desea comparar los nuevos valores con los antiguos o algo así, ¿por qué no almacenarlo en una lista y luego agregarlo al final?

mylist, df['Column'] = [], ''

for <condition>:
    mylist.append(something/update/new_value)

df['Column'] = mylist
Pranzell
fuente
7
for i, row in df.iterrows():
    if <something>:
        df.at[i, 'ifor'] = x
    else:
        df.at[i, 'ifor'] = y
Duane
fuente
0

Es mejor usar lambdafunciones usando df.apply()-

df["ifor"] = df.apply(lambda x: {value} if {condition} else x["ifor"], axis=1)
Prachit Patil
fuente
-3

Incremente el número MAX de una columna. Por ejemplo :

df1 = [sort_ID, Column1,Column2]
print(df1)

Mi salida:

Sort_ID Column1 Column2
12         a    e
45         b    f
65         c    g
78         d    h

MAX = df1['Sort_ID'].max() #This returns my Max Number 

Ahora, necesito crear una columna en df2 y llenar los valores de la columna que incrementan el MAX.

Sort_ID Column1 Column2
79      a1       e1
80      b1       f1
81      c1       g1
82      d1       h1

Nota: df2 inicialmente contendrá solo la Columna1 y la Columna2. necesitamos que se cree la columna Sortid e incremental del MAX desde df1.

Shazir Jabbar
fuente