Zaktualizuj ramkę danych w pandach podczas iteracji rząd po rzędzie

214

Mam ramkę danych pand, która wygląda tak (jest to dość duża)

           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 

teraz chciałbym iterować wiersz po wierszu, a gdy przechodzę przez każdy wiersz, wartość ifor w każdym wierszu może się zmieniać w zależności od niektórych warunków i muszę wyszukać inną ramkę danych.

Jak mogę to zaktualizować podczas iteracji? Próbowałem kilku rzeczy, ale żadna z nich nie działała.

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

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

Żadne z tych podejść nie działa. Nie widzę wartości zaktualizowanych w ramce danych.

AMM
źródło
2
Myślę, że chcesz df.ix[i,'ifor']. df.ix[i]['ifor']jest problematyczne, ponieważ jest indeksowane łańcuchowo (co nie jest niezawodne w pandach).
Karl D.
1
Czy możesz podać inną ramkę, a także <something>. To, czy kod będzie wektoryzowany, będzie zależeć od tych rzeczy. Ogólnie unikaj iterrows. W twoim przypadku zdecydowanie powinieneś tego unikać, ponieważ każdy wiersz będzie objecttypem Series.
Phillip Cloud
Lepiej byłoby stworzyć maskę logiczną dla swojego stanu, zaktualizować wszystkie te wiersze, a następnie ustawić resztę na inną wartość
EdChum
Nie używaj iterrows (). Jest rażącym narzędziem najgorszego anty-wzoru w historii pand.
cs95

Odpowiedzi:

232

Możesz przypisać wartości w pętli za pomocą 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)

Jeśli nie potrzebujesz wartości wierszy, możesz po prostu iterować po indeksach df, ale zachowałem oryginalną pętlę for na wypadek, gdybyś potrzebował wartości wiersza dla czegoś, czego tutaj nie pokazano.

aktualizacja

Funkcja df.set_value () została uznana za przestarzałą od wersji 0.21.0. Zamiast tego można użyć funkcji df.at ():

for i, row in df.iterrows():
    ifor_val = something
    if <condition>:
        ifor_val = something_else
    df.at[i,'ifor'] = ifor_val
rakke
źródło
6
Patrz pandas.pydata.org/pandas-docs/stable/generated/… , drugi punkt : „2. Nigdy nie powinieneś modyfikować czegoś, nad czym się iteruje”
Davor Josipovic
32
Nie jestem pewien, czy czytamy to dokładnie tak samo. Jeśli spojrzysz na mój pseudo kod, dokonam modyfikacji w ramce danych, a nie w wartości z iteratora. Wartość iteratora jest używana tylko dla indeksu wartości / obiektu. To, co się nie powiedzie, to wiersz ['ifor'] = some_thing, z powodów wymienionych w dokumentacji.
rakke
3
Dziękuję za wyjaśnienie.
Davor Josipovic,
8
teraz set_value jest również zdeplikowany i powinien używać .at (lub .iat), więc moja pętla wygląda następująco: dla i, wiersz w df.iterrows (): ifor_val = coś jeśli <warunek>: ifor_val = coś_else df.at [ i, „ifor”] = ifor_val
complexM
2
wartość set_value jest przestarzała i zostanie usunięta w przyszłej wersji. Zamiast tego należy użyć akcesoriów
.at
75

Obiekt Pandas DataFrame należy traktować jako serię serii. Innymi słowy, powinieneś pomyśleć o tym w kategoriach kolumn. Powodem, dla którego jest to ważne, jest to, że podczas używania pd.DataFrame.iterrowsiterujesz wiersze jako Serie. Ale nie są to Serie, które przechowuje ramka danych, więc są to nowe Serie, które są tworzone dla ciebie podczas iteracji. Oznacza to, że gdy spróbujesz je przypisać, te zmiany nie zostaną odzwierciedlone w oryginalnej ramce danych.

Ok, teraz to już nie przeszkadza: co robimy?

Sugestie poprzedzające ten post obejmują:

  1. pd.DataFrame.set_valuejest przestarzałe od wersji Pandy 0.21
  2. pd.DataFrame.ixjest przestarzałe
  3. pd.DataFrame.locjest w porządku, ale może działać na indeksatorach tablic i możesz zrobić to lepiej

Moja rekomendacja
Użyjpd.DataFrame.at

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

Możesz nawet zmienić to na:

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

Odpowiedź na komentarz

a co jeśli będę musiał użyć wartości z poprzedniego wiersza dla warunku 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
źródło
a co jeśli będę musiał użyć wartości z poprzedniego wiersza dla warunku if? dodać opóźnioną kolumnę do OG df?
Yuca
pod względem wydajności, czy twoje podejście jest lepsze niż dodanie opóźnionej kolumny, czy też efekt jest nieistotny w przypadku małych zestawów danych? (<10k wierszy)
Yuca
To zależy. Wybrałbym użycie opóźnionej kolumny. Ta odpowiedź pokazuje, co zrobić, jeśli musisz wykonać pętlę. Ale jeśli nie musisz zapętlać, nie rób tego.
piRSquared,
Rozumiem, także jeśli możliwe jest uzyskanie opinii na temat stackoverflow.com/q/51753001/9754169 , byłoby wspaniale: D
Yuca
Ładnie kontrastuje .at [] ze starszymi alternatywami
Justas
35

Metodą, której można użyć itertuples(), jest iteracja wierszy DataFrame jako nazwanych świątyń, z wartością indeksu jako pierwszym elementem krotki. I jest o wiele szybszy w porównaniu z iterrows(). Na itertuples()każdy rowzawiera jego Indexw DataFrame, i można użyć loc, aby ustawić wartość.

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

W większości przypadków itertuples()jest szybszy niż iatlub at.

Dzięki @SantiStSupery, korzystanie .atjest znacznie szybsze niżloc .

Idąc moją drogą
źródło
3
Ponieważ wskazujesz tylko precyzyjny indeks, możesz pomyśleć o użyciu .at zamiast .loc w celu poprawy wydajności. Zobacz to pytanie, aby uzyskać więcej informacji na ten temat
SantiStSupery
dziwne myślenie, ale df.loc[row.Index, 3] = xnie działa. Z drugiej strony df.loc[row.Index, 'ifor'] = xdziała!
seralouk
19

Powinieneś przypisać wartość przez df.ix[i, 'exp']=Xlub df.loc[i, 'exp']=Xzamiast df.ix[i]['ifor'] = x.

W przeciwnym razie pracujesz nad widokiem i powinieneś uzyskać ocieplenie:

-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

Ale z pewnością pętla prawdopodobnie powinna zostać zastąpiona algorytmem wektorowym, aby w pełni wykorzystać, DataFramejak sugeruje @Phillip Cloud.

CT Zhu
źródło
10

Cóż, jeśli mimo wszystko zamierzasz iterować, dlaczego nie zastosować najprostszej ze wszystkich metod, df['Column'].values[i]

df['Column'] = ''

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

Lub jeśli chcesz porównać nowe wartości ze starymi lub czymś podobnym, dlaczego nie zapisać go na liście, a następnie dołączyć na końcu.

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

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

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

Lepiej jest używać lambdafunkcji za pomocą df.apply()-

df["ifor"] = df.apply(lambda x: {value} if {condition} else x["ifor"], axis=1)
Prachit Patil
źródło
-3

Zwiększ liczbę MAX z kolumny. Na przykład :

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

Moja produkcja:

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 

Teraz muszę utworzyć kolumnę w df2 i wypełnić wartości kolumny, które zwiększają wartość MAX.

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

Uwaga: df2 początkowo będzie zawierał tylko Kolumnę1 i Kolumnę2. potrzebujemy utworzyć kolumnę Sortid i przyrostową wartość MAX z df1.

Shazir Jabbar
źródło