python - index - ¿En qué se diferencian iloc, ix y loc?
pandas select columns (3)
¿Alguien puede explicar cómo estos tres métodos de corte son diferentes?
He visto
los documentos
y he visto
these
answers
, 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 un
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?
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
posición
para
.iloc
y, en cambio, prefiero
la ubicación de enteros,
ya que es mucho más descriptivo y exactamente lo que significa
.iloc
.
La palabra clave es INTEGER:
.iloc
necesita INTEGERS.
Vea mi serie de blog extremadamente detallada sobre la selección de subconjuntos para más
.ix está en desuso y es ambiguo y nunca debe usarse
Como
.ix
está en desuso, solo nos centraremos en las diferencias entre
.loc
y
.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''])
Todas las palabras en
negrita
son las etiquetas.
Las etiquetas,
age
,
color
,
food
,
height
,
score
y
state
se utilizan para las
columnas
.
Las otras etiquetas,
Jane
,
Nick
,
Aaron
,
Penelope
,
Dean
,
Christina
,
Cornelia
se utilizan para el
índice
.
Las formas principales de seleccionar filas particulares en un DataFrame son con los
.loc
y
.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 indexador
.loc
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 después de
.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:
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'']
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:
Selección de varias filas con .iloc con notación de corte
df.iloc[:5:3]
Selección simultánea de filas y columnas con .loc y .iloc
Una excelente capacidad de ambos
.loc/.iloc
es su capacidad para seleccionar tanto filas como 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'':]
Esto utiliza una lista de etiquetas para las filas y la notación de corte para las columnas.
Naturalmente, podemos hacer operaciones similares con
.iloc
usando 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
.ix
se usó para hacer selecciones simultáneamente con etiquetas y ubicación de números enteros, lo cual 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 las filas
Nick
y
Cornelia
junto con las columnas 2 y 4, podríamos usar
.loc
al convertir 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 método de índice
get_loc
.
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 de
food
y
score
, podemos hacer lo siguiente:
df.loc[df[''age''] > 30, [''food'', ''score'']]
Puede replicar esto con
.iloc
pero 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/.iloc
solo para la selección de columnas.
Puede seleccionar todas las filas utilizando dos puntos como este:
df.loc[:, ''color'':''score'':2]
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 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'']]
Con lo que las personas están menos familiarizadas es que, cuando se utiliza 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
df[2:6:2] # slice rows by integer location
La
.loc/.iloc
de
.loc/.iloc
para 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''
iloc
funciona en base al posicionamiento de enteros.
Así que no importa cuáles sean sus etiquetas de fila, 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,
.loc
usa í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 columna
''date''
por
df.loc[''b'':, ''date''] # equivalent to df.iloc[1:, 1]
y así.
Ahora, probablemente valga la pena señalar que los índices predeterminados de fila y columna para un
DataFrame
son enteros de 0 y, en este caso,
iloc
y
loc
funcionarían de la misma manera.
Es por eso que sus tres ejemplos son equivalentes.
Si tuviera un índice no numérico como cadenas u
df.loc[:5]
fecha,
df.loc[:5]
generaría un error.
Además, puede realizar la recuperación de columnas simplemente usando el
__getitem__
del marco de datos:
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 entra
.ix
:
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 método
loc
.
Por ejemplo:
b = [True, False, True]
df.loc[b]
Devolverá la primera y tercera filas 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''
Nota: en pandas versión 0.20.0 y superior,
ix
está en
deprecated
y se recomienda el uso de
loc
e
iloc
.
He dejado intactas las partes de esta respuesta que describen
ix
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:
-
loc
obtiene filas (o columnas) con etiquetas particulares del índice. -
iloc
obtiene filas (o columnas) en posiciones particulares en el índice (por lo que solo toma enteros). -
ix
generalmente intenta comportarse comoloc
pero vuelve a comportarse comoiloc
si no hay una etiqueta en el índice.
Es importante tener en cuenta algunas sutilezas que pueden hacer que
ix
poco difícil de usar:
-
Si el índice es de tipo entero,
ix
solo utilizará 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 números enteros, entonces dado un número entero,
ix
usará de inmediato la indexación basada en posición en lugar de la indexación basada en etiquetas. Sin embargo, si aix
se 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
Observe que
s.ix[:3]
devuelve la misma serie que
s.loc[:3]
ya que primero busca la etiqueta en lugar de trabajar en la posición (y el índice para
s
es 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 que
6
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 señaladas anteriormente,
s.ix[:6]
ahora genera un KeyError porque intenta funcionar como
loc
pero no puede encontrar un
6
en 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 entero,
ix
se comportaría como
iloc
inmediatamente 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
ix
aú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,
iloc
con
loc
o
iloc
para evitar resultados inesperados; intente no usar
ix
.
Combinación de indexación basada en posición y basada en etiquetas
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),
ix
permite hacer esto con bastante cuidado: podemos dividir las filas por etiqueta y las columnas por posición (tenga en cuenta que para las columnas,
ix
pasará de manera predeterminada al corte basado en la posición ya que
4
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 usando
iloc
y 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
iloc
es 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 deprecated .