pandas loc contre iloc contre ix contre at contre iat?

171

Récemment, j'ai commencé à se diversifier de mon endroit sûr (R) vers Python et je suis un peu confus par la localisation / sélection des cellules dans Pandas. J'ai lu la documentation mais j'ai du mal à comprendre les implications pratiques des différentes options de localisation / sélection.

  • Y a-t-il une raison pour laquelle je devrais utiliser .locou .ilocsur l'option la plus générale .ix?
  • Je comprends que .loc, iloc, atet iatpeut fournir une certaine exactitude garantie que .ixne peut pas offrir, mais je l' ai lu aussi où a .ixtendance à être la solution la plus rapide dans tous les domaines .
  • Veuillez expliquer le raisonnement des meilleures pratiques dans le monde réel derrière l'utilisation de quelque chose d'autre que .ix?
griffonnages
la source
3
locest une indexation basée sur les étiquettes, donc la recherche d'une valeur dans une ligne, ilocest une indexation basée sur des lignes entières, ixest une méthode générale qui exécute d'abord basée sur les étiquettes, si cela échoue, elle passe à base d'entiers. atest obsolète et il est conseillé de ne plus l'utiliser. L'autre chose à considérer est ce que vous essayez de faire car certaines de ces méthodes permettent le découpage et l'affectation des colonnes, pour être honnête, les documents sont assez clairs: pandas.pydata.org/pandas-docs/stable/indexing.html
EdChum
1
@EdChum - qu'est-ce qui vous fait dire que atc'est obsolète? Je ne le vois pas dans les documents at (ou iat ).
Russ
1
C'est une erreur qui n'est pas obsolète, je pense qu'il a été question de le désapprouver mais cette idée a été abandonnée parce que je pense que c'est plus rapide
EdChum
4
Explication détaillée entre loc, ixet ilocici: stackoverflow.com/questions/31593201/…
Alex Riley

Réponses:

142

loc: fonctionne uniquement sur l'index
iloc: travaille sur la position
ix: Vous pouvez obtenir des données de dataframe sans qu'elles soient dans l'index
à: obtenir des valeurs scalaires. C'est une
localisation très rapide : obtenir des valeurs scalaires. C'est un iloc très rapide

http://pyciencia.blogspot.com/2015/05/obtener-y-filtrar-datos-de-un-dataframe.html

Remarque: à partir de pandas 0.20.0, l' .ixindexeur est obsolète au profit des indexeurs .ilocet plus stricts .loc.

Lautremont
la source
9
Si atet iatsont des versions très rapides de locet iloc, alors pourquoi utiliser locet pas ilocdu tout?
Ray le
57
atet iatun destiné à accéder à un scalaire, c'est-à-dire à un seul élément de la trame de données, tandis que locet ilocsont destinés à accéder à plusieurs éléments en même temps, potentiellement pour effectuer des opérations vectorisées.
ncasas
@ncasas - si je lis bien la documentation .at ne peut accéder que par index tandis que .loc peut également accéder par nom de colonne. Existe-t-il un moyen d'utiliser le .at plus rapide mais d'utiliser le nom de la colonne au lieu d'un index? Comme remplacer x = df.loc [df.Id == source_Id, 'someValue']. ​​Values ​​[0] par x = df.at [df.Id == source_Id, 'someValue']. La version avec .at jette "ValueError: At based indexing on an integer index can only have integer indexers"
Vega
94

Mis à jour pour une pandas 0.20donnée ixobsolète. Cela démontre non seulement comment utiliser loc, iloc, at, iat, set_value, mais comment accomplir, l' indexation en fonction de position / étiquette mixte.


loc- basé sur les étiquettes
Vous permet de passer des tableaux 1-D comme indexeurs. Les tableaux peuvent être des tranches (sous-ensembles) de l'index ou de la colonne, ou ils peuvent être des tableaux booléens qui sont égaux en longueur à l'index ou aux colonnes.

Remarque spéciale: lorsqu'un indexeur scalaire est passé, locpeut attribuer un nouvel index ou une nouvelle valeur de colonne qui n'existait pas auparavant.

# label based, but we can use position values
# to get the labels from the index object
df.loc[df.index[2], 'ColName'] = 3

df.loc[df.index[1:3], 'ColName'] = 3

iloc- basé sur la position
Similaire à locsauf avec des positions plutôt que des valeurs d'index. Cependant, vous ne pouvez pas affecter de nouvelles colonnes ou index.

# position based, but we can get the position
# from the columns object via the `get_loc` method
df.iloc[2, df.columns.get_loc('ColName')] = 3

df.iloc[2, 4] = 3

df.iloc[:3, 2:4] = 3

at- basé sur des étiquettes
Fonctionne très similaire à locpour les indexeurs scalaires. Impossible de fonctionner sur les indexeurs de tableau. Pouvez! attribuer de nouveaux index et colonnes.

Avantage sur locest que c'est plus rapide.
L'inconvénient est que vous ne pouvez pas utiliser de tableaux pour les indexeurs.

# label based, but we can use position values
# to get the labels from the index object
df.at[df.index[2], 'ColName'] = 3

df.at['C', 'ColName'] = 3

iat- basé sur la position
Fonctionne de manière similaire à iloc. Impossible de fonctionner dans les indexeurs de tableau. Ne peux pas! attribuer de nouveaux index et colonnes.

Avantage sur ilocest que c'est plus rapide.
L'inconvénient est que vous ne pouvez pas utiliser de tableaux pour les indexeurs.

# position based, but we can get the position
# from the columns object via the `get_loc` method
IBM.iat[2, IBM.columns.get_loc('PNL')] = 3

set_value- basé sur des étiquettes
Fonctionne très similaire à locpour les indexeurs scalaires. Impossible de fonctionner sur les indexeurs de tableau. Pouvez! attribuer de nouveaux index et colonnes

Avantage Super rapide, car il y a très peu de frais généraux!
Inconvénient Il y a très peu de frais généraux car il pandasne fait pas un tas de contrôles de sécurité. Utilisez à vos risques et périls . En outre, cela n'est pas destiné à un usage public.

# label based, but we can use position values
# to get the labels from the index object
df.set_value(df.index[2], 'ColName', 3)

set_valuewithtakable=True - basé sur la position
Fonctionne de la même manière queiloc. Impossible de fonctionner dans les indexeurs de tableau. Ne peux pas! attribuer de nouveaux index et colonnes.

Avantage Super rapide, car il y a très peu de frais généraux!
Inconvénient Il y a très peu de frais généraux car il pandasne fait pas un tas de contrôles de sécurité. Utilisez à vos risques et périls . En outre, cela n'est pas destiné à un usage public.

# position based, but we can get the position
# from the columns object via the `get_loc` method
df.set_value(2, df.columns.get_loc('ColName'), 3, takable=True)
piRSquared
la source
Alors, existe-t-il un moyen simple de lire / définir plusieurs colonnes par position? De plus, disons, je voulais ajouter un tableau de valeurs chacune dans de nouvelles colonnes, est-ce facile?
Wordsmith
@wordsmith il existe des moyens simples d'ajouter de nouvelles colonnes à la fin du dataframe. Ou même le début. Si les positions sont impliquées, non, il n'y a pas de moyen facile.
piRSquared
Cette réponse était exactement ce dont j'avais besoin! Pandas est certainement puissant, mais cela se fait au détriment de tout rendre extrêmement compliqué à comprendre et à reconstituer.
slhck
1
Notez que set_valuea été obsolète en faveur de .atet .iatdepuis la version 0.21
nedned
59

Les pandas peuvent effectuer des sélections à partir d'un DataFrame de deux manières principales.

  • Par étiquette
  • Par emplacement entier

La documentation utilise le terme position pour désigner l' emplacement des nombres entiers . Je n'aime pas cette terminologie car je trouve qu'elle prête à confusion. L'emplacement entier est plus descriptif et correspond exactement à ce que .ilocsignifie. Le mot clé ici est INTEGER - vous devez utiliser des entiers lors de la sélection par emplacement entier.

Avant d'afficher le résumé, assurons-nous tous que ...

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

Il existe trois indexeurs principaux pour les pandas. Nous avons l'opérateur d'indexation lui-même (les crochets []) .loc, et .iloc. Résumons-les:

  • []- Sélectionne principalement des sous-ensembles de colonnes, mais peut également sélectionner des lignes. Impossible de sélectionner simultanément des lignes et des colonnes.
  • .loc - sélectionne les sous-ensembles de lignes et de colonnes par étiquette uniquement
  • .iloc - sélectionne les sous-ensembles de lignes et de colonnes par emplacement entier uniquement

Je n'utilise presque jamais .atou .iatcar ils n'ajoutent aucune fonctionnalité supplémentaire et avec juste une petite augmentation des performances. Je découragerais leur utilisation à moins que vous n'ayez une application très urgente. Quoi qu'il en soit, nous avons leur résumé:

  • .at sélectionne une seule valeur scalaire dans le DataFrame par étiquette uniquement
  • .iat sélectionne une seule valeur scalaire dans le DataFrame par emplacement entier uniquement

En plus de la sélection par étiquette et emplacement entier, il existe une sélection booléenne également appelée indexation booléenne .


Exemples expliquant .loc, .iloc, la sélection booléenne et .atet .iatsont présentés ci - dessous

Nous nous concentrerons d'abord 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 ligne. 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 types d' étiquettes, Jane, Nick, Aaron, Penelope, Dean, Christina, Corneliasont utilisés comme étiquettes pour les lignes. Collectivement, ces étiquettes de ligne sont appelées index .


Les principaux moyens de sélectionner des lignes particulières dans un DataFrame sont 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 le moment. En outre, 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 libellés

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

Il existe de nombreuses entrées différentes que vous pouvez utiliser pour .loctrois d'entre elles sont

  • 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 de 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, les pandas incluent la valeur d'arrêt dans le retour. Les tranches suivantes d'Aaron à Dean, inclus. Sa taille de pas n'est pas explicitement définie mais par défaut à 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. Cela s'ajoute à l'étiquette qui s'affiche visuellement dans la sortie. L'emplacement entier est simplement le nombre de lignes / colonnes du haut / gauche commençant à 0.

Il existe de nombreuses entrées différentes que vous pouvez utiliser pour .iloctrois d'entre elles sont

  • 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) sous forme de 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 de 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 des lignes et des 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 nous le faisons pour les lignes. Nous devons simplement séparer la sélection de ligne et de colonne 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 ceci:

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

entrez la description de l'image ici

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

Nous pouvons naturellement faire des opérations similaires en .ilocn'utilisant que 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 faire des sélections simultanément avec les étiquettes et l'emplacement des nombres entiers, ce qui était utile mais parfois déroutant et ambigu et heureusement, il a été déconseillé. Dans le cas où vous auriez besoin de faire une sélection avec un mélange d'étiquettes et d'emplacements entiers, vous devrez effectuer à la fois vos étiquettes de sélections ou 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 souhaitons trouver toutes les lignes où l'âge est supérieur à 30 ans et ne renvoyer que les colonnes foodet, scorenous pouvons faire ce qui suit:

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

Vous pouvez le reproduire avec .ilocmais vous ne pouvez pas lui transmettre une série booléenne. Vous devez convertir la série booléenne en un tableau numpy comme ceci:

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

Sélection de toutes les lignes

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

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

entrez la description de l'image ici


L'opérateur d'indexation,, []can slice 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 sont moins familiers, c'est que, lorsque la notation de tranche est utilisée, la sélection se fait par des étiquettes de ligne ou par un 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 des lignes est fortement 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'

Sélection par .atet.iat

La sélection avec .atest presque identique à .locmais elle ne sélectionne qu'une seule «cellule» dans votre DataFrame. Nous appelons généralement cette cellule une valeur scalaire. Pour l'utiliser .at, transmettez-lui une étiquette de ligne et de colonne séparées par une virgule.

df.at['Christina', 'color']
'black'

La sélection avec .iatest presque identique à .ilocmais elle ne sélectionne qu'une seule valeur scalaire. Vous devez lui transmettre un entier pour les emplacements des lignes et des colonnes

df.iat[2, 5]
'FL'
Ted Petrou
la source
31
df = pd.DataFrame({'A':['a', 'b', 'c'], 'B':[54, 67, 89]}, index=[100, 200, 300])

df

                        A   B
                100     a   54
                200     b   67
                300     c   89
In [19]:    
df.loc[100]

Out[19]:
A     a
B    54
Name: 100, dtype: object

In [20]:    
df.iloc[0]

Out[20]:
A     a
B    54
Name: 100, dtype: object

In [24]:    
df2 = df.set_index([df.index,'A'])
df2

Out[24]:
        B
    A   
100 a   54
200 b   67
300 c   89

In [25]:    
df2.ix[100, 'a']

Out[25]:    
B    54
Name: (100, a), dtype: int64
Lydia
la source
4

Commençons par ce petit df:

import pandas as pd
import time as tm
import numpy as np
n=10
a=np.arange(0,n**2)
df=pd.DataFrame(a.reshape(n,n))

Nous aurons donc

df
Out[25]: 
        0   1   2   3   4   5   6   7   8   9
    0   0   1   2   3   4   5   6   7   8   9
    1  10  11  12  13  14  15  16  17  18  19
    2  20  21  22  23  24  25  26  27  28  29
    3  30  31  32  33  34  35  36  37  38  39
    4  40  41  42  43  44  45  46  47  48  49
    5  50  51  52  53  54  55  56  57  58  59
    6  60  61  62  63  64  65  66  67  68  69
    7  70  71  72  73  74  75  76  77  78  79
    8  80  81  82  83  84  85  86  87  88  89
    9  90  91  92  93  94  95  96  97  98  99

Avec cela, nous avons:

df.iloc[3,3]
Out[33]: 33

df.iat[3,3]
Out[34]: 33

df.iloc[:3,:3]
Out[35]: 
    0   1   2   3
0   0   1   2   3
1  10  11  12  13
2  20  21  22  23
3  30  31  32  33



df.iat[:3,:3]
Traceback (most recent call last):
   ... omissis ...
ValueError: At based indexing on an integer index can only have integer indexers

Ainsi, nous ne pouvons pas utiliser .iat pour le sous-ensemble, où nous devons utiliser uniquement .iloc.

Mais essayons les deux pour sélectionner un df plus grand et vérifions la vitesse ...

# -*- coding: utf-8 -*-
"""
Created on Wed Feb  7 09:58:39 2018

@author: Fabio Pomi
"""

import pandas as pd
import time as tm
import numpy as np
n=1000
a=np.arange(0,n**2)
df=pd.DataFrame(a.reshape(n,n))
t1=tm.time()
for j in df.index:
    for i in df.columns:
        a=df.iloc[j,i]
t2=tm.time()
for j in df.index:
    for i in df.columns:
        a=df.iat[j,i]
t3=tm.time()
loc=t2-t1
at=t3-t2
prc = loc/at *100
print('\nloc:%f at:%f prc:%f' %(loc,at,prc))

loc:10.485600 at:7.395423 prc:141.784987

Donc, avec .loc, nous pouvons gérer des sous-ensembles et avec .at seulement un seul scalaire, mais .at est plus rapide que .loc

:-)

Fabio Pomi
la source