En quoi iloc, ix et loc sont-ils différents?

637

Quelqu'un peut-il expliquer en quoi ces trois méthodes de découpage sont différentes?
J'ai vu les documents et j'ai vu ces réponses , mais je n'arrive toujours pas à expliquer en quoi les trois sont différents. Pour moi, ils semblent interchangeables en grande partie, car ils sont aux niveaux inférieurs de tranchage.

Par exemple, disons que nous voulons obtenir les cinq premières lignes de a DataFrame. Comment se fait-il que ces trois éléments fonctionnent?

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

Quelqu'un peut-il présenter trois cas où la distinction des usages est plus claire?

AZhao
la source
7
très important de mentionner les scénarios SettingWithCopyWarning: stackoverflow.com/questions/20625582/… et stackoverflow.com/questions/23688307/…
Paul
9
Notez que ix est maintenant prévu pour la dépréciation: github.com/pandas-dev/pandas/issues/14218
JohnE

Réponses:

970

Remarque: dans les versions 0.20.0 et supérieures de pandas, ixest déconseillé et l'utilisation de locet ilocest plutôt encouragée. J'ai laissé les parties de cette réponse qui décrivent ixintactes comme référence pour les utilisateurs des versions antérieures de pandas. Des exemples ont été ajoutés ci-dessous montrant des alternatives à ix .


Tout d'abord, voici un récapitulatif des trois méthodes:

  • locobtient des lignes (ou colonnes) avec des étiquettes particulières de l'index.
  • ilocobtient des lignes (ou colonnes) à des positions particulières dans l'index (donc il ne prend que des entiers).
  • ixessaie généralement de se comporter comme locmais revient à se comporter comme ilocsi une étiquette n'est pas présente dans l'index.

Il est important de noter certaines subtilités qui peuvent être ixlégèrement délicates à utiliser:

  • si l'index est de type entier, ixn'utilisera que l'indexation basée sur les étiquettes et ne retombera pas sur l'indexation basée sur la position. Si l'étiquette n'est pas dans l'index, une erreur est générée.

  • si l'index ne contient pas uniquement des entiers, alors étant donné un entier, ixutilisera immédiatement l'indexation basée sur la position plutôt que l'indexation basée sur l'étiquette. Si toutefois ixon lui donne un autre type (par exemple une chaîne), il peut utiliser une indexation basée sur les étiquettes.


Pour illustrer les différences entre les trois méthodes, considérons la série suivante:

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

Nous allons voir le découpage avec la valeur entière 3.

Dans ce cas, s.iloc[:3]nous renvoie les 3 premières lignes (car il traite 3 comme une position) et s.loc[:3]nous renvoie les 8 premières lignes (car il traite 3 comme une étiquette):

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

Remarque s.ix[:3]renvoie la même série s.loc[:3]car il recherche d'abord l'étiquette plutôt que de travailler sur la position (et l'index pour sest de type entier).

Et si nous essayons avec une étiquette entière qui n'est pas dans l'index (disons 6)?

Renvoie ici s.iloc[:6]les 6 premières lignes de la série comme prévu. Cependant, s.loc[:6]déclenche une erreur KeyError puisqu'elle 6n'est pas dans l'index.

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

Selon les subtilités notées ci-dessus, s.ix[:6]déclenche maintenant une erreur KeyError car il essaie de fonctionner comme locmais ne peut pas trouver un 6dans l'index. Parce que notre index est de type entier ixne revient pas à se comporter comme iloc.

Si, cependant, notre index était de type mixte, étant donné qu'un entier ixse comporterait comme ilocimmédiatement au lieu de déclencher une erreur de clé:

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

Gardez à l'esprit que vous ixpouvez toujours accepter des non-entiers et vous comporter comme loc:

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

Comme conseil général, si vous indexez uniquement à l'aide d'étiquettes, ou uniquement à l'aide de positions entières, respectez locou ilocpour éviter des résultats inattendus - essayez de ne pas utiliser ix.


Combinaison d'une indexation basée sur la position et basée sur une étiquette

Parfois, étant donné un DataFrame, vous souhaiterez mélanger les méthodes d'indexation d'étiquette et de position pour les lignes et les colonnes.

Par exemple, considérez le DataFrame suivant. Comment découper au mieux les lignes jusqu'au «c» inclus et prendre les quatre premières colonnes?

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

Dans les versions antérieures de pandas (avant 0.20.0), cela ixvous permet de le faire assez bien - nous pouvons découper les lignes par étiquette et les colonnes par position (notez que pour les colonnes, ixpar défaut, le découpage basé sur la position 4n'est pas un nom de colonne ):

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

Dans les versions ultérieures de pandas, nous pouvons obtenir ce résultat à l'aide ilocet à l'aide d'une autre méthode:

>>> 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()est une méthode d'index qui signifie "obtenir la position de l'étiquette dans cet index". Notez que puisque le découpage avec ilocest exclusif de son point de terminaison, nous devons ajouter 1 à cette valeur si nous voulons également la ligne 'c'.

Il y a d'autres exemples dans la documentation des pandas ici .

Alex Riley
la source
12
Grande explication! Une question connexe que j'ai toujours eue est quelle relation, le cas échéant, loc, iloc et ix ont avec les avertissements SettingWithCopy? Il y a de la documentation , mais pour être honnête , je suis encore un peu confus pandas.pydata.org/pandas-docs/stable/...
measureallthethings
3
@measureallthethings: loc, ilocet ixpourrait encore déclencher l'avertissement si elles sont enchaînées. L'utilisation de l'exemple DataFrame dans les documents liés dfmi.loc[:, 'one'].loc[:, 'second']déclenche l'avertissement, tout comme dfmi['one']['second']une copie des données (plutôt qu'une vue) peut être renvoyée par la première opération d'indexation.
Alex Riley
Qu'utilisez-vous si vous souhaitez rechercher un DateIndex avec une date, ou quelque chose comme ça df.ix[date, 'Cash']?
cjm2671
@ cjm2671: les deux locou ixdevraient fonctionner dans ce cas. Par exemple, df.loc['2016-04-29', 'Cash']retournera tous les index de ligne avec cette date particulière de la colonne 'Cash'. (Vous pouvez être aussi précis que vous le souhaitez lors de la récupération des index avec des chaînes, par exemple '2016-01', sélectionnera tous les horaires tombant en janvier 2016, `` 2016-01-02 11 '' sélectionnera les horaires le 2 janvier 2016 avec l'heure 11: ??: ?? .)
Alex Riley
Si vous souhaitez mettre à jour cette réponse à un moment donné, il existe des suggestions ici pour savoir comment utiliser loc / iloc au lieu de ix github.com/pandas-dev/pandas/issues/14218
JohnE
142

ilocfonctionne basé sur le positionnement entier. Donc, peu importe vos étiquettes de ligne, vous pouvez toujours, par exemple, obtenir la première ligne en faisant

df.iloc[0]

ou les cinq dernières lignes en faisant

df.iloc[-5:]

Vous pouvez également l'utiliser sur les colonnes. Cela récupère la 3ème colonne:

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

Vous pouvez les combiner pour obtenir des intersections de lignes et de colonnes:

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

D'un autre côté, .locutilisez des indices nommés. Configurons un bloc de données avec des chaînes comme étiquettes de ligne et de colonne:

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

Ensuite, nous pouvons obtenir la première ligne en

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

et les deux deuxièmes rangées de la 'date'colonne par

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

etc. Maintenant, il convient probablement de souligner que les indices de ligne et de colonne par défaut pour a DataFramesont des entiers de 0 et dans ce cas iloc, locils fonctionneraient de la même manière. C'est pourquoi vos trois exemples sont équivalents. Si vous aviez un index non numérique tel que des chaînes ou des heures, df.loc[:5] cela déclencherait une erreur.

En outre, vous pouvez effectuer une récupération de colonne en utilisant simplement le bloc de données __getitem__:

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

Supposons maintenant que vous vouliez mélanger la position et l'indexation nommée, c'est-à-dire l'indexation en utilisant des noms sur les lignes et des positions sur les colonnes (pour clarifier, je veux dire sélectionner dans notre bloc de données, plutôt que de créer un bloc de données avec des chaînes dans l'index des lignes et des entiers dans l'index de la colonne). C'est là .ixqu'intervient:

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

Je pense qu'il convient également de mentionner que vous pouvez également transmettre des vecteurs booléens à la locméthode. Par exemple:

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

Renvoie les 1er et 3e rangées de df. C'est équivalent à df[b]pour la sélection, mais il peut également être utilisé pour l'affectation via des vecteurs booléens:

df.loc[b, 'name'] = 'Mary', 'John'
JoeCondron
la source
Df.iloc [:,:] est-il équivalent à toutes les lignes et colonnes?
Alvis
C'est, comme ce serait df.loc[:, :]. Il peut être utilisé pour réaffecter les valeurs de l'ensemble DataFrameou en créer une vue.
JoeCondron
salut, savez-vous pourquoi loc et iloc prennent des paramètres entre les parenthèses carrées [] et non comme une méthode normale entre les parenthèses classiques ()?
Marine Galantin Il y a
119

À mon avis, la réponse acceptée prête à confusion, car elle utilise un DataFrame avec uniquement des valeurs manquantes. Je n'aime pas non plus le terme basé sur la position .ilocet préfère plutôt l' emplacement entier car il est beaucoup plus descriptif et exactement ce qui .ilocsignifie. Le mot clé est INTEGER - a .ilocbesoin d'INTÉGRATEURS.

Voir ma série de blogs extrêmement détaillée sur la sélection de sous-ensembles pour en savoir plus


.ix est obsolète et ambigu et ne doit jamais être utilisé

Parce qu'il .ixest obsolète, nous nous concentrerons uniquement sur les différences entre .locet .iloc.

Avant de parler des différences, il est important de comprendre que les DataFrames ont des étiquettes qui aident à identifier chaque colonne et chaque index. Jetons un coup d'œil à un exemple de 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'])

entrez la description de l'image ici

Tous les mots en gras sont les étiquettes. Les étiquettes, age, color, food, height, scoreet statesont utilisés pour les colonnes . Les autres étiquettes, Jane, Nick, Aaron, Penelope, Dean, Christina, Corneliasont utilisés pour l' indice .


Les principaux moyens de sélectionner des lignes particulières dans un DataFrame sont avec les indexeurs .locet .iloc. Chacun de ces indexeurs peut également être utilisé pour sélectionner simultanément des colonnes, mais il est plus facile de se concentrer uniquement sur les lignes pour l'instant. De plus, chacun des indexeurs utilise un ensemble de crochets qui suivent immédiatement leur nom pour effectuer leurs sélections.

.loc sélectionne les données uniquement par des étiquettes

Nous parlerons d'abord de l' .locindexeur qui sélectionne uniquement les données par les étiquettes d'index ou de colonne. Dans notre exemple DataFrame, nous avons fourni des noms significatifs comme valeurs pour l'index. De nombreux DataFrames n'auront pas de noms significatifs et seront, par défaut, uniquement des entiers de 0 à n-1, où n est la longueur du DataFrame.

Vous pouvez utiliser trois entrées différentes pour .loc

  • Un string
  • Une liste de chaînes
  • Notation de tranche utilisant des chaînes comme valeurs de début et de fin

Sélection d'une seule ligne avec .loc avec une chaîne

Pour sélectionner une seule ligne de données, placez l'étiquette d'index à l'intérieur des crochets suivants .loc.

df.loc['Penelope']

Cela renvoie la ligne de données sous forme de série

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

Sélection de plusieurs lignes avec .loc avec une liste de chaînes

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

Cela renvoie un DataFrame avec les lignes dans l'ordre spécifié dans la liste:

entrez la description de l'image ici

Sélection de plusieurs lignes avec .loc avec notation par tranche

La notation de tranche est définie par des valeurs de début, d'arrêt et de pas. Lors du découpage par étiquette, pandas inclut la valeur d'arrêt dans le retour. Les tranches suivantes d'Aaron à Dean, inclusivement. Sa taille de pas n'est pas explicitement définie mais définie par défaut sur 1.

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

entrez la description de l'image ici

Les tranches complexes peuvent être prises de la même manière que les listes Python.

.iloc sélectionne les données uniquement par emplacement entier

Passons maintenant à .iloc. Chaque ligne et colonne de données dans un DataFrame a un emplacement entier qui le définit. Ceci s'ajoute au libellé affiché visuellement dans la sortie . L'emplacement entier est simplement le nombre de lignes / colonnes à partir du haut / gauche commençant à 0.

Vous pouvez utiliser trois entrées différentes pour .iloc

  • Un nombre entier
  • Une liste d'entiers
  • Notation de tranche utilisant des entiers comme valeurs de début et de fin

Sélection d'une seule ligne avec .iloc avec un entier

df.iloc[4]

Cela renvoie la 5ème ligne (emplacement entier 4) en tant que série

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

Sélection de plusieurs lignes avec .iloc avec une liste d'entiers

df.iloc[[2, -2]]

Cela renvoie un DataFrame des troisième et avant-dernière lignes:

entrez la description de l'image ici

Sélection de plusieurs lignes avec .iloc avec notation par tranche

df.iloc[:5:3]

entrez la description de l'image ici


Sélection simultanée de lignes et de colonnes avec .loc et .iloc

Une excellente capacité des deux .loc/.ilocest leur capacité à sélectionner simultanément les lignes et les colonnes. Dans les exemples ci-dessus, toutes les colonnes ont été renvoyées à partir de chaque sélection. Nous pouvons choisir des colonnes avec les mêmes types d'entrées que pour les lignes. Nous devons simplement séparer la sélection des lignes et des colonnes par une virgule .

Par exemple, nous pouvons sélectionner les lignes Jane et Dean avec juste la hauteur, le score et l'état des colonnes comme ceci:

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

entrez la description de l'image ici

Cela utilise une liste d'étiquettes pour les lignes et une notation de tranche pour les colonnes

Nous pouvons naturellement effectuer des opérations similaires en .ilocutilisant uniquement des entiers.

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

Sélection simultanée avec étiquettes et emplacement entier

.ixa été utilisé pour effectuer des sélections simultanément avec des étiquettes et un emplacement entier, ce qui était utile mais parfois déroutant et ambigu et heureusement, il a été déconseillé. Dans le cas où vous devez effectuer une sélection avec un mélange d'étiquettes et d'emplacements entiers, vous devrez effectuer à la fois vos étiquettes de sélections ou des emplacements entiers.

Par exemple, si nous voulons sélectionner des lignes Nicket Corneliaavec les colonnes 2 et 4, nous pourrions utiliser .locen convertissant les entiers en étiquettes avec ce qui suit:

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

Ou bien, convertissez les étiquettes d'index en entiers avec la get_locméthode d'index.

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

Sélection booléenne

L'indexeur .loc peut également effectuer une sélection booléenne. Par exemple, si nous voulons trouver toutes les lignes dont l'âge est supérieur à 30 ans et renvoyer uniquement les colonnes foodet, scorenous pouvons procéder comme suit:

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

Vous pouvez répliquer cela avec .ilocmais vous ne pouvez pas lui passer une série booléenne. Vous devez convertir la série booléenne en un tableau numpy comme celui-ci:

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

Sélection de toutes les lignes

Il est possible d'utiliser .loc/.ilocuniquement pour la sélection de colonnes. Vous pouvez sélectionner toutes les lignes en utilisant deux points comme ceci:

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

entrez la description de l'image ici


L'opérateur d'indexation,, []peut également sélectionner des lignes et des colonnes, mais pas simultanément.

La plupart des gens connaissent le but principal de l'opérateur d'indexation DataFrame, qui est de sélectionner des colonnes. Une chaîne sélectionne une seule colonne en tant que série et une liste de chaînes sélectionne plusieurs colonnes en tant que DataFrame.

df['food']

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

L'utilisation d'une liste sélectionne plusieurs colonnes

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

entrez la description de l'image ici

Ce que les gens connaissent moins, c'est que, lorsque la notation par tranche est utilisée, la sélection se fait par étiquettes de ligne ou par emplacement entier. C'est très déroutant et quelque chose que je n'utilise presque jamais, mais cela fonctionne.

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

entrez la description de l'image ici

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

entrez la description de l'image ici

L'explication de la .loc/.ilocsélection de lignes est hautement préférée. L'opérateur d'indexation seul ne peut pas sélectionner simultanément des lignes et des colonnes.

df[3:5, 'color']
TypeError: unhashable type: 'slice'
Ted Petrou
la source
6
Wow, c'était l'une des explications très bien articulées et lucides que j'ai jamais rencontrées sur un sujet de programmation, ce que vous avez expliqué dans le dernier à propos de l'indexation normale qui fonctionne soit sur les lignes soit sur les colonnes est l'une des raisons pour lesquelles nous avons loc et iloc méthode. Je suis tombé sur cette mise en garde dans le cours de datacamp. a.) Que renvoient df.columns et df.index? Est-ce une liste de chaînes? S'il s'agit d'une liste, est-il autorisé à accéder à deux éléments comme celui-ci df.columns [[2,4]] dans une liste? b.) Puis-je appeler get_loc () sur df.columns? c.) Pourquoi devons-nous appeler df ['age']> 30.valeurs en cas d'iloc.
pragun
Le meilleur répondeur que j'aie jamais vu.
max
C'est une très bonne réponse, j'ai aimé qu'il n'entre pas beaucoup dans ix, ce qui est obsolète et inutile de plonger profondément. Merci.
omabena