Czym różnią się iloc, ix i loc?

635

Czy ktoś może wyjaśnić, czym różnią się te trzy metody krojenia?
Widziałem dokumenty i widziałem te odpowiedzi , ale wciąż nie jestem w stanie wyjaśnić, jak te trzy są różne. Wydają mi się one w dużej mierze wymienne, ponieważ znajdują się na niższych poziomach krojenia.

Powiedzmy, że chcemy uzyskać pierwsze pięć wierszy DataFrame. Jak to się dzieje, że wszystkie trzy z nich działają?

df.loc[:5]
df.ix[:5]
df.iloc[:5]

Czy ktoś może przedstawić trzy przypadki, w których rozróżnienie w zastosowaniu jest wyraźniejsze?

AZhao
źródło
7
bardzo ważne jest, aby wspomnieć o scenariuszach SettingWithCopyWarning: stackoverflow.com/questions/20625582/... i stackoverflow.com/questions/23688307/...
Paul
9
Zauważ, że ix jest teraz planowany do wycofania: github.com/pandas-dev/pandas/issues/14218
JohnE

Odpowiedzi:

968

Uwaga: w pandach w wersji 0.20.0 i nowszych ixjest przestarzałe, a zamiast niego zalecane jest używanie loci iloc. Zostawiłem części tej odpowiedzi, które opisują ixnietknięte jako odniesienie dla użytkowników wcześniejszych wersji pand. Dodano przykłady pokazujące alternatywy dla ix .


Po pierwsze, oto podsumowanie trzech metod:

  • locpobiera wiersze (lub kolumny) z konkretnymi etykietami z indeksu.
  • ilocpobiera wiersze (lub kolumny) w określonych pozycjach w indeksie (więc przyjmuje tylko liczby całkowite).
  • ixzwykle stara się zachowywać jak, locale wraca do zachowania, jakby ilocetykieta nie była obecna w indeksie.

Ważne jest, aby zwrócić uwagę na niektóre subtelności, które mogą utrudnić ixużycie:

  • jeśli indeks jest liczbą całkowitą, ixużyje tylko indeksowania opartego na etykietach, a nie powróci do indeksowania opartego na pozycji. Jeśli etykiety nie ma w indeksie, pojawia się błąd.

  • jeśli indeks nie zawiera tylko liczb całkowitych, to podana liczba całkowita ixnatychmiast zastosuje indeksowanie oparte na pozycji, a nie indeksowanie oparte na etykietach. Jeśli jednak ixzostanie podany inny typ (np. Ciąg znaków), można zastosować indeksowanie oparte na etykietach.


Aby zilustrować różnice między trzema metodami, rozważ następującą serię:

>>> s = pd.Series(np.nan, index=[49,48,47,46,45, 1, 2, 3, 4, 5])
>>> s
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN
4    NaN
5    NaN

Przyjrzymy się krojeniu na wartość całkowitą 3 .

W tym przypadku s.iloc[:3]zwraca nam pierwsze 3 wiersze (ponieważ traktuje 3 jako pozycję) i s.loc[:3]zwraca pierwsze 8 wierszy (ponieważ traktuje 3 jako etykietę):

>>> s.iloc[:3] # slice the first three rows
49   NaN
48   NaN
47   NaN

>>> s.loc[:3] # slice up to and including label 3
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN

>>> s.ix[:3] # the integer is in the index so s.ix[:3] works like loc
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN

Zawiadomienie s.ix[:3]zwraca tę samą serię, s.loc[:3]ponieważ najpierw szuka etykiety, a nie pracy nad pozycją (a indeks sjest typu całkowitego).

Co jeśli spróbujemy z etykietą całkowitą, której nie ma w indeksie (powiedzmy 6)?

Tu s.iloc[:6]zwraca pierwsze 6 rzędów z serii, jak oczekiwano. Jednak s.loc[:6]podnosi KeyError, ponieważ 6nie ma go w indeksie.

>>> s.iloc[:6]
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN

>>> s.loc[:6]
KeyError: 6

>>> s.ix[:6]
KeyError: 6

Zgodnie z subtelnościami wspomnianymi powyżej, s.ix[:6]teraz podnosi KeyError, ponieważ próbuje działać jak, locale nie może znaleźć 6w indeksie. Ponieważ nasz indeks jest liczbą całkowitą ix, nie zachowuje się podobnieiloc .

Gdyby jednak nasz indeks był typu mieszanego, podana liczba całkowita ixzachowałaby się jak ilocnatychmiast zamiast wywoływania KeyError:

>>> s2 = pd.Series(np.nan, index=['a','b','c','d','e', 1, 2, 3, 4, 5])
>>> s2.index.is_mixed() # index is mix of different types
True
>>> s2.ix[:6] # now behaves like iloc given integer
a   NaN
b   NaN
c   NaN
d   NaN
e   NaN
1   NaN

Należy pamiętać, że ixnadal można akceptować liczby całkowite i zachowywać się tak loc:

>>> s2.ix[:'c'] # behaves like loc given non-integer
a   NaN
b   NaN
c   NaN

Jako ogólna rada, jeśli indeksujesz tylko za pomocą etykiet lub indeksujesz tylko za pomocą liczb całkowitych, trzymaj się loclub ilocunikaj nieoczekiwanych wyników - spróbuj nie używać ix.


Łączenie indeksowania opartego na pozycji i na podstawie etykiet

Czasami biorąc pod uwagę DataFrame, będziesz chciał mieszać metody indeksowania etykiet i pozycji dla wierszy i kolumn.

Weźmy na przykład następujące DataFrame. Jak najlepiej pociąć wiersze do „c” włącznie i wziąć pierwsze cztery kolumny?

>>> df = pd.DataFrame(np.nan, 
                      index=list('abcde'),
                      columns=['x','y','z', 8, 9])
>>> df
    x   y   z   8   9
a NaN NaN NaN NaN NaN
b NaN NaN NaN NaN NaN
c NaN NaN NaN NaN NaN
d NaN NaN NaN NaN NaN
e NaN NaN NaN NaN NaN

We wcześniejszych wersjach pand (wcześniejszych niż 0.20.0) ixpozwala to zrobić dość starannie - możemy pokroić wiersze według etykiety, a kolumny według pozycji (zwróć uwagę, że w przypadku kolumn, ixdomyślnie będzie to oparte na pozycjonowaniu, ponieważ 4nie jest to nazwa kolumny ):

>>> df.ix[:'c', :4]
    x   y   z   8
a NaN NaN NaN NaN
b NaN NaN NaN NaN
c NaN NaN NaN NaN

W późniejszych wersjach pand możemy osiągnąć ten wynik za pomocą iloci za pomocą innej metody:

>>> df.iloc[:df.index.get_loc('c') + 1, :4]
    x   y   z   8
a NaN NaN NaN NaN
b NaN NaN NaN NaN
c NaN NaN NaN NaN

get_loc()to metoda indeksu oznaczająca „uzyskaj pozycję etykiety w tym indeksie”. Zauważ, że od krojenia za pomocąiloc obejmuje punktu końcowego, musimy dodać 1 do tej wartości, jeśli chcemy również wiersz „c”.

Istnieją dalsze przykłady w dokumentacji pand tutaj .

Alex Riley
źródło
12
Świetne wyjaśnienie! Jedno pokrewne pytanie, które zawsze miałem, to jaki związek, jeśli w ogóle, loc, iloc i ix mają z ostrzeżeniami SettingWithCopy? Istnieje dokumentacja, ale szczerze mówiąc nadal jestem trochę zdezorientowany pandas.pydata.org/pandas-docs/stable/...
measureallthethings
3
@measureallthethings: loc, iloci ixnadal może wywołać ostrzeżenie, jeżeli są one połączone w łańcuch. Użycie przykładowego elementu DataFrame w połączonych dokumentach dfmi.loc[:, 'one'].loc[:, 'second']powoduje wyświetlenie ostrzeżenia, tak jak dfmi['one']['second']w przypadku pierwszej operacji indeksowania może zostać zwrócona kopia danych (a nie widok).
Alex Riley,
Czego używasz, jeśli chcesz wyszukać DateIndex z datą lub coś w tym rodzaju df.ix[date, 'Cash']?
cjm2671
@ cjm2671: oba loclub ixpowinny działać w takim przypadku. Na przykład df.loc['2016-04-29', 'Cash']zwróci wszystkie indeksy wierszy z tą konkretną datą z kolumny „Gotówka”. (Możesz być tak '2016-01'dokładny, jak chcesz podczas pobierania indeksów z ciągami, np. Wybierze wszystkie czasy danych przypadające w styczniu 2016 r., `` 2016-01-02 11 '' wybierze czasy danych 2 stycznia 2016 r. Z czasem 11: ??: ?? .)
Alex Riley
Jeśli chcesz w pewnym momencie zaktualizować tę odpowiedź, tutaj znajdziesz sugestie dotyczące użycia loc / iloc zamiast ix github.com/pandas-dev/pandas/issues/14218
JohnE
142

ilocdziała w oparciu o pozycjonowanie liczb całkowitych. Niezależnie od tego, jakie są etykiety wierszy, zawsze możesz np. Uzyskać pierwszy wiersz, wykonując tę ​​czynność

df.iloc[0]

lub ostatnie pięć wierszy wykonując

df.iloc[-5:]

Możesz także użyć go na kolumnach. Spowoduje to pobranie trzeciej kolumny:

df.iloc[:, 2]    # the : in the first position indicates all rows

Możesz je połączyć, aby uzyskać przecięcia wierszy i kolumn:

df.iloc[:3, :3] # The upper-left 3 X 3 entries (assuming df has 3+ rows and columns)

Z drugiej strony .locużyj nazwanych indeksów. Ustawmy ramkę danych z ciągami znaków jako etykietami wierszy i kolumn:

df = pd.DataFrame(index=['a', 'b', 'c'], columns=['time', 'date', 'name'])

Wtedy możemy uzyskać pierwszy wiersz

df.loc['a']     # equivalent to df.iloc[0]

a drugie dwa rzędy 'date'kolumny wg

df.loc['b':, 'date']   # equivalent to df.iloc[1:, 1]

i tak dalej. Warto chyba zaznaczyć, że domyślnymi indeksami wierszy i kolumn dla a DataFramesą liczby całkowite od 0, w tym przypadku iloci locdziałałyby w ten sam sposób. Właśnie dlatego twoje trzy przykłady są równoważne. Jeśli miałbyś indeks nieliczbowy, taki jak ciągi znaków lub czasy danych, df.loc[:5] spowodowałby błąd.

Możesz także pobierać kolumny tylko za pomocą ramki danych __getitem__:

df['time']    # equivalent to df.loc[:, 'time']

Załóżmy teraz, że chcesz mieszać pozycję i indeksowanie nazwane, czyli indeksowanie przy użyciu nazw w wierszach i pozycji w kolumnach (aby to wyjaśnić, mam na myśli wybranie z naszej ramki danych, zamiast tworzenia ramki danych z ciągami znaków w indeksie wierszy i liczbami całkowitymi w indeks kolumny). Oto gdzie .ixprzychodzi:

df.ix[:2, 'time']    # the first two rows of the 'time' column

Myślę, że warto również wspomnieć, że do locmetody można również przekazywać wektory boolowskie . Na przykład:

 b = [True, False, True]
 df.loc[b] 

Zwróci pierwszy i trzeci rząd df. Jest to równoważne z df[b]wyborem, ale może być również użyte do przypisania za pomocą wektorów boolowskich:

df.loc[b, 'name'] = 'Mary', 'John'
JoeCondron
źródło
Czy df.iloc [:,:] jest równoważne wszystkim wierszom i kolumnom?
Alvis
Tak będzie df.loc[:, :]. Można go użyć do ponownego przypisania wartości całości DataFramelub utworzenia jej widoku.
JoeCondron
119

Moim zdaniem zaakceptowana odpowiedź jest myląca, ponieważ używa DataFrame tylko z brakującymi wartościami. Nie podoba mi się również określenie oparte na pozycji .iloci zamiast tego wolę lokalizację całkowitą, ponieważ jest ona bardziej opisowa i dokładnie to, co .ilocoznacza. Kluczowym słowem jest INTEGER - .ilocwymaga INTEGERS.

Zobacz moją bardzo szczegółową serię blogów na temat wyboru podzbiorów, aby uzyskać więcej


.ix jest przestarzałe i niejednoznaczne i nigdy nie należy go używać

Ponieważ .ixjest przestarzałe, skupimy się tylko na różnicach między .loci .iloc.

Zanim omówimy różnice, ważne jest, aby zrozumieć, że DataFrames mają etykiety, które pomagają zidentyfikować każdą kolumnę i każdy indeks. Rzućmy okiem na przykładową ramkę DataFrame:

df = pd.DataFrame({'age':[30, 2, 12, 4, 32, 33, 69],
                   'color':['blue', 'green', 'red', 'white', 'gray', 'black', 'red'],
                   'food':['Steak', 'Lamb', 'Mango', 'Apple', 'Cheese', 'Melon', 'Beans'],
                   'height':[165, 70, 120, 80, 180, 172, 150],
                   'score':[4.6, 8.3, 9.0, 3.3, 1.8, 9.5, 2.2],
                   'state':['NY', 'TX', 'FL', 'AL', 'AK', 'TX', 'TX']
                   },
                  index=['Jane', 'Nick', 'Aaron', 'Penelope', 'Dean', 'Christina', 'Cornelia'])

wprowadź opis zdjęcia tutaj

Wszystkie pogrubione słowa są etykietami. Etykiety, age, color, food, height, scorei statesą wykorzystywane do kolumn . Pozostałe etykiety, Jane, Nick, Aaron, Penelope, Dean, Christina, Corneliasłużą do indeksu .


Podstawowymi sposobami wyboru poszczególnych wierszy w ramce danych są narzędzia indeksujące .loci .iloc. Każdy z tych indeksatorów może być również używany do jednoczesnego wybierania kolumn, ale na razie łatwiej jest skupić się na wierszach. Ponadto każdy z indeksatorów używa zestawu nawiasów, które bezpośrednio po nazwie dokonują wyboru.

.loc wybiera dane tylko według etykiet

Najpierw porozmawiamy o .locindeksatorze, który wybiera dane tylko według etykiet indeksu lub kolumn. W naszym przykładowym DataFrame podaliśmy znaczące nazwy jako wartości dla indeksu. Wiele DataFrames nie będzie miało żadnych znaczących nazw i zamiast tego będzie domyślnie tylko liczbami całkowitymi od 0 do n-1, gdzie n jest długością DataFrame.

Istnieją trzy różne dane wejściowe, których możesz użyć .loc

  • Sznurek
  • Lista ciągów
  • Wycinanie notacji za pomocą łańcuchów jako wartości początkowej i końcowej

Wybór pojedynczego wiersza z .loc z ciągiem znaków

Aby wybrać pojedynczy wiersz danych, umieść etykietę indeksu wewnątrz następujących nawiasów .loc.

df.loc['Penelope']

Zwraca to wiersz danych jako Serię

age           4
color     white
food      Apple
height       80
score       3.3
state        AL
Name: Penelope, dtype: object

Zaznaczanie wielu wierszy za pomocą .loc z listą ciągów

df.loc[['Cornelia', 'Jane', 'Dean']]

Zwraca DataFrame z wierszami w kolejności określonej na liście:

wprowadź opis zdjęcia tutaj

Zaznaczanie wielu wierszy za pomocą .loc z zapisem wycinka

Notacja wycinka jest zdefiniowana przez wartości początkową, zatrzymaną i krokową. Podczas krojenia według etykiety pandy uwzględniają wartość stop na zwrocie. Następujące plastry od Aarona do Deana włącznie. Wielkość kroku nie jest wyraźnie zdefiniowana, ale domyślnie wynosi 1.

df.loc['Aaron':'Dean']

wprowadź opis zdjęcia tutaj

Złożone plastry można pobierać w taki sam sposób, jak listy w języku Python.

.iloc wybiera dane tylko według lokalizacji całkowitej

Przejdźmy teraz do .iloc. Każdy wiersz i kolumna danych w DataFrame ma liczbę całkowitą, która ją definiuje. Jest to dodatek do etykiety wyświetlanej na wydruku . Lokalizacją całkowitą jest po prostu liczba wierszy / kolumn od góry / po lewej, zaczynająca się od 0.

Istnieją trzy różne dane wejściowe, których możesz użyć .iloc

  • Liczba całkowita
  • Lista liczb całkowitych
  • Odcięcie notacji za pomocą liczb całkowitych jako wartości początkowej i końcowej

Wybór pojedynczego wiersza z .iloc z liczbą całkowitą

df.iloc[4]

Zwraca piąty wiersz (liczba całkowita 4) jako szereg

age           32
color       gray
food      Cheese
height       180
score        1.8
state         AK
Name: Dean, dtype: object

Zaznaczanie wielu wierszy za pomocą .iloc z listą liczb całkowitych

df.iloc[[2, -2]]

Zwraca DataFrame trzeciego i drugiego do ostatniego wiersza:

wprowadź opis zdjęcia tutaj

Zaznaczanie wielu wierszy za pomocą .iloc z zapisem plastra

df.iloc[:5:3]

wprowadź opis zdjęcia tutaj


Jednoczesny wybór wierszy i kolumn za pomocą .loc i .iloc

Jedną z doskonałych zdolności obu .loc/.ilocjest możliwość jednoczesnego wyboru zarówno wierszy, jak i kolumn. W powyższych przykładach wszystkie kolumny zostały zwrócone z każdego wyboru. Możemy wybrać kolumny z tymi samymi typami danych wejściowych, co w przypadku wierszy. Po prostu musimy oddzielić zaznaczenie wiersza i kolumny przecinkiem .

Na przykład możemy wybrać wiersze Jane i Dean za pomocą wysokości kolumn, wyniku i stanu w następujący sposób:

df.loc[['Jane', 'Dean'], 'height':]

wprowadź opis zdjęcia tutaj

Używa to listy etykiet dla wierszy i notacji plastra dla kolumn

Możemy naturalnie wykonywać podobne operacje, .ilocużywając tylko liczb całkowitych.

df.iloc[[1,4], 2]
Nick      Lamb
Dean    Cheese
Name: food, dtype: object

Jednoczesny wybór z etykietami i lokalizacją całkowitą

.ixbył używany do dokonywania wyborów jednocześnie z etykietami i lokalizacją liczb całkowitych, co było użyteczne, ale czasami mylące i dwuznaczne, i na szczęście zostało wycofane. W przypadku, gdy musisz dokonać wyboru za pomocą kombinacji etykiet i lokalizacji liczb całkowitych, musisz dokonać zarówno etykiet wyboru, jak i lokalizacji liczb całkowitych.

Na przykład, jeśli chcemy wybrać wiersze Nicki Corneliawraz z kolumnami 2 i 4, moglibyśmy skorzystać .locz konwersji liczb całkowitych na etykiety z następującymi:

col_names = df.columns[[2, 4]]
df.loc[['Nick', 'Cornelia'], col_names] 

Lub alternatywnie przekonwertuj etykiety indeksu na liczby całkowite za pomocą get_locmetody index.

labels = ['Nick', 'Cornelia']
index_ints = [df.index.get_loc(label) for label in labels]
df.iloc[index_ints, [2, 4]]

Wybór boolowski

Indeksator .loc może także dokonywać wyboru wartości logicznej. Na przykład, jeśli chcemy znaleźć wszystkie wiersze, w których wiek jest wyższy niż 30 i zwrócić tylko kolumny foodi score, możemy wykonać następujące czynności:

df.loc[df['age'] > 30, ['food', 'score']] 

Możesz to powtórzyć, .ilocale nie możesz przekazać mu serii boolowskiej. Musisz przekonwertować wartość logiczną na tablicę liczb liczbowych, taką jak ta:

df.iloc[(df['age'] > 30).values, [2, 4]] 

Zaznaczanie wszystkich wierszy

Można użyć .loc/.iloctylko do wyboru kolumny. Możesz zaznaczyć wszystkie wiersze za pomocą dwukropka takiego jak ten:

df.loc[:, 'color':'score':2]

wprowadź opis zdjęcia tutaj


Operator indeksowania []może również wybierać wiersze i kolumny, ale nie jednocześnie.

Większość ludzi zna podstawowy cel operatora indeksowania DataFrame, którym jest wybieranie kolumn. Ciąg wybiera pojedynczą kolumnę jako Serię, a lista ciągów wybiera wiele kolumn jako ramkę danych.

df['food']

Jane          Steak
Nick           Lamb
Aaron         Mango
Penelope      Apple
Dean         Cheese
Christina     Melon
Cornelia      Beans
Name: food, dtype: object

Korzystanie z listy wybiera wiele kolumn

df[['food', 'score']]

wprowadź opis zdjęcia tutaj

Ludzie mniej znają to, że przy stosowaniu notacji plastra wybór odbywa się na podstawie etykiet wierszy lub według lokalizacji liczb całkowitych. Jest to bardzo mylące i coś, czego prawie nigdy nie używam, ale działa.

df['Penelope':'Christina'] # slice rows by label

wprowadź opis zdjęcia tutaj

df[2:6:2] # slice rows by integer location

wprowadź opis zdjęcia tutaj

Jednoznaczność z .loc/.ilocwysoce korzystne dla wierszy zaznaczania. Sam operator indeksowania nie jest w stanie jednocześnie zaznaczyć wierszy i kolumn.

df[3:5, 'color']
TypeError: unhashable type: 'slice'
Ted Petrou
źródło
6
Wow, to było jedno z bardzo dobrze wyartykułowanych i jasnych wyjaśnień, jakie kiedykolwiek spotkałem na temat programowania. To, co wyjaśniłeś w poprzednim rozdziale na temat normalnego indeksowania, które działa albo na wierszach, albo na kolumnach, jest jednym z powodów, dla których mamy loc i iloc metoda. Natknąłem się na to zastrzeżenie na kursie datacamp. a.) Co zwracają kolumny df.index? Czy to lista ciągów? Jeśli jest to lista, czy można uzyskać dostęp do dwóch elementów takich jak ten df.columns [[2,4]] na liście? b.) Czy mogę wywołać get_loc () na df.columns? c.) Dlaczego musimy nazywać df ['wiek']> 30. wartości w przypadku iloc.
pragun
Najlepsza odpowiedź, jaką kiedykolwiek widziałem.
Maks.
To jest naprawdę dobra odpowiedź. Podobało mi się, że nie wnosi zbyt wiele do IX, który jest przestarzały i nie ma sensu nurkować głęboko. Dzięki.
omabena