Jak iterować po wierszach DataFrame w Pandach?

1943

Mam DataFramez pand:

import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df

Wynik:

   c1   c2
0  10  100
1  11  110
2  12  120

Teraz chcę iterować po wierszach tej ramki. Dla każdego wiersza chcę mieć dostęp do jego elementów (wartości w komórkach) według nazw kolumn. Na przykład:

for row in df.rows:
   print row['c1'], row['c2']

Czy można to zrobić w pandach?

Znalazłem podobne pytanie . Ale nie daje mi odpowiedzi, której potrzebuję. Na przykład zaleca się użycie:

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

lub

for row in df.iterrows():

Ale nie rozumiem, czym rowjest przedmiot i jak mogę z nim pracować.

rzymski
źródło
11
Funkcja df.iteritems () dokonuje iteracji po kolumnach, a nie wierszach. Tak więc, aby iterować po wierszach, musisz transponować („T”), co oznacza, że ​​zamieniasz wiersze i kolumny na siebie (odbijaj ponad przekątną). W rezultacie skutecznie iterujesz oryginalną ramkę danych w jej wierszach, gdy używasz df.T.iteritems ()
Stefan Gruenwald,
11
Jeśli jesteś nowy w tym wątku i dopiero zaczynasz przygodę z pandami, NIE PRZERWAJ !! Iteracja nad ramkami danych jest anty-wzorcem i jest czymś, czego nie powinieneś robić, chyba że chcesz przyzwyczaić się do długiego oczekiwania. W zależności od tego, co próbujesz zrobić, być może są o wiele lepsze alternatywy . iter*funkcje powinny być używane w bardzo rzadkich okolicznościach. Również powiązane .
cs95
18
W przeciwieństwie do tego, co mówi cs95, istnieją doskonałe powody, aby chcieć iterować ramkę danych, więc nowi użytkownicy nie powinni się zniechęcać. Przykładem może być wykonanie kodu przy użyciu wartości każdego wiersza jako danych wejściowych. Ponadto, jeśli twoja ramka danych jest względnie mała (np. Mniej niż 1000 elementów), wydajność nie jest tak naprawdę problemem.
oulenz
1
@oulenz: Jeśli z jakiegoś dziwnego powodu chcesz latać w obliczu używania interfejsu API w celu, do którego został zaprojektowany (wysokowydajne transformacje danych), bądź moim gościem. Ale przynajmniej nie używaj iterrows, istnieją lepsze sposoby na iterację w DataFrame, możesz równie dobrze po prostu iterować listę list w tym momencie. Jeśli jesteś w punkcie, w którym nie robisz nic poza iteracją DataFrame, tak naprawdę nie ma żadnej korzyści z używania DataFrame (zakładając, że iteracja nad nim jest jedyną rzeczą, którą z tym robisz). Tylko mój 2c.
cs95,
7
I drugi @oulenz. O ile mogę stwierdzić, pandasto wybór odczytu pliku csv, nawet jeśli zestaw danych jest mały. Po prostu łatwiej jest programować manipulowanie danymi za pomocą interfejsów API
Chris,

Odpowiedzi:

2628

DataFrame.iterrows to generator generujący zarówno indeks, jak i wiersz

import pandas as pd
import numpy as np

df = pd.DataFrame([{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}])

for index, row in df.iterrows():
    print(row['c1'], row['c2'])

Output: 
   10 100
   11 110
   12 120
waitingkuo
źródło
206
Uwaga: „Ponieważ iterrows zwraca serię dla każdego wiersza, nie zachowuje typów w wierszach”. Ponadto „Nigdy nie powinieneś modyfikować czegoś, nad czym się iteruje”. Według pand 0.19.1 docs
viddik13
3
@ viddik13 to świetna uwaga dzięki. Z tego powodu natknąłem się na przypadek, w którym wartości liczbowe, takie jak 431341610650read read as 4.31E+11. Czy istnieje sposób na zachowanie dtypów?
Aziz Alto,
26
@AzizAlto używać itertuples, jak wyjaśniono poniżej. Zobacz także pandas.pydata.org/pandas-docs/stable/generated/…
Axel
100
Nie używaj iteracji. Itertuples jest szybszy i zachowuje typ danych. Więcej informacji
James L.
11
Z dokumentacji : „Iterowanie po obiektach pand jest ogólnie powolne. W wielu przypadkach ręczne iterowanie po wierszach nie jest potrzebne [...]”. Twoja odpowiedź jest poprawna (w kontekście pytania), ale nigdzie o niej nie wspomina, więc nie jest zbyt dobra.
cs95
451

Jak iterować po wierszach DataFrame w Pandach?

Odpowiedź: NIE * !

Iteracja w pandach jest anty-wzorem i jest czymś, co powinieneś zrobić tylko wtedy, gdy wyczerpiesz każdą inną opcję. Nie powinieneś używać żadnej funkcji z „ iter” w jej nazwie dla więcej niż kilku tysięcy wierszy, w przeciwnym razie będziesz musiał przyzwyczaić się do długiego oczekiwania.

Czy chcesz wydrukować ramkę danych? Zastosowanie DataFrame.to_string().

Chcesz coś obliczyć? W takim przypadku wyszukaj metody w tej kolejności (lista zmodyfikowana tutaj ):

  1. Wektoryzacja
  2. Procedury cytonowe
  3. Zrozumienie listy ( forpętla waniliowa )
  4. DataFrame.apply(): i) Redukcje, które można wykonać w cytonie, ii) Iteracja w przestrzeni pytona
  5. DataFrame.itertuples() i iteritems()
  6. DataFrame.iterrows()

iterrowsi itertuples(oba otrzymujące wiele głosów w odpowiedziach na to pytanie) powinny być używane w bardzo rzadkich okolicznościach, takich jak generowanie obiektów wierszy / imion do przetwarzania sekwencyjnego, co jest naprawdę jedyną rzeczą, do której te funkcje są przydatne.

Apel do organu
Na stronie z dokumentami na iteracji znajduje się ogromne czerwone pole ostrzegawcze z napisem:

Iteracja po obiektach pand jest zwykle powolna. W wielu przypadkach ręczne iterowanie po wierszach nie jest potrzebne [...].

* To jest trochę bardziej skomplikowane niż „nie”. df.iterrows()jest poprawną odpowiedzią na to pytanie, ale „wektoryzacja operacji” jest lepsza. Przyznaję, że istnieją okoliczności, w których nie można uniknąć iteracji (na przykład niektóre operacje, w których wynik zależy od wartości obliczonej dla poprzedniego wiersza). Jednak potrzeba pewnej znajomości biblioteki, aby wiedzieć, kiedy. Jeśli nie masz pewności, czy potrzebujesz rozwiązania iteracyjnego, prawdopodobnie nie. PS: Aby dowiedzieć się więcej o moim uzasadnieniu napisania tej odpowiedzi, przejdź na sam dół.


Szybszy niż zapętlenie : wektoryzacja , cython

Wiele podstawowych operacji i obliczeń jest „wektoryzowanych” przez pandy (za pomocą NumPy lub funkcji cytonizowanych). Obejmuje to operacje arytmetyczne, porównania, (większość) redukcji, przekształcanie (takie jak przestawianie), łączenia i operacje grupowania. Przejrzyj dokumentację Essential Basic Functionality, aby znaleźć odpowiednią metodę wektorową dla twojego problemu.

Jeśli nie istnieje, możesz napisać własny przy użyciu niestandardowych rozszerzeń cytonów .


Następna najlepsza rzecz: lista zrozumień *

Wyjaśnienia list powinny być Twoim następnym portem wywoławczym, jeśli 1) nie ma dostępnego rozwiązania wektorowego, 2) wydajność jest ważna, ale niewystarczająco ważna, aby przejść przez kłopot związany z cytonizacją kodu, i 3) próbujesz przeprowadzić transformację elementarną na twoim kodzie. Istnieje wiele dowodów na to, że zrozumienie listy jest wystarczająco szybkie (a czasem nawet szybsze) do wielu typowych zadań pand.

Formuła jest prosta,

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

Jeśli umiesz zawrzeć logikę biznesową w funkcji, możesz skorzystać ze spisu, który ją wywołuje. Możesz sprawić, by dowolnie złożone rzeczy działały dzięki prostocie i szybkości surowego pytona.

Wyjaśnienia dotyczące
listy zastrzeżeń zakładają, że dane są łatwe w obsłudze - oznacza to, że typy danych są spójne i nie masz NaN, ale nie zawsze można to zagwarantować.

  1. Pierwsza jest bardziej oczywista, ale w przypadku NaN preferuj wbudowane metody pand, jeśli istnieją (ponieważ mają znacznie lepszą logikę obsługi przypadków narożnych) lub upewnij się, że logika biznesowa zawiera odpowiednią logikę obsługi NaN.
  2. W przypadku mieszanych typów danych należy iterować zip(df['A'], df['B'], ...)zamiast, df[['A', 'B']].to_numpy()ponieważ ten drugi domyślnie upcastuje dane do najbardziej powszechnego typu. Na przykład, jeśli A jest liczbą, a B jest ciągiem, to_numpy()rzutuje całą tablicę na ciąg, co może nie być tym, czego chcesz. Na szczęście zippingowanie kolumn razem jest najprostszym obejściem tego problemu.

* YMMV z powodów przedstawionych w zastrzeżeń sekcji powyżej.


Oczywisty przykład

Pokażmy różnicę na prostym przykładzie dodania dwóch kolumn pand A + B. Jest to operatyzowalna wektoryzacja, więc łatwo będzie skontrolować wydajność metod omówionych powyżej.

wprowadź opis zdjęcia tutaj

Kod porównawczy, w celach informacyjnych.

Powinienem jednak wspomnieć, że nie zawsze jest to tak cięte i suche. Czasami odpowiedź na „najlepszą metodę operacji” brzmi „zależy to od twoich danych”. Radzę przetestować różne podejścia do swoich danych, zanim zdecydujesz się na jedno.


Dalsza lektura

* Metody łańcuchowe pand są „wektoryzowane” w tym sensie, że są określone w serii, ale działają na każdym elemencie. Mechanizmy leżące u ich podstaw są wciąż iteracyjne, ponieważ operacje łańcuchowe są z natury trudne do wektoryzacji.


Dlaczego napisałem tę odpowiedź

Częstym trendem, który zauważam od nowych użytkowników, jest zadawanie pytań w formie „w jaki sposób mogę iterować nad moim df, aby zrobić X?”. Pokazuje kod, który wywołuje iterrows()podczas wykonywania czegoś w pętli for. Oto dlaczego. Nowy użytkownik biblioteki, który nie został zaznajomiony z koncepcją wektoryzacji, prawdopodobnie wyobrazi sobie kod, który rozwiązuje ich problem podczas iteracji danych, aby coś zrobić. Nie wiedząc, jak wykonać iterację w DataFrame, pierwszą rzeczą, którą robią, jest Google i kończy się tutaj, na to pytanie. Następnie widzą zaakceptowaną odpowiedź, mówiąc im, jak to zrobić, i zamykają oczy i uruchamiają ten kod, nie pytając nigdy, czy iteracja nie jest właściwa.

Ta odpowiedź ma na celu pomóc nowym użytkownikom zrozumieć, że iteracja niekoniecznie jest rozwiązaniem każdego problemu oraz że mogą istnieć lepsze, szybsze i bardziej idiomatyczne rozwiązania oraz że warto poświęcić czas na ich zbadanie. Nie próbuję rozpocząć wojny iteracji z wektoryzacją, ale chcę, aby nowi użytkownicy byli informowani podczas opracowywania rozwiązań ich problemów z tą biblioteką.

cs95
źródło
22
To jedyna odpowiedź, która koncentruje się na technikach idiomatycznych, których należy używać w przypadku pand, co czyni ją najlepszą odpowiedzią na to pytanie. Uczenie się, jak uzyskać właściwą odpowiedź z właściwym kodem (zamiast właściwej odpowiedzi z niewłaściwym kodem - tj. Nieefektywne, nie skaluje się, nie jest zbyt dopasowane do określonych danych) jest dużą częścią uczenia się pand (i danych w ogóle).
LinkBerest
3
Myślę, że jesteś niesprawiedliwy w stosunku do pętli for, ponieważ są one tylko trochę wolniejsze niż zrozumienie listy w moich testach. Sztuką jest zapętlenie zip(df['A'], df['B'])zamiast df.iterrows().
Imperishable Night
2
@ImperishableNight W ogóle nie; celem tego postu nie jest ogólne potępienie iteracji - to konkretnie potępienie użycia iterrows()iteracji, jeśli i kiedy istnieją lepsze alternatywy. forsame pętle są w porządku, ale zrozumienie listy jest lepsze, jeśli wykonujesz iteracyjnie transformacje elementarne.
cs95
1
@sdbbs tam jest, użyj sort_values ​​do posortowania danych, a następnie wywołaj to_string () na wyniku.
cs95,
1
W części Zrozumienia listy przykład „iteracja po wielu kolumnach” wymaga ostrzeżenia: DataFrame.valuesprzekonwertuje każdą kolumnę na wspólny typ danych. DataFrame.to_numpy()robi to też. Na szczęście możemy używać zipdowolnej liczby kolumn.
David Wasserman
396

Najpierw zastanów się, czy naprawdę musisz iterować wiersze w ramce danych. Zobacz tę odpowiedź, aby znaleźć alternatywy.

Jeśli nadal musisz wykonywać iterację po wierszach, możesz użyć poniższych metod. Zwróć uwagę na kilka ważnych zastrzeżeń, które nie zostały wymienione w żadnej z pozostałych odpowiedzi.

itertuples() ma być szybszy niż iterrows()

Ale uważaj, zgodnie z dokumentami (w tej chwili pandy 0.24.2):

  • iterrows: dtypemoże nie pasować między wierszami

    Ponieważ iterrows zwraca serię dla każdego wiersza, nie zachowuje dtypów w wierszach (dtypy są zachowywane w kolumnach dla DataFrames). Aby zachować dtypy podczas iteracji po wierszach, lepiej jest użyć itertuples (), która zwraca nazwane wartości i która jest generalnie znacznie szybsza niż iterrows ()

  • iterrows: Nie modyfikuj wierszy

    Nigdy nie powinieneś modyfikować czegoś, nad czym się iteruje. Nie gwarantuje się, że zadziała we wszystkich przypadkach. W zależności od typów danych iterator zwraca kopię, a nie widok, a zapis do niej nie przyniesie żadnego efektu.

    Zamiast tego użyj DataFrame.apply () :

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

    Nazwy kolumn zostaną przemianowane na nazwy pozycyjne, jeśli są niepoprawnymi identyfikatorami Pythona, powtórzone lub zaczynają się od podkreślenia. Przy dużej liczbie kolumn (> 255) zwracane są regularne krotki.

Zobacz dokumentację pandy na temat iteracji, aby uzyskać więcej informacji.

viddik13
źródło
4
Tylko małe pytanie od osoby czytającej ten wątek tak długo po jego zakończeniu: jak df.apply () wypada pod względem wydajności w itertu?
Raul Guarini
4
Uwaga: możesz również powiedzieć coś takiego, for row in df[['c1','c2']].itertuples(index=True, name=None):aby uwzględnić tylko niektóre kolumny w iteratorze wierszy.
Brian Burns
12
Zamiast tego getattr(row, "c1")możesz użyć just row.c1.
viraptor,
1
Jestem około 90% pewien, że jeśli użyjesz getattr(row, "c1")zamiast tego row.c1, stracisz jakąkolwiek przewagę wydajności itertuples, a jeśli faktycznie potrzebujesz dostać się do nieruchomości za pośrednictwem ciągu, powinieneś użyć iterrows.
Noctiphobia
3
Natknąłem się na to pytanie, ponieważ chociaż wiedziałem, że istnieje split- Apply -Combine, nadal naprawdę musiałem iterować DataFrame (jak mówi pytanie). Nie każdy ma do poprawienia luksus numbai cython(te same dokumenty mówią, że „zawsze najpierw warto zoptymalizować w Pythonie”). Napisałem tę odpowiedź, aby pomóc innym uniknąć (czasem frustrujących) problemów, ponieważ żadna z pozostałych odpowiedzi nie wspomina o tych zastrzeżeniach. Wprowadzanie nikogo w błąd lub mówienie „tak należy robić” nigdy nie było moim zamiarem. Poprawiłem odpowiedź.
viddik13
201

Powinieneś użyć df.iterrows(). Chociaż iteracja rząd po rzędzie nie jest szczególnie wydajna, ponieważ Seriesobiekty muszą być tworzone.

Wes McKinney
źródło
12
Czy jest to szybsze niż konwertowanie DataFrame na tablicę numpy (poprzez .values) i bezpośrednie działanie na tablicy? Mam ten sam problem, ale ostatecznie przekonwertowałem się na tablicę numpy, a następnie użyłem cytonu.
vgoklani,
12
@vgoklani Jeśli iteracja wiersz po rzędzie jest nieefektywna i masz nieobiektywną tablicę numpy, to prawie na pewno użycie surowej tablicy numpy będzie szybsze, szczególnie w przypadku tablic z wieloma wierszami. powinieneś unikać iteracji po wierszach, chyba że absolutnie musisz
Phillip Cloud
7
Zrobiłem trochę testów dotyczących zużycia czasu dla df.iterrows (), df.itertuples () i zip (df ['a'], df ['b']) i opublikowałem wynik w odpowiedzi innej pytanie: stackoverflow.com/a/34311080/2142098
Richard Wong
154

Chociaż iterrows()jest to dobra opcja, czasami itertuples()może być znacznie szybsza:

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
e9t
źródło
5
Wydaje się, że znaczna różnica czasu w dwóch przykładach wynika z faktu, że wydaje się, że używasz indeksowania opartego na etykietach dla polecenia .iterrows () i indeksowania opartego na liczbach całkowitych dla polecenia .itertuples ().
Alex
2
W przypadku ramek danych finansowych (znacznik czasu i 4x zmiennoprzecinkowe) iteracje są 19,57 razy szybsze niż na mojej maszynie. Tylko for a,b,c in izip(df["a"],df["b"],df["c"]:prawie równie szybko.
harbun,
7
Czy możesz wyjaśnić, dlaczego jest szybszy?
Abe Miessler,
4
@AbeMiessler umieszcza iterrows()każdy wiersz danych w serii, itertuples()ale nie.
miradulo
3
Zauważ, że kolejność kolumn jest w rzeczywistości nieokreślona, ​​ponieważ dfjest tworzona ze słownika, więc row[1]może odnosić się do dowolnej kolumny. Jak się okazuje, czasy są mniej więcej takie same dla liczb całkowitych i liczb zmiennoprzecinkowych.
Brian Burns
88

Możesz także użyć df.apply()do iteracji po wierszach i uzyskać dostęp do wielu kolumn dla funkcji.

docs: 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)
bezczelny drań
źródło
Czy df [„cena”] odnosi się do nazwy kolumny w ramce danych? Próbuję utworzyć słownik z unikalnymi wartościami z kilku kolumn w pliku csv. Użyłem twojej logiki do stworzenia słownika z unikatowymi kluczami i wartościami i dostałem błąd informujący o TypeError: („Obiekty„ Series ”są zmienne, więc nie można ich haszować”, u 'wywoływane przy indeksie 0')
SRS
Kod: df ['Workclass'] = df.apply (lambda row: dic_update (row), axis = 1) end of line id = 0 end of line def dic_update (row): if row not in dic: dic [row] = id id = id + 1
SRS
Nieważne, mam to. Zmieniono linię wywołania funkcji na df_new = df ['Workclass']. Apply (to samo)
SRS
2
Domyślne ustawienie osi na 0 jest najgorsze
zthomas.nc
9
Zauważ, że applynie „iteruje się” nad wierszami, a raczej stosuje funkcję wierszowo. Powyższy kod nie będzie działać, jeśli naprawdę zrobić iteracji potrzeba i indeksami, na przykład przy porównywaniu wartości w różnych wierszach (w tym przypadku można zrobić nic poza iteracji).
gented
82

Możesz użyć funkcji df.iloc w następujący sposób:

for i in range(0, len(df)):
    print df.iloc[i]['c1'], df.iloc[i]['c2']
PJay
źródło
1
Wiem, że należy tego unikać na korzyść iteracji lub iteracji, ale byłoby interesujące wiedzieć, dlaczego. jakieś pomysły?
rocarvaj
12
Jest to jedyna znana mi technika, jeśli chcesz zachować typy danych, a także odwoływać się do kolumn według nazwy. itertupleszachowuje typy danych, ale pozbywa się nazw, których nie lubi. iterrowsrobi coś przeciwnego.
Ken Williams
6
Spędziłem godziny próbując przedzierać się przez specyfikę struktur danych pand, aby zrobić coś prostego ORAZ wyrazistego. Daje to czytelny kod.
Sean Anderson,
Chociaż for i in range(df.shape[0])może nieco przyspieszyć to podejście, wciąż jest około 3,5 razy wolniejsze niż powyższe podejście iterrows () w mojej aplikacji.
Kim Miller,
W przypadku dużych Datafrmes wydaje się to lepsze, ponieważ my_iter = df.itertuples()potrzeba podwójnej pamięci i dużo czasu na jej skopiowanie. to samo dla iterrows().
Bastiaan
33

Szukałem sposobu iteracji w wierszach ORAZ kolumnach i zakończyłem tutaj tak:

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)
Lucas B.
źródło
18

Możesz napisać własny iterator, który implementuje 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)

Jest to bezpośrednio porównywalne z pd.DataFrame.itertuples. Chcę wykonać to samo zadanie z większą wydajnością.


Dla podanej ramki danych z moją funkcją:

list(myiter(df))

[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]

Lub z pd.DataFrame.itertuples:

list(df.itertuples(index=False))

[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]

Kompleksowy test
Testujemy udostępnianie wszystkich kolumn i ich podział.

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

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

piRSquared
źródło
2
Dla osób, które nie chcą czytać kodu: niebieska linia to intertuplespomarańczowa linia to lista iteratorów przez blok wydajności. interrowsnie jest porównywany.
James L.
18

Jak skutecznie iterować?

Jeśli naprawdę musisz iterować ramkę danych pand, prawdopodobnie będziesz chciał uniknąć używania iterrows () . Istnieją różne metody i zwykle iterrows()daleki jest od bycia najlepszym. itertuples () może być 100 razy szybszy.

W skrócie:

  • Zasadniczo używaj df.itertuples(name=None). W szczególności, gdy masz stałą liczbę kolumn i mniej niż 255 kolumn. Patrz punkt (3)
  • W przeciwnym razie użyj, df.itertuples()chyba że kolumny zawierają znaki specjalne, takie jak spacje lub „-”. Punkt 2
  • Można użyć, itertuples()nawet jeśli twoja ramka danych ma dziwne kolumny, używając ostatniego przykładu. Patrz punkt (4)
  • Używaj tylko iterrows()wtedy, gdy nie możesz poprzednich rozwiązań. Zobacz punkt (1)

Różne metody iteracji po wierszach w ramce danych pandy:

Wygeneruj losową ramkę danych z milionem wierszy i 4 kolumnami:

    df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
    print(df)

1) Zwykle iterrows()jest wygodne, ale cholernie wolne:

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) Wartość domyślna itertuples()jest już znacznie szybsza, ale nie działa z nazwami kolumn, takimi jak My Col-Name is very Strange(należy unikać tej metody, jeśli kolumny są powtarzane lub jeśli nazwy kolumny nie można po prostu przekonwertować na nazwę zmiennej 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) Domyślne itertuples()użycie name = None jest jeszcze szybsze, ale nie bardzo wygodne, ponieważ musisz zdefiniować zmienną dla kolumny.

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) Wreszcie, nazwany itertuples()jest wolniejszy niż poprzedni punkt, ale nie musisz definiować zmiennej dla kolumny i działa z nazwami kolumn, takimi jak 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))

Wynik:

         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

Ten artykuł jest bardzo interesującym porównaniem iteracji i itertu

Romain Capron
źródło
14

Aby zapętlić wszystkie wiersze dataframe, możesz użyć:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]
CONvid19
źródło
1
Jest to indeksowanie łańcuchowe. Nie polecam tego robić.
cs95,
@ cs95 Co byś polecił?
CONvid19
Jeśli chcesz, aby to zadziałało, wywołaj df.columns.get_loc, aby uzyskać pozycję indeksu liczb całkowitych w kolumnie daty (poza pętlą), a następnie użyj pojedynczego wywołania indeksowania iloc w środku.
cs95
14
 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]
Grag2015
źródło
1
Jaka jest wydajność tej opcji, gdy jest używana na dużej ramce danych (na przykład miliony wierszy)?
Bazyli Debowski
Szczerze mówiąc, nie wiem dokładnie, myślę, że w porównaniu z najlepszą odpowiedzią, upływ czasu będzie mniej więcej taki sam, ponieważ oba przypadki używają konstrukcji „do”. Ale pamięć może być inna w niektórych przypadkach.
Grag2015,
4
Jest to indeksowanie łańcuchowe. Nie używaj tego!
cs95,
7

Czasami przydatnym wzorem jest:

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

Co skutkuje w:

{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}
Zach
źródło
6

Aby zapętlić wszystkie wiersze dataframeai wygodnie użyć wartości każdego wiersza , można przekonwertować je na s. Na przykład:namedtuplesndarray

df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])

Iterowanie po wierszach:

for row in df.itertuples(index=False, name='Pandas'):
    print np.asarray(row)

prowadzi do:

[ 1.   0.1]
[ 2.   0.2]

Należy pamiętać, że jeśli index=True, indeks jest dodany jako pierwszy element krotki , co może być niekorzystne dla niektórych aplikacji.

Bezpłatny inżynier Herpes
źródło
5

Istnieje sposób na iterację rzutów wierszy podczas uzyskiwania w zamian DataFrame, a nie serii. Nie widzę nikogo wspominającego, że można przekazać indeks jako listę dla wiersza, który ma zostać zwrócony jako DataFrame:

for i in range(len(df)):
    row = df.iloc[[i]]

Zwróć uwagę na użycie podwójnych nawiasów. Zwraca DataFrame z pojedynczym wierszem.

Zeitgeist
źródło
Było to bardzo pomocne w uzyskaniu n-tego największego wiersza w ramce danych po posortowaniu. Dzięki!
Jason Harrison,
3

Użyłbym zarówno do przeglądania, jak i modyfikowania wartości iterrows(). W pętli for i przy użyciu rozpakowywania krotek (patrz przykład i, row:) używam rowtylko do przeglądania wartości i używam iz locmetodą, gdy chcę zmodyfikować wartości. Jak stwierdzono w poprzednich odpowiedziach, tutaj nie powinieneś modyfikować czegoś, nad czym się iterujesz.

for i, row in df.iterrows():
    df_column_A = df.loc[i, 'A']
    if df_column_A == 'Old_Value':
        df_column_A = 'New_value'  

Tutaj rowpętla jest kopią tego wiersza, a nie jego widokiem. Dlatego NIE powinieneś pisać czegoś takiego row['A'] = 'New_Value', nie spowoduje to modyfikacji DataFrame. Możesz jednak użyć ii locokreślić DataFrame do wykonania pracy.

Hossein
źródło
2

Wiem, że spóźniłem się na przyjęcie, ale chciałem tylko dodać powyższą odpowiedź @ cs95, która moim zdaniem powinna być odpowiedzią przyjętą. W swojej odpowiedzi pokazuje, że wektoryzacja pand znacznie przewyższa inne metody obliczania pand przy użyciu ramek danych.

Chciałem dodać, że jeśli najpierw przekonwertujesz ramkę danych na tablicę numpy, a następnie użyjesz wektoryzacji, będzie to nawet szybsze niż wektoryzacja ramki danych pandy (i obejmuje to czas, aby ponownie przekształcić ją w serię ramek danych).

Jeśli dodasz następujące funkcje do kodu testu porównawczego @ cs95, stanie się to dość oczywiste:

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]

wprowadź opis zdjęcia tutaj

spray na owady
źródło
1

Możesz także wykonać numpyindeksowanie, aby uzyskać jeszcze większe przyspieszenia. To nie jest tak naprawdę iteracja, ale działa znacznie lepiej niż iteracja dla niektórych aplikacji.

subset = row['c1'][0:5]
all = row['c1'][:]

Możesz także rzucić go na tablicę. Te indeksy / selekcje powinny już działać jak tablice Numpy, ale napotkałem problemy i musiałem przesyłać

np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) #resize every image in an hdf5 file
James L.
źródło
1

Jest tak wiele sposobów na iterację wierszy w ramce danych pand. Jednym z bardzo prostych i intuicyjnych sposobów jest:

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]])
Shubham Ranjan
źródło
0

W tym przykładzie użyto programu iloc do wyodrębnienia każdej cyfry w ramce danych.

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])
mjr2000
źródło
0

Niektóre biblioteki (np. Używana przeze mnie biblioteka międzyoperacyjna Java) wymagają przekazywania wartości w jednym rzędzie, na przykład w przypadku przesyłania strumieniowego danych. Aby odtworzyć naturę przesyłania strumieniowego, „przesyłam strumieniowo” moje wartości ramek danych jedna po drugiej, napisałem poniżej, co jest przydatne od czasu do czasu.

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

Które mogą być użyte:

for row in DataFrameReader(df):
  print(row.my_column_name)
  print(row.to_dict())
  print(row['my_column_name'])
  print(row.tolist())

I zachowuje mapowanie wartości / nazw dla iterowanych wierszy. Oczywiście jest o wiele wolniejszy niż stosowanie aplikacji i Cython, jak wskazano powyżej, ale w niektórych okolicznościach jest konieczne.

morganika
źródło
0

W skrócie

  • Jeśli to możliwe, użyj wektoryzacji
  • Jeśli operacja nie może być wektoryzowana - użyj wyrażeń listowych
  • Jeśli potrzebujesz jednego obiektu reprezentującego cały wiersz - użyj itertu
  • Jeśli powyższe jest zbyt wolne - spróbuj swifter.apply
  • Jeśli nadal jest zbyt wolny - wypróbuj procedurę Cython

Szczegóły w tym filmie

Reper Benchmark iteracji nad wierszami w pandach DataFrame

artoby
źródło