web-dev-qa-db-fra.com

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

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 ceux-ciréponses , mais je me trouve toujours incapable d'expliquer comment les trois sont différents. Pour moi, ils semblent en grande partie interchangeables, car ils se situent aux niveaux de découpage les plus bas.

Par exemple, supposons que nous voulions obtenir les cinq premières lignes d'un 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 utilisations est plus claire?

535
AZhao

Remarque: dans pandas versions 0.20.0 et supérieures, ix est obsolète et l'utilisation de loc et iloc est encouragé à la place. J'ai laissé les parties de cette réponse qui décrivent ix intact à titre de référence pour les utilisateurs des versions antérieures de pandas. Des exemples ont été ajoutés ci-dessous pour illustrer des alternatives à ix.


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

  • loc obtient les lignes (ou colonnes) avec des étiquettes particulières de l'index.
  • iloc obtient des lignes (ou des colonnes) à des positions particulières dans l'index (il ne prend donc que des entiers).
  • ix essaye généralement de se comporter comme loc mais retombe pour se comporter comme iloc si une étiquette n'est pas présente dans l'index.

Il est important de noter quelques subtilités qui peuvent rendre ix un peu délicat à utiliser:

  • si l'index est de type entier, ix n'utilisera que l'indexation basée sur une étiquette 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 seulement entiers, alors étant donné un entier, ix utilisera immédiatement l'indexation basée sur la position plutôt que l'indexation basée sur l'étiquette . Si, toutefois, ix se voit attribuer un autre type (par exemple, une chaîne), il peut utiliser l'indexation basée sur une étiquette.


Pour illustrer les différences entre les trois méthodes, considérons les séries suivantes:

_>>> 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 examiner le découpage avec la valeur entière _3_.

Dans ce cas, _s.iloc[:3]_ nous renvoie les 3 premières lignes (puisqu'il traite 3 en tant que position) et _s.loc[:3]_ nous renvoie les 8 premières lignes (puisqu'il traite 3 en tant qu'é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 que _s.loc[:3]_ car elle recherche d'abord l'étiquette plutôt que de travailler sur la position (et l'index pour s est de type entier).

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

Ici _s.iloc[:6]_ renvoie les 6 premières lignes de la série comme prévu. Toutefois, _s.loc[:6]_ déclenche une erreur KeyError puisque _6_ ne figure 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
_

Conformément aux subtilités mentionnées ci-dessus, _s.ix[:6]_ génère maintenant une erreur KeyError car elle tente de fonctionner comme loc mais ne trouve pas _6_ dans l'index. Comme notre index est de type entier ix, il ne se comporte pas comme iloc.

Cependant, si notre index était de type mixte, un entier ix se comporterait comme iloc immédiatement au lieu de générer une erreur 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
_

Gardez à l'esprit que ix peut toujours accepter des non-entiers et se comporter comme loc:

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

En règle générale, si vous n'indexez qu'à l'aide d'étiquettes ou d'indexation à l'aide de positions entières, respectez loc ou iloc pour éviter des résultats inattendus - essayez de ne pas utiliser ix.


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

Parfois, à l'aide d'un DataFrame, vous souhaiterez mélanger des méthodes d'indexation d'étiquette et de position pour les lignes et les colonnes.

Par exemple, considérons le DataFrame suivant. Quelle est la meilleure méthode pour découper les lignes jusqu'à et y compris 'c' et 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 la version.20.20) ix vous permet de le faire très précisément - nous pouvons découper les lignes par libellé et les colonnes par position (notez que pour les colonnes, ix utilisera par défaut le découpage basé sur la position car _4_ n'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 en utilisant iloc et en utilisant 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'indexation signifiant "obtenir la position du libellé dans cet index". Notez que puisque le découpage avec iloc exclut 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 .

820
Alex Riley

iloc fonctionne sur la base du positionnement entier. Ainsi, quels que soient vos libellés de lignes, vous pouvez toujours, par exemple, obtenir la première ligne en faisant

df.iloc[0]

ou les cinq derniers rangs en faisant

df.iloc[-5:]

Vous pouvez également l'utiliser sur les colonnes. Ceci 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)

Par contre, .loc utilise des index nommés. Configurons un cadre de données avec des chaînes en tant qu'étiquettes de lignes et de colonnes:

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

Ensuite, nous pouvons obtenir la première ligne par

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

et les deux autres lignes de la colonne 'date' par

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

etc. Maintenant, il est probablement utile de préciser que les index de ligne et de colonne par défaut pour un DataFrame sont des entiers de 0 et que, dans ce cas, iloc et loc 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 dates-heures, df.loc[:5] provoquerait une erreur .

En outre, vous pouvez récupérer les colonnes simplement en utilisant le __getitem__ du bloc de données:

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 des lignes et des positions sur des colonnes (pour préciser, sélectionnez un cadre de données, plutôt que de créer un cadre de données avec des chaînes dans l'index des lignes et des entiers dans l'index de colonne). C'est ici que .ix entre:

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

Je pense qu'il est également intéressant de mentionner que vous pouvez également transmettre des vecteurs booléens à la méthode loc. Par exemple:

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

Retourne les 1ère et 3ème rangées de df. Ceci équivaut à df[b] pour la sélection, mais il peut également être utilisé pour affecter des vecteurs booléens:

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

À mon avis, la réponse acceptée est source de confusion, car elle utilise un DataFrame avec uniquement des valeurs manquantes. Je n'aime pas non plus le terme basé sur la position pour _.iloc_ et préfère plutôt l'emplacement entier comme c'est beaucoup plus descriptif et exactement ce que _.iloc_ représente. Le mot clé est INTEGER - _.iloc_ nécessite INTEGERS.

Voir mon extrêmement détaillé série de blogs sur la sélection de sous-ensemble pour plus


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

Puisque _.ix_ est obsolète, nous nous concentrerons uniquement sur les différences entre _.loc_ et _.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'])
_

enter image description here

Tous les mots en gras en gras sont les étiquettes. Les étiquettes, age, color, food, height, score et state sont utilisées pour le colonnes . Les autres étiquettes, Jane, Nick, Aaron, Penelope, Dean, Christina, Cornelia sont utilisées pour le index .


Les méthodes principales de sélection de lignes particulières dans un DataFrame sont les indexeurs _.loc_ et _.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 jeu de crochets qui suit immédiatement son nom pour effectuer ses sélections.

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

Nous allons d’abord parler de l’indexeur _.loc_ qui ne sélectionne que les données en fonction des étiquettes d’index ou de colonne. Dans notre exemple DataFrame, nous avons fourni des noms significatifs en tant que valeurs pour l'index. Beaucoup de DataFrames n'auront aucun nom significatif et utiliseront par défaut uniquement les entiers compris entre 0 et 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 par tranches utilisant des chaînes comme valeurs de départ et d'arrêt

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

Pour sélectionner une seule ligne de données, placez l'index à l'intérieur des crochets après _.loc_.

_df.loc['Penelope']
_

Ceci retourne la ligne de données en tant que 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:

enter image description here

Sélection de plusieurs lignes avec .loc avec notation slice

La notation de tranche est définie par les valeurs de départ, d'arrêt et d'étape. Lors du découpage par étiquette, pandas inclut la valeur d'arrêt dans la déclaration. Les tranches suivantes d'Aaron à Dean, inclus. Sa taille de pas n'est pas explicitement définie mais définie par défaut à 1.

_df.loc['Aaron':'Dean']
_

enter image description here

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 d'un DataFrame a un emplacement entier qui le définit. Ceci est en plus de l'étiquette qui est affichée visuellement dans la sortie . L'emplacement entier est simplement le nombre de lignes/colonnes du haut/gauche commençant à 0.

Vous pouvez utiliser trois entrées différentes pour _.iloc_

  • Un nombre entier
  • Une liste d'entiers
  • Notation par tranches utilisant des entiers comme valeurs de départ et d'arrêt

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

_df.iloc[4]
_

Ceci retourne 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 retourne un DataFrame de la troisième et dernière ligne:

enter image description here

Sélection de plusieurs lignes avec .iloc avec notation slice

_df.iloc[:5:3]
_

enter image description here


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

Une excellente capacité des deux _.loc/.iloc_ est leur capacité à sélectionner simultanément des lignes et des colonnes. Dans les exemples ci-dessus, toutes les colonnes ont été renvoyées pour 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 uniquement la hauteur, le score et l’état des colonnes, comme suit:

_df.loc[['Jane', 'Dean'], 'height':]
_

enter image description here

Ceci utilise une liste d'étiquettes pour les lignes et la notation de tranche pour les colonnes.

Nous pouvons naturellement faire des opérations similaires avec _.iloc_ en utilisant 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

_.ix_ a été utilisé pour faire des sélections simultanément avec des étiquettes et un emplacement entier, ce qui était utile mais parfois déroutant et ambigu. Heureusement, il est déconseillé. Si vous devez faire une sélection avec une combinaison d’étiquettes et d’emplacements d’entiers, vous devrez créer les deux étiquettes de sélection ou les emplacements d’entiers.

Par exemple, si nous voulons sélectionner les lignes Nick et Cornelia avec les colonnes 2 et 4, nous pourrions utiliser _.loc_ en convertissant les entiers en étiquettes avec les éléments suivants:

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

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

_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 souhaitons rechercher toutes les lignes dont l'âge est supérieur à 30 ans et ne renvoyer que les colonnes food et score, nous pouvons procéder comme suit:

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

Vous pouvez répliquer ceci avec _.iloc_ mais vous ne pouvez pas lui transmettre une série booléenne. Vous devez convertir la série booléenne dans un tableau numpy comme ceci:

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

Sélection de toutes les lignes

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

_df.loc[:, 'color':'score':2]
_

enter image description here


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

La plupart des utilisateurs sont familiarisés avec l'objectif principal de l'opérateur d'indexation DataFrame, qui consiste à 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
_

Utiliser une liste sélectionne plusieurs colonnes

_df[['food', 'score']]
_

enter image description here

Ce que les gens connaissent moins, c’est que, lorsque la notation par tranches est utilisée, la sélection se fait par libellés de lignes ou par emplacement d’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
_

enter image description here

_df[2:6:2] # slice rows by integer location
_

enter image description here

Le caractère explicite de _.loc/.iloc_ pour la sélection de lignes est hautement préféré. L'opérateur d'indexation seul ne peut pas sélectionner des lignes et des colonnes simultanément.

_df[3:5, 'color']
TypeError: unhashable type: 'slice'
_
95
Ted Petrou