¿En qué se diferencian iloc, ix y loc?

637

¿Alguien puede explicar cómo estos tres métodos de corte son diferentes?
He visto los documentos y he visto estas respuestas , pero sigo sin poder explicar cómo los tres son diferentes. Para mí, parecen intercambiables en gran parte, porque están en los niveles más bajos de corte.

Por ejemplo, supongamos que queremos obtener las primeras cinco filas de a DataFrame. ¿Cómo es que los tres funcionan?

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

¿Puede alguien presentar tres casos donde la distinción en los usos es más clara?

AZhao
fuente
77
Es muy importante mencionar los escenarios SettingWithCopyWarning: stackoverflow.com/questions/20625582/… y stackoverflow.com/questions/23688307/…
Paul
99
Tenga en cuenta que ix ahora está planificado para su desuso: github.com/pandas-dev/pandas/issues/14218
JohnE

Respuestas:

970

Nota: en los pandas versión 0.20.0 y por encima, ixse desaprueba y el uso de locy ilocse fomenta su lugar. He dejado las partes de esta respuesta que describen ixintactas como referencia para los usuarios de versiones anteriores de pandas. A continuación se han agregado ejemplos que muestran alternativas a ix .


Primero, aquí hay un resumen de los tres métodos:

  • locobtiene filas (o columnas) con etiquetas particulares del índice.
  • ilocobtiene filas (o columnas) en posiciones particulares en el índice (por lo que solo toma enteros).
  • ixpor lo general trata de comportarse como locpero se cambia a comportarse como ilocsi una etiqueta no está presente en el índice.

Es importante tener en cuenta algunas sutilezas que pueden ser un ixpoco difíciles de usar:

  • si el índice es de tipo entero, ixsolo usará la indexación basada en etiquetas y no recurrirá a la indexación basada en posiciones. Si la etiqueta no está en el índice, se genera un error.

  • si el índice no contiene solo enteros, entonces dado un entero, ixinmediatamente usará indexación basada en posición en lugar de indexación basada en etiqueta. Sin embargo, si ixse le da otro tipo (por ejemplo, una cadena), puede usar indexación basada en etiquetas.


Para ilustrar las diferencias entre los tres métodos, considere la siguiente 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

Veremos el corte con el valor entero 3.

En este caso, s.iloc[:3]nos devuelve las primeras 3 filas (ya que trata a 3 como una posición) y s.loc[:3]nos devuelve las primeras 8 filas (ya que trata a 3 como una etiqueta):

>>> 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]devuelve la misma serie s.loc[:3]ya que busca primero la etiqueta en lugar de trabajar en la posición (y el índice ses de tipo entero).

¿Qué pasa si intentamos con una etiqueta entera que no está en el índice (digamos 6)?

Aquí s.iloc[:6]devuelve las primeras 6 filas de la serie como se esperaba. Sin embargo, s.loc[:6]genera un KeyError ya 6que no está en el índice.

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

Según las sutilezas mencionadas anteriormente, s.ix[:6]ahora genera un KeyError porque intenta funcionar como locpero no puede encontrar uno 6en el índice. Debido a que nuestro índice es de tipo entero ix, no vuelve a comportarse como iloc.

Sin embargo, si nuestro índice fuera de tipo mixto, dado un número entero ixse comportaría de manera ilocinmediata en lugar de generar 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

Tenga en cuenta que ixaún puede aceptar no enteros y comportarse como loc:

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

Como consejo general, si solo está indexando usando etiquetas, o solo indexando usando posiciones enteras, quédese con loco ilocpara evitar resultados inesperados, intente no usar ix.


Combinación de indexación basada en posición y etiqueta

A veces, dado un DataFrame, querrá mezclar etiquetas y métodos de indexación posicional para las filas y columnas.

Por ejemplo, considere el siguiente DataFrame. ¿Cuál es la mejor forma de cortar las filas hasta e incluyendo 'c' y tomar las primeras cuatro columnas?

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

En versiones anteriores de pandas (antes de 0.20.0) le ixpermite hacer esto con bastante cuidado: podemos cortar las filas por etiqueta y las columnas por posición (tenga en cuenta que para las columnas, el ixcorte predeterminado se basará en la posición ya 4que no es un nombre de columna ):

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

En versiones posteriores de pandas, podemos lograr este resultado utilizando ilocy la ayuda de otro método:

>>> 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()es un método de índice que significa "obtener la posición de la etiqueta en este índice". Tenga en cuenta que dado que el corte con iloces exclusivo de su punto final, debemos agregar 1 a este valor si también queremos la fila 'c'.

Hay más ejemplos en la documentación de los pandas aquí .

Alex Riley
fuente
12
¡Gran explicación! Una pregunta relacionada que siempre tuve es qué relación, si es que hay alguna, loc, iloc e ix tienen con las advertencias de SettingWithCopy. Hay algo de documentación, pero para ser sincero, todavía estoy un poco confundido pandas.pydata.org/pandas-docs/stable/…
measureallthethings
3
@measureallthethings: loc, ilocy ixaún podría desencadenar la advertencia si están encadenados juntos. El uso del ejemplo DataFrame en los documentos vinculados dfmi.loc[:, 'one'].loc[:, 'second']activa la advertencia al igual que dfmi['one']['second']la primera operación de indexación puede devolver una copia de los datos (en lugar de una vista).
Alex Riley
¿Qué utiliza si desea buscar un DateIndex con una fecha, o algo así df.ix[date, 'Cash']?
cjm2671
@ cjm2671: ambos loco ixdeberían funcionar en ese caso. Por ejemplo, df.loc['2016-04-29', 'Cash']devolverá todos los índices de fila con esa fecha en particular desde la columna 'Efectivo'. (Puede ser tan específico como desee al recuperar índices con cadenas, por ejemplo '2016-01', seleccionará todas las fechas que caen en enero de 2016, `` 2016-01-02 11 'seleccionará las fechas el 2 de enero de 2016 con la hora 11: ??: ?? .)
Alex Riley
En caso de que desee actualizar esta respuesta en algún momento, hay sugerencias aquí sobre cómo usar loc / iloc en lugar de ix github.com/pandas-dev/pandas/issues/14218
JohnE
142

ilocTrabajos basados ​​en posicionamiento de enteros. Así que no importa cuáles sean las etiquetas de sus filas, siempre puede, por ejemplo, obtener la primera fila haciendo

df.iloc[0]

o las últimas cinco filas haciendo

df.iloc[-5:]

También puede usarlo en las columnas. Esto recupera la tercera columna:

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

Puede combinarlos para obtener intersecciones de filas y columnas:

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

Por otro lado, .locuse índices con nombre. Configuremos un marco de datos con cadenas como etiquetas de fila y columna:

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

Entonces podemos obtener la primera fila por

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

y las segundas dos filas de la 'date'columna por

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

y así. Ahora, probablemente vale la pena señalar que la fila por defecto y los índices de columna para una DataFrameson números enteros de 0 y en este caso ilocy locfuncionarían de la misma manera. Es por eso que sus tres ejemplos son equivalentes. Si tuviera un índice no numérico, como cadenas u horas, df.loc[:5] generaría un error.

Además, puede hacer la recuperación de columnas simplemente usando los marcos de datos __getitem__:

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

Ahora suponga que desea mezclar la posición y la indexación con nombre, es decir, indexar usando nombres en filas y posiciones en columnas (para aclarar, me refiero a seleccionar de nuestro marco de datos, en lugar de crear un marco de datos con cadenas en el índice de fila y enteros en El índice de la columna). Aquí es donde .ixentra:

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

Creo que también vale la pena mencionar que también puedes pasar vectores booleanos al locmétodo. Por ejemplo:

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

Devolverá la primera y tercera fila de df. Esto es equivalente a df[b]para la selección, pero también se puede usar para asignar mediante vectores booleanos:

df.loc[b, 'name'] = 'Mary', 'John'
JoeCondron
fuente
¿Es df.iloc [:,:] equivalente a todas las filas y columnas?
Alvis
Es, como sería df.loc[:, :]. Se puede usar para reasignar los valores del conjunto DataFrameo crear una vista de él.
JoeCondron
hola, ¿sabes por qué loc e iloc toman parámetros entre paréntesis cuadrados [] y no como un método normal entre paréntesis clásico ()?
Marine Galantin hace
119

En mi opinión, la respuesta aceptada es confusa, ya que utiliza un DataFrame con solo valores faltantes. Tampoco me gusta el término basado en la posición .ilocy, en cambio, prefiero la ubicación de enteros, ya que es mucho más descriptivo y exactamente lo que .ilocsignifica. La palabra clave es INTEGER - .ilocnecesita INTEGERS.

Vea mi serie de blogs extremadamente detallada sobre la selección de subconjuntos para más


.ix está en desuso y es ambiguo y nunca debe usarse

Debido a que .ixestá en desuso, solo nos centraremos en las diferencias entre .locy .iloc.

Antes de hablar sobre las diferencias, es importante comprender que los marcos de datos tienen etiquetas que ayudan a identificar cada columna y cada índice. Echemos un vistazo a un DataFrame de muestra:

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

ingrese la descripción de la imagen aquí

Todas las palabras en negrita son las etiquetas. Las etiquetas, age, color, food, height, scorey statese utilizan para las columnas . Las otras etiquetas, Jane, Nick, Aaron, Penelope, Dean, Christina, Corneliase utilizan para el índice .


Las formas principales de seleccionar filas particulares en un DataFrame son con los indexadores .locy .iloc. Cada uno de estos indexadores también se puede usar para seleccionar columnas simultáneamente, pero por ahora es más fácil centrarse en las filas. Además, cada uno de los indexadores utiliza un conjunto de corchetes que siguen inmediatamente a su nombre para realizar sus selecciones.

.loc selecciona datos solo por etiquetas

Primero hablaremos sobre el .locindexador que solo selecciona datos por las etiquetas de índice o columna. En nuestro DataFrame de muestra, proporcionamos nombres significativos como valores para el índice. Muchos DataFrames no tendrán nombres significativos y, por el contrario, solo usarán los enteros de 0 a n-1, donde n es la longitud del DataFrame.

Hay tres entradas diferentes que puede usar para .loc

  • Una cuerda
  • Una lista de cadenas
  • Corte la notación utilizando cadenas como valores iniciales y finales

Seleccionar una sola fila con .loc con una cadena

Para seleccionar una sola fila de datos, coloque la etiqueta de índice dentro de los corchetes siguientes .loc.

df.loc['Penelope']

Esto devuelve la fila de datos como una serie

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

Seleccionar varias filas con .loc con una lista de cadenas

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

Esto devuelve un DataFrame con las filas en el orden especificado en la lista:

ingrese la descripción de la imagen aquí

Seleccionar varias filas con .loc con notación de corte

La notación de corte se define mediante valores de inicio, parada y paso. Al cortar por etiqueta, los pandas incluyen el valor de detención en el retorno. Los siguientes cortes de Aaron a Dean, inclusive. Su tamaño de paso no está definido explícitamente sino que está predeterminado en 1.

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

ingrese la descripción de la imagen aquí

Los cortes complejos se pueden tomar de la misma manera que las listas de Python.

.iloc selecciona datos solo por ubicación entera

Pasemos ahora a .iloc. Cada fila y columna de datos en un DataFrame tiene una ubicación entera que lo define. Esto se suma a la etiqueta que se muestra visualmente en la salida . La ubicación entera es simplemente el número de filas / columnas desde la parte superior / izquierda que comienza en 0.

Hay tres entradas diferentes que puede usar para .iloc

  • Un entero
  • Una lista de enteros
  • Corte la notación usando enteros como valores de inicio y parada

Seleccionar una sola fila con .iloc con un entero

df.iloc[4]

Esto devuelve la quinta fila (ubicación entera 4) como una serie

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

Seleccionar varias filas con .iloc con una lista de enteros

df.iloc[[2, -2]]

Esto devuelve un DataFrame de la tercera y la penúltima fila:

ingrese la descripción de la imagen aquí

Selección de varias filas con .iloc con notación de corte

df.iloc[:5:3]

ingrese la descripción de la imagen aquí


Selección simultánea de filas y columnas con .loc y .iloc

Una excelente habilidad de ambos .loc/.iloces su habilidad para seleccionar filas y columnas simultáneamente. En los ejemplos anteriores, todas las columnas fueron devueltas de cada selección. Podemos elegir columnas con los mismos tipos de entradas que para las filas. Simplemente necesitamos separar la selección de fila y columna con una coma .

Por ejemplo, podemos seleccionar las filas Jane y Dean con solo la altura de las columnas, la puntuación y el estado de esta manera:

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

ingrese la descripción de la imagen aquí

Esto usa una lista de etiquetas para las filas y la notación de corte para las columnas

Naturalmente, podemos hacer operaciones similares .ilocusando solo enteros.

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

Selección simultánea con etiquetas y ubicación de enteros

.ixse usó para hacer selecciones simultáneamente con etiquetas y ubicación de enteros, lo que fue útil pero confuso y ambiguo a veces y, afortunadamente, ha quedado en desuso. En el caso de que necesite hacer una selección con una combinación de etiquetas y ubicaciones de enteros, deberá realizar tanto las etiquetas de selección como las ubicaciones de enteros.

Por ejemplo, si queremos seleccionar filas Nicky Corneliajunto con las columnas 2 y 4, podríamos usar .locconvirtiendo los enteros en etiquetas con lo siguiente:

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

O, alternativamente, convierta las etiquetas de índice a enteros con el get_locmétodo de índice.

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

Selección booleana

El indexador .loc también puede hacer una selección booleana. Por ejemplo, si estamos interesados ​​en encontrar todas las filas con una edad superior a 30 y devolver solo las columnas foody score, podemos hacer lo siguiente:

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

Puede replicar esto con, .ilocpero no puede pasarle una serie booleana. Debe convertir la serie booleana en una matriz numpy como esta:

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

Seleccionar todas las filas

Es posible usar .loc/.ilocsolo para la selección de columnas. Puede seleccionar todas las filas utilizando dos puntos como este:

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

ingrese la descripción de la imagen aquí


El operador de indexación [], también puede seleccionar filas y columnas, pero no simultáneamente.

La mayoría de las personas están familiarizadas con el propósito principal del operador de indexación de DataFrame, que es seleccionar columnas. Una cadena selecciona una sola columna como Serie y una lista de cadenas selecciona varias columnas como un Marco de datos.

df['food']

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

Usar una lista selecciona múltiples columnas

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

ingrese la descripción de la imagen aquí

Con lo que la gente está menos familiarizada es que, cuando se usa la notación de corte, la selección se realiza por etiquetas de fila o por ubicación entera. Esto es muy confuso y es algo que casi nunca uso, pero funciona.

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

ingrese la descripción de la imagen aquí

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

ingrese la descripción de la imagen aquí

La especificidad de .loc/.ilocpara seleccionar filas es altamente preferida. El operador de indexación solo no puede seleccionar filas y columnas simultáneamente.

df[3:5, 'color']
TypeError: unhashable type: 'slice'
Ted Petrou
fuente
66
Wow, esta fue una de las explicaciones muy bien articuladas y lúcidas que he encontrado sobre un tema de programación, lo que explicaste en el último sobre la indexación normal que funciona en filas o columnas es una de las razones por las que tenemos loc e iloc método. Encontré esa advertencia en el curso del campamento de datos. a.) ¿Qué devuelven df.columns y df.index? ¿Es una lista de cadenas? Si es una lista, ¿está permitido acceder a dos elementos como este df.columns [[2,4]] en una lista? b.) ¿Puedo llamar a get_loc () en df.columns? c.) ¿Por qué necesitamos llamar a df ['age']> 30.valores en caso de iloc.
pragun
El mejor contestador que he visto.
Max
Esta es una respuesta realmente buena, me gustó que no entra mucho en ix, que está en desuso y no tiene sentido sumergirse profundamente. Gracias.
omabena