In che modo iloc, ix e loc sono diversi?


637

Qualcuno può spiegare come questi tre metodi di taglio sono diversi?
Ho visto i documenti e ho visto queste risposte , ma mi trovo ancora incapace di spiegare come i tre sono diversi. A me sembrano intercambiabili in gran parte, perché sono ai livelli più bassi di affettatura.

Ad esempio, supponiamo di voler ottenere le prime cinque righe di a DataFrame. Come mai funzionano tutti e tre?

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

Qualcuno può presentare tre casi in cui la distinzione negli usi è più chiara?


7
molto importante citare gli scenari SettingWithCopyWarning: stackoverflow.com/questions/20625582/... e stackoverflow.com/questions/23688307/...
Paul

9
Si noti che ix è ora pianificato per la deprecazione: github.com/pandas-dev/pandas/issues/14218
JohnE

Risposte:


970

Nota: nella versione panda 0,20 e successive, ixè deprecatoloc e ilocviene invece incoraggiato l'uso di e . Ho lasciato le parti di questa risposta che descrivono ixintatte come riferimento per gli utenti delle versioni precedenti di Panda. Di seguito sono stati aggiunti esempi che mostrano alternative a ix .


Innanzitutto, ecco un riepilogo dei tre metodi:

  • locottiene righe (o colonne) con etichette particolari dall'indice.
  • ilocottiene righe (o colonne) in particolari posizioni nell'indice (quindi richiede solo numeri interi).
  • ixdi solito cerca di comportarsi come, locma ricomincia a comportarsi come ilocse un'etichetta non fosse presente nell'indice.

È importante notare alcune sottigliezze che possono rendere ixleggermente difficili da usare:

  • se l'indice è di tipo intero, ixutilizzerà solo l'indicizzazione basata sull'etichetta e non tornerà all'indicizzazione basata sulla posizione. Se l'etichetta non è nell'indice, viene generato un errore.

  • se l'indice non contiene solo numeri interi, quindi viene fornito un numero intero, ixutilizzerà immediatamente l'indicizzazione basata sulla posizione anziché l'indicizzazione basata sull'etichetta. Se tuttavia ixviene assegnato un altro tipo (ad esempio una stringa), può utilizzare l'indicizzazione basata su etichetta.


Per illustrare le differenze tra i tre metodi, considerare le seguenti serie:

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

Vedremo lo slicing con il valore intero 3.

In questo caso, s.iloc[:3]ci restituisce le prime 3 righe (poiché tratta 3 come una posizione) e s.loc[:3]ci restituisce le prime 8 righe (poiché tratta 3 come un'etichetta):

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

Notice s.ix[:3]restituisce la stessa serie in s.loc[:3]quanto cerca prima l'etichetta anziché lavorare sulla posizione (e l'indice per sè di tipo intero).

Cosa succede se proviamo con un'etichetta intera che non è nell'indice (diciamo 6)?

Qui s.iloc[:6]restituisce le prime 6 file della serie come previsto. Tuttavia, s.loc[:6]genera un KeyError poiché 6non è nell'indice.

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

Secondo le sottigliezze sopra indicate, s.ix[:6]ora genera un KeyError perché cerca di funzionare come locma non riesce a trovare un 6nell'indice. Perché il nostro indice è di tipo intero ixnon si comporta come iloc.

Se, tuttavia, il nostro indice fosse di tipo misto, dato che un numero intero ixsi comporterebbe ilocimmediatamente invece di sollevare un 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

Tieni presente che ixpuò ancora accettare numeri non interi e comportarsi come loc:

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

Come consiglio generale, se stai solo indicizzando usando le etichette, o solo indicizzando usando posizioni intere, segui loco ilocper evitare risultati imprevisti - prova a non usare ix.


Combinazione di indicizzazione basata sulla posizione e basata sull'etichetta

A volte dato un DataFrame, vorrai mescolare i metodi di indicizzazione di posizione e di etichetta per le righe e le colonne.

Ad esempio, considerare il seguente DataFrame. Il modo migliore per dividere le righe fino a "c" inclusa e prendere le prime quattro colonne?

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

Nelle versioni precedenti di Panda (prima di 0.20.0) ixti consente di farlo abbastanza ordinatamente: possiamo tagliare le righe per etichetta e le colonne per posizione (nota che per le colonne, ixper impostazione predefinita , la suddivisione in base alla posizione 4non sarà un nome di colonna ):

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

Nelle versioni successive di Panda, possiamo ottenere questo risultato usando iloce con l'aiuto di un altro metodo:

>>> 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()è un metodo indice che significa "ottieni la posizione dell'etichetta in questo indice". Si noti che poiché lo slicing con ilocè esclusivo del suo endpoint, dobbiamo aggiungere 1 a questo valore se vogliamo anche la riga 'c'.

Ci sono ulteriori esempi nella documentazione di Panda qui .


12
Grande spiegazione! Una domanda correlata che ho sempre avuto è quale relazione, se presente, loc, iloc e ix hanno con gli avvisi SettingWithCopy? V'è una certa documentazione, ma ad essere onesti io sono ancora un po 'confuso pandas.pydata.org/pandas-docs/stable/...
measureallthethings

3
@measureallthethings: loc, iloce ixpotrebbero ancora scattare l'allarme se sono collegati tra loro. L'uso dell'esempio DataFrame nei documenti collegati dfmi.loc[:, 'one'].loc[:, 'second']attiva l'avviso come se dfmi['one']['second']una prima operazione di indicizzazione potesse restituire una copia dei dati (anziché una vista).
Alex Riley,

Cosa usi se vuoi cercare un DateIndex con una data o qualcosa del genere df.ix[date, 'Cash']?
cjm2671,

@ cjm2671: entrambi loco ixdovrebbero funzionare in quel caso. Ad esempio, df.loc['2016-04-29', 'Cash']restituirà tutti gli indici di riga con quella data particolare dalla colonna "Contanti". (Puoi essere il più specifico che desideri quando recuperi gli indici con le stringhe, ad esempio '2016-01'selezionerà tutti i periodi di tempo che cadono a gennaio 2016, `` 2016-01-02 11 'selezionerà i periodi di tempo il 2 gennaio 2016 con l'ora 11: ??: ?? .)
Alex Riley,

Nel caso in cui desideri aggiornare questa risposta a un certo punto, ci sono suggerimenti qui su come utilizzare loc / iloc invece di ix github.com/pandas-dev/pandas/issues/14218
JohnE

142

ilocfunziona in base al posizionamento dei numeri interi. Quindi, indipendentemente dalle etichette delle righe, puoi sempre ottenere, ad esempio, la prima riga

df.iloc[0]

o le ultime cinque righe facendo

df.iloc[-5:]

Puoi anche usarlo sulle colonne. Questo recupera la terza colonna:

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

Puoi combinarli per ottenere intersezioni di righe e colonne:

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

D'altra parte, .locutilizzare indici denominati. Configuriamo un frame di dati con stringhe come etichette di riga e colonna:

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

Quindi possiamo ottenere la prima riga

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

e le seconde due righe della 'date'colonna di

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

e così via. Ora, probabilmente vale la pena sottolineare che gli indici di riga e colonna predefiniti per a DataFramesono numeri interi da 0 e in questo caso iloce locfunzionerebbero allo stesso modo. Ecco perché i tuoi tre esempi sono equivalenti. Se avessi un indice non numerico come stringhe o periodi di dati, df.loc[:5] genererebbe un errore.

Inoltre, è possibile eseguire il recupero delle colonne semplicemente utilizzando i frame di dati __getitem__:

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

Supponiamo ora che tu voglia mescolare posizione e indicizzazione denominata, cioè indicizzazione usando nomi su righe e posizioni su colonne (per chiarire, intendo selezionare dal nostro frame di dati, piuttosto che creare un frame di dati con stringhe nell'indice di riga e numeri interi in l'indice di colonna). È qui che .ixentra in gioco:

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

Penso che valga anche la pena ricordare che è possibile passare anche vettori booleani al locmetodo. Per esempio:

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

Restituirà la prima e la terza riga di df. Ciò equivale a df[b]per la selezione, ma può anche essere utilizzato per l'assegnazione tramite vettori booleani:

df.loc[b, 'name'] = 'Mary', 'John'

Df.iloc [:,:] è equivalente a tutte le righe e colonne?
Alvis,

Lo è, come sarebbe df.loc[:, :]. Può essere utilizzato per riassegnare i valori dell'intero DataFrameo crearne una vista.
JoeCondron,

ciao, sai perché loc e iloc accettano parametri tra la parentesi quadra [] e non come un metodo normale tra parentesi classica ()?
Marine Galantin

119

A mio avviso, la risposta accettata è confusa, poiché utilizza un DataFrame con solo valori mancanti. Anche io non mi piace il termine basato sulla posizione per .iloce invece, preferiscono integer posizione in quanto è molto più descrittivo e esattamente ciò che .ilocrappresenta. La parola chiave è INTEGER - ha .ilocbisogno di INTEGERS.

Vedi le mie serie di blog estremamente dettagliate sulla selezione dei sottoinsiemi per ulteriori informazioni


.ix è obsoleto e ambiguo e non dovrebbe mai essere usato

Poiché .ixè deprecato, ci concentreremo solo sulle differenze tra .loce .iloc.

Prima di parlare delle differenze, è importante capire che DataFrames ha etichette che aiutano a identificare ogni colonna e ogni indice. Diamo un'occhiata a un DataFrame di esempio:

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

inserisci qui la descrizione dell'immagine

Tutte le parole in grassetto sono le etichette. Le etichette, age, color, food, height, scoree statesono utilizzati per le colonne . Gli altri marchi, Jane, Nick, Aaron, Penelope, Dean, Christina, Corneliasono utilizzati per l'index .


I modi principali per selezionare determinate righe in un DataFrame sono con gli indicizzatori .loce .iloc. Ognuno di questi indicizzatori può anche essere utilizzato per selezionare contemporaneamente le colonne, ma per ora è più semplice concentrarsi solo sulle righe. Inoltre, ciascuno degli indicizzatori utilizza una serie di parentesi che seguono immediatamente il proprio nome per effettuare le selezioni.

.loc seleziona i dati solo dalle etichette

Parleremo prima .locdell'indicizzatore che seleziona i dati solo dalle etichette dell'indice o della colonna. Nel nostro DataFrame di esempio, abbiamo fornito nomi significativi come valori per l'indice. Molti DataFrame non avranno nomi significativi e, invece, saranno predefiniti solo ai numeri interi da 0 a n-1, dove n è la lunghezza del DataFrame.

Esistono tre diversi input per cui è possibile utilizzare .loc

  • Una stringa
  • Un elenco di stringhe
  • Taglia la notazione usando le stringhe come valori di inizio e fine

Selezione di una singola riga con .loc con una stringa

Per selezionare una singola riga di dati, posizionare l'etichetta dell'indice all'interno delle parentesi seguenti .loc.

df.loc['Penelope']

Ciò restituisce la riga di dati come una serie

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

Selezione di più righe con .loc con un elenco di stringhe

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

Ciò restituisce un DataFrame con le righe nell'ordine specificato nell'elenco:

inserisci qui la descrizione dell'immagine

Selezione di più righe con .loc con notazione slice

La notazione delle sezioni è definita da un valore di avvio, arresto e step. Quando si taglia per etichetta, i panda includono il valore di arresto nel ritorno. Le seguenti sezioni da Aaron a Dean, incluso. La dimensione del passo non è definita in modo esplicito, ma per impostazione predefinita è 1.

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

inserisci qui la descrizione dell'immagine

Le sezioni complesse possono essere prese allo stesso modo delle liste Python.

.iloc seleziona i dati solo per posizione intera

Passiamo ora a .iloc. Ogni riga e colonna di dati in un DataFrame ha una posizione intera che lo definisce. Ciò si aggiunge all'etichetta visualizzata visivamente nell'output . La posizione intera è semplicemente il numero di righe / colonne dall'alto / a sinistra a partire da 0.

Esistono tre diversi input per cui è possibile utilizzare .iloc

  • Un numero intero
  • Un elenco di numeri interi
  • Taglia la notazione usando numeri interi come valori di inizio e fine

Selezione di una singola riga con .iloc con un numero intero

df.iloc[4]

Ciò restituisce la quinta riga (posizione intera 4) come una serie

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

Selezione di più righe con .iloc con un elenco di numeri interi

df.iloc[[2, -2]]

Ciò restituisce un DataFrame della terza e seconda all'ultima riga:

inserisci qui la descrizione dell'immagine

Selezione di più righe con .iloc con notazione slice

df.iloc[:5:3]

inserisci qui la descrizione dell'immagine


Selezione simultanea di righe e colonne con .loc e .iloc

Un'ottima capacità di entrambi .loc/.ilocè la possibilità di selezionare contemporaneamente righe e colonne. Negli esempi sopra, tutte le colonne sono state restituite da ciascuna selezione. Siamo in grado di scegliere colonne con gli stessi tipi di input utilizzati per le righe. Dobbiamo semplicemente separare la selezione di righe e colonne con una virgola .

Ad esempio, possiamo selezionare le righe Jane e Dean con solo l'altezza delle colonne, segnare e dichiarare in questo modo:

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

inserisci qui la descrizione dell'immagine

Questo utilizza un elenco di etichette per le righe e la notazione delle sezioni per le colonne

Naturalmente possiamo fare operazioni simili .ilocusando solo numeri interi.

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

Selezione simultanea con etichette e posizione intera

.ixè stato usato per effettuare selezioni simultaneamente con etichette e posizione di numeri interi che a volte era utile ma confusa e ambigua e per fortuna è stato deprecato. Nel caso in cui sia necessario effettuare una selezione con una combinazione di etichette e posizioni di numeri interi, sarà necessario effettuare sia le etichette di selezione sia le posizioni di numeri interi.

Ad esempio, se vogliamo selezionare le righe Nicke Corneliainsieme alle colonne 2 e 4, potremmo utilizzare .locconvertendo i numeri interi in etichette con quanto segue:

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

In alternativa, converti le etichette degli indici in numeri interi con il get_locmetodo index.

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

Selezione booleana

L'indicizzatore .loc può anche fare una selezione booleana. Ad esempio, se siamo interessati a trovare tutte le righe in cui l'età è superiore a 30 e restituiamo solo le colonne foode score, possiamo fare quanto segue:

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

Puoi replicarlo con .ilocma non puoi passargli una serie booleana. È necessario convertire la serie booleana in un array intorpidito come questo:

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

Selezione di tutte le righe

È possibile utilizzare .loc/.ilocsolo per la selezione delle colonne. Puoi selezionare tutte le righe usando i due punti come questo:

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

inserisci qui la descrizione dell'immagine


L'operatore di indicizzazione [], può selezionare anche righe e colonne ma non contemporaneamente.

Molte persone hanno familiarità con lo scopo principale dell'operatore di indicizzazione DataFrame, ovvero selezionare le colonne. Una stringa seleziona una singola colonna come Serie e un elenco di stringhe seleziona più colonne come DataFrame.

df['food']

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

L'uso di un elenco consente di selezionare più colonne

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

inserisci qui la descrizione dell'immagine

Ciò che le persone hanno meno familiarità è che, quando viene utilizzata la notazione delle sezioni, la selezione avviene per etichette di riga o per posizione di numero intero. Questo è molto confuso e qualcosa che non uso quasi mai, ma funziona.

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

inserisci qui la descrizione dell'immagine

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

inserisci qui la descrizione dell'immagine

La spiegazione di .loc/.ilocper selezionare le righe è altamente preferita. Il solo operatore di indicizzazione non è in grado di selezionare contemporaneamente righe e colonne.

df[3:5, 'color']
TypeError: unhashable type: 'slice'

6
Wow, questa è stata una delle spiegazioni molto ben articolate e lucide che io abbia mai incontrato su un argomento di programmazione, quello che hai spiegato negli ultimi sull'indicizzazione normale che funziona su righe o colonne è uno dei motivi per cui abbiamo loc e iloc metodo. Mi sono imbattuto in questo avvertimento nel corso del datacamp. a.) Cosa restituiscono df.columns e df.index? È un elenco di stringhe? Se è un elenco, è consentito accedere a due elementi come questo df.columns [[2,4]] in un elenco? b.) Posso chiamare get_loc () su df.columns? c.) Perché dobbiamo chiamare df ['age']> 30.values ​​in caso di iloc.
pragun,

La migliore risposta che abbia mai visto.
Max

Questa è davvero una buona risposta, mi è piaciuto che non entri molto in ix, che è deprecato e inutile immergersi in profondità. Grazie.
omabena,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.