Aktualisieren Sie einen Datenrahmen in Pandas, während Sie Zeile für Zeile iterieren

213

Ich habe einen Pandas-Datenrahmen, der so aussieht (es ist ein ziemlich großer)

           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 

Jetzt möchte ich Zeile für Zeile iterieren. Während ich durch jede Zeile gehe, kann sich der Wert ifor in jeder Zeile abhängig von bestimmten Bedingungen ändern, und ich muss einen anderen Datenrahmen nachschlagen.

Wie aktualisiere ich dies jetzt, während ich iteriere? Versuchte ein paar Dinge, von denen keiner funktionierte.

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

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

Keiner dieser Ansätze scheint zu funktionieren. Ich sehe die im Datenrahmen aktualisierten Werte nicht.

AMM
quelle
2
Ich denke du willst df.ix[i,'ifor']. df.ix[i]['ifor']ist problematisch, weil es sich um eine verkettete Indizierung handelt (was bei Pandas nicht zuverlässig ist).
Karl D.
1
Können Sie den anderen Rahmen sowie den <something>. Ob Ihr Code vektorisiert werden kann, hängt von diesen Dingen ab. Im Allgemeinen vermeiden iterrows. In Ihrem Fall sollten Sie dies unbedingt vermeiden, da jede Zeile ein objectD-Typ ist Series.
Phillip Cloud
Sie sollten eine boolesche Maske für Ihre Bedingung erstellen, alle diese Zeilen aktualisieren und dann den Rest auf den anderen Wert setzen
EdChum
Bitte verwenden Sie keine iterrows (). Es ist ein eklatanter Wegbereiter für das schlimmste Anti-Muster in der Geschichte der Pandas.
CS95

Antworten:

232

Sie können Werte in der Schleife mit df.set_value zuweisen:

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

Wenn Sie die Zeilenwerte nicht benötigen, können Sie einfach über die Indizes von df iterieren, aber ich habe die ursprüngliche for-Schleife beibehalten, falls Sie den Zeilenwert für etwas benötigen, das hier nicht angezeigt wird.

aktualisieren

df.set_value () ist seit Version 0.21.0 veraltet. Sie können stattdessen df.at () verwenden:

for i, row in df.iterrows():
    ifor_val = something
    if <condition>:
        ifor_val = something_else
    df.at[i,'ifor'] = ifor_val
Rakke
quelle
6
Siehe pandas.pydata.org/pandas-docs/stable/generated/… , zweite Kugel: "2. Sie sollten niemals etwas ändern, über das Sie iterieren"
Davor Josipovic
32
Ich bin mir nicht sicher, ob wir es genau gleich lesen. Wenn Sie in meinen Pseudocode schauen, ändere ich den Datenrahmen und nicht den Wert des Iterators. Der Iteratorwert wird nur für den Index des Werts / Objekts verwendet. Was aus den in der Dokumentation genannten Gründen fehlschlägt, ist row ['ifor'] = some_thing.
Rakke
3
Danke für die Klarstellung.
Davor Josipovic
8
Jetzt ist set_value ebenfalls korrigiert und sollte .at (oder .iat) verwenden, sodass meine Schleife folgendermaßen aussieht: Für i Zeile in df.iterrows (): ifor_val = etwas wenn <Bedingung>: ifor_val = etwas_else df.at [ i, 'ifor'] = ifor_val
complexM
2
set_value ist veraltet und wird in einer zukünftigen Version entfernt. Bitte verwenden Sie stattdessen .at [] oder .iat [] Accessoren
RoyaumeIX
75

Das Pandas DataFrame-Objekt sollte als Serie von Serien betrachtet werden. Mit anderen Worten, Sie sollten es in Spalten betrachten. Der Grund, warum dies wichtig ist, liegt darin, dass Sie bei der Verwendung pd.DataFrame.iterrowsZeilen als Serien durchlaufen. Dies sind jedoch nicht die Serien, die im Datenrahmen gespeichert werden. Es handelt sich also um neue Serien, die für Sie erstellt werden, während Sie iterieren. Dies bedeutet, dass diese Änderungen beim Versuch, sie zuzuweisen, nicht im ursprünglichen Datenrahmen wiedergegeben werden.

Ok, jetzt wo das nicht im Weg ist: Was machen wir?

Vorschläge vor diesem Beitrag umfassen:

  1. pd.DataFrame.set_valueist ab Pandas Version 0.21 veraltet
  2. pd.DataFrame.ixist veraltet
  3. pd.DataFrame.locist in Ordnung, kann aber mit Array-Indexern arbeiten und Sie können es besser machen

Meine Empfehlung
Verwendenpd.DataFrame.at

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

Sie können dies sogar ändern in:

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

Antwort auf Kommentar

und was ist, wenn ich den Wert der vorherigen Zeile für die if-Bedingung verwenden muss?

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
quelle
und was ist, wenn ich den Wert der vorherigen Zeile für die if-Bedingung verwenden muss? Hinzufügen einer verzögerten Spalte zum OG df?
Yuca
In Bezug auf die Effizienz ist Ihr Ansatz besser als das Hinzufügen einer verzögerten Spalte oder ist der Effekt für kleine Datensätze vernachlässigbar? (<10k Reihen)
Yuca
Kommt darauf an. Ich würde eine verzögerte Spalte verwenden. Diese Antwort zeigt, was zu tun ist, wenn Sie eine Schleife durchführen müssen. Aber wenn Sie keine Schleife machen müssen, dann nicht.
piRSquared
Verstanden , auch wenn es möglich ist, Ihr Feedback für stackoverflow.com/q/51753001/9754169 zu haben, dann wäre es fantastisch: D
Yuca
Schön, um .at [] mit den älteren Alternativen zu kontrastieren
Justas
35

Eine Methode, die Sie verwenden können, besteht darin itertuples(), DataFrame-Zeilen als Namedtuples zu durchlaufen, wobei der Indexwert das erste Element des Tupels ist. Und es ist viel viel schneller als iterrows(). Denn itertuples()jedes rowenthält sein Indexim DataFrame, und Sie können locden Wert festlegen.

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

In den meisten Fällen itertuples()ist schneller als iatoder at.

Danke @SantiStSupery, die Verwendung .atist viel schneller alsloc .

Ich gehe meinen Weg
quelle
3
Da Sie nur auf einen genauen Index zeigen, können Sie .at anstelle von .loc verwenden, um Ihre Leistung zu verbessern. Siehe diese Frage für weitere Informationen zu diesem
SantiStSupery
komisch denken, df.loc[row.Index, 3] = xfunktioniert aber nicht. Auf der anderen Seite df.loc[row.Index, 'ifor'] = xfunktioniert!
Seralouk
19

Sie sollten den Wert durch df.ix[i, 'exp']=Xoder df.loc[i, 'exp']=Xanstelle von zuweisen df.ix[i]['ifor'] = x.

Ansonsten arbeiten Sie an einer Ansicht und sollten sich erwärmen:

-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

Aber sicherlich sollte die Schleife wahrscheinlich besser durch einen vektorisierten Algorithmus ersetzt werden, um die volle Nutzung von DataFrame@Phillip Cloud zu nutzen.

CT Zhu
quelle
10

Nun, wenn Sie sowieso iterieren wollen, warum nicht die einfachste Methode von allen verwenden, df['Column'].values[i]

df['Column'] = ''

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

Oder wenn Sie die neuen Werte mit alten oder ähnlichen Werten vergleichen möchten, speichern Sie sie in einer Liste und hängen Sie sie am Ende an.

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

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

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

Es ist besser, lambdaFunktionen mit df.apply()- zu verwenden.

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

Erhöhen Sie die MAX-Nummer aus einer Spalte. Zum Beispiel :

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

Meine Ausgabe:

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 

Jetzt muss ich eine Spalte in df2 erstellen und die Spaltenwerte füllen, die den MAX erhöhen.

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

Hinweis: df2 enthält zunächst nur die Spalten 1 und 2. Wir müssen die Spalte Sortid erstellen und die MAX von df1 inkrementieren.

Shazir Jabbar
quelle