Sélection de plusieurs colonnes dans une trame de données pandas


1113

J'ai des données dans différentes colonnes mais je ne sais pas comment les extraire pour les enregistrer dans une autre variable.

index  a   b   c
1      2   3   4
2      3   4   5

Comment puis-je sélectionner 'a', 'b'et de l' enregistrer pour DF1?

j'ai essayé

df1 = df['a':'b']
df1 = df.ix[:, 'a':'b']

Aucun ne semble fonctionner.


2
Vous ne voulez jamais l'utiliser .ixcar c'est ambigu. Utilisez .ilocou .locsi vous devez.
Acumenus

1
Existe-t-il un moyen de le faire sans faire référence aux noms d'en-tête? comme dans R, je peux le faire comme ceci: > csvtable_imp_1 <- csvtable_imp[0:6]et il sélectionne le montant delta des premières colonnes entre 0 et 6. Tout ce que j'avais à faire est de lire la table csv comme délimitée avec la bibliothèque readr.
MichaelR

J'ai travaillé un peu plus avec. J'ai trouvé quelque chose qui fonctionnait comme voulu. La valeur par défaut consiste à sélectionner le nombre de caractères et non les colonnes. infile_1 = largefile_stay.ix[:,0:6]
MichaelR

3
Pour ceux qui trébuchent sur cette fin, ixest désormais obsolète. Pandas recommande d'utiliser: loc(indexation basée sur les étiquettes) ou iloc(indexation basée sur la position).
ZaydH

Réponses:


1773

Les noms de colonne (qui sont des chaînes) ne peuvent pas être tranchés de la manière que vous avez essayée.

Ici, vous avez quelques options. Si vous savez à partir du contexte quelles variables vous souhaitez découper, vous pouvez simplement renvoyer une vue de ces colonnes uniquement en passant une liste dans la __getitem__syntaxe (les []).

df1 = df[['a','b']]

Alternativement, s'il est important de les indexer numériquement et non par leur nom (par exemple, votre code devrait le faire automatiquement sans connaître les noms des deux premières colonnes), vous pouvez le faire à la place:

df1 = df.iloc[:,0:2] # Remember that Python does not slice inclusive of the ending index.

De plus, vous devez vous familiariser avec l'idée d'une vue dans un objet Pandas par rapport à une copie de cet objet. La première des méthodes ci-dessus renverra une nouvelle copie en mémoire du sous-objet souhaité (les tranches souhaitées).

Parfois, cependant, il existe des conventions d'indexation dans Pandas qui ne le font pas et vous donnent à la place une nouvelle variable qui fait simplement référence au même bloc de mémoire que le sous-objet ou la tranche dans l'objet d'origine. Cela se produira avec la deuxième façon d'indexer, vous pouvez donc le modifier avec la copy()fonction pour obtenir une copie régulière. Lorsque cela se produit, changer ce que vous pensez être l'objet découpé en tranches peut parfois altérer l'objet d'origine. Toujours bon d'être à l'affût de cela.

df1 = df.iloc[0,0:2].copy() # To avoid the case where changing df1 also changes df

Pour l'utiliser iloc, vous devez connaître les positions des colonnes (ou indices). Comme les positions des colonnes peuvent changer, au lieu des indices de codage en dur, vous pouvez utiliser ilocavec la get_locfonction de la columnsméthode de l'objet de trame de données pour obtenir les indices des colonnes.

{df.columns.get_loc(c):c for idx, c in enumerate(df.columns)}

Maintenant, vous pouvez utiliser ce dictionnaire pour accéder aux colonnes par des noms et en utilisant iloc.


192
Remarque: df[['a','b']]produit une copie
Wes McKinney

1
Oui, c'était implicite dans ma réponse. Le bit sur la copie était uniquement destiné à être utilisé ix[]si vous préférez l' utiliser ix[]pour une raison quelconque.
le

1
ixindexe les lignes, pas les colonnes. Je pensais que l'OP voulait des colonnes.
plaques de cuisson

9
ixaccepte les arguments de tranche, vous pouvez donc également obtenir des colonnes. Par exemple, df.ix[0:2, 0:2]obtient le sous-tableau 2x2 supérieur gauche comme il le fait pour une matrice NumPy (en fonction des noms de vos colonnes bien sûr). Vous pouvez même utiliser la syntaxe de tranche sur les noms de chaîne des colonnes, comme df.ix[0, 'Col1':'Col5']. Cela obtient toutes les colonnes qui se trouvent être ordonnées entre Col1et Col5dans le df.columnstableau. Il est incorrect de dire que ixindexe les lignes. C'est juste son utilisation la plus élémentaire. Il prend également en charge beaucoup plus d'indexation que cela. Donc, ixest parfaitement général pour cette question.
le

7
@AndrewCassidy N'utilisez plus jamais .ix. Si vous souhaitez découper avec des entiers, utilisez .ilocce qui exclut la dernière position, tout comme les listes Python.
Ted Petrou

133

Depuis la version 0.11.0, les colonnes peuvent être découpées de la manière que vous avez essayé d'utiliser l' .locindexeur:

df.loc[:, 'C':'E']

est équivalent à

df[['C', 'D', 'E']]  # or df.loc[:, ['C', 'D', 'E']]

et les colonnes de retours Cpar E.


Une démo sur un DataFrame généré aléatoirement:

import pandas as pd
import numpy as np
np.random.seed(5)
df = pd.DataFrame(np.random.randint(100, size=(100, 6)), 
                  columns=list('ABCDEF'), 
                  index=['R{}'.format(i) for i in range(100)])
df.head()

Out: 
     A   B   C   D   E   F
R0  99  78  61  16  73   8
R1  62  27  30  80   7  76
R2  15  53  80  27  44  77
R3  75  65  47  30  84  86
R4  18   9  41  62   1  82

Pour obtenir les colonnes de C à E (notez que contrairement au découpage d'entiers, 'E' est inclus dans les colonnes):

df.loc[:, 'C':'E']

Out: 
      C   D   E
R0   61  16  73
R1   30  80   7
R2   80  27  44
R3   47  30  84
R4   41  62   1
R5    5  58   0
...

Il en va de même pour la sélection de lignes en fonction des étiquettes. Obtenez les lignes «R6» à «R10» à partir de ces colonnes:

df.loc['R6':'R10', 'C':'E']

Out: 
      C   D   E
R6   51  27  31
R7   83  19  18
R8   11  67  65
R9   78  27  29
R10   7  16  94

.locaccepte également un tableau booléen afin que vous puissiez sélectionner les colonnes dont l'entrée correspondante dans le tableau est True. Par exemple, df.columns.isin(list('BCD'))renvoie array([False, True, True, True, False, False], dtype=bool)- True si le nom de la colonne est dans la liste ['B', 'C', 'D']; Faux, sinon.

df.loc[:, df.columns.isin(list('BCD'))]

Out: 
      B   C   D
R0   78  61  16
R1   27  30  80
R2   53  80  27
R3   65  47  30
R4    9  41  62
R5   78   5  58
...

110

En supposant que les noms de vos colonnes ( df.columns) le sont ['index','a','b','c'], les données que vous souhaitez se trouvent dans les 3e et 4e colonnes. Si vous ne connaissez pas leurs noms lors de l'exécution de votre script, vous pouvez le faire

newdf = df[df.columns[2:4]] # Remember, Python is 0-offset! The "3rd" entry is at slot 2.

Comme le souligne EMS dans sa réponse , les df.ixcolonnes de tranches sont un peu plus concises, mais l' .columnsinterface de découpage pourrait être plus naturelle car elle utilise la syntaxe d'indexation / découpage de liste python 1-D vanilla.

WARN: 'index'est un mauvais nom pour une DataFramecolonne. Cette même étiquette est également utilisée pour l' df.indexattribut réel , un Indextableau. Votre colonne est donc renvoyée par df['index']et le véritable index DataFrame est renvoyé par df.index. An Indexest un type spécial d' Seriesoptimisation pour la recherche des valeurs de ses éléments. Pour df.index, il s'agit de rechercher des lignes par leur étiquette. Cet df.columnsattribut est également un pd.Indextableau, pour rechercher des colonnes par leurs étiquettes.


3
Comme je l'ai noté dans mon commentaire ci-dessus, ce .ixn'est pas seulement pour les lignes. Il est destiné à un découpage à usage général et peut être utilisé pour un découpage multidimensionnel. Il s'agit essentiellement d'une interface avec la __getitem__syntaxe habituelle de NumPy . Cela dit, vous pouvez facilement convertir un problème de colonne trancher un problème rangée découpage en tranches par la simple application d' une opération de transposition, df.T. Votre exemple utilise columns[1:3], ce qui est un peu trompeur. Le résultat de columnsest un Series; veillez à ne pas le traiter comme un tableau. En outre, vous devriez probablement le modifier columns[2:3]pour correspondre à votre commentaire "3e et 4e".
le

@ Mr.F: Mon [2:4]est correct. Tu [2:3]as tort. Et l'utilisation de la notation de découpage python standard pour générer une séquence / série n'est pas trompeuse IMO. Mais j'aime votre contournement de l'interface DataFrame pour accéder au tableau numpy sous-jacent avec ix.
plaques de cuisson

Vous avez raison dans ce cas, mais le point que j'essayais de faire valoir est qu'en général, le découpage avec des étiquettes dans Pandas inclut le point de terminaison de la tranche (ou du moins c'était le comportement dans la plupart des versions précédentes de Pandas). Donc, si vous récupérez df.columnset souhaitez le découper par étiquette , vous aurez alors une sémantique de découpage différente que si vous le découpiez par position d'index entier . Mais je ne l'ai certainement pas bien expliqué dans mon commentaire précédent.
le

Ahh, maintenant je vois votre point. J'ai oublié qu'il columnss'agit d'une série immuable et le getter a été remplacé pour utiliser des étiquettes comme indices. Merci d'avoir pris le temps de clarifier.
plaques de cuisson

2
Notez l'avertissement de dépréciation: .ix est déconseillé. Par conséquent, cela a du sens: newdf = df [df.columns [2: 4]]
Martien Lubberink

64
In [39]: df
Out[39]: 
   index  a  b  c
0      1  2  3  4
1      2  3  4  5

In [40]: df1 = df[['b', 'c']]

In [41]: df1
Out[41]: 
   b  c
0  3  4
1  4  5

1
Et si je voulais renommer la colonne, par exemple quelque chose comme: df[['b as foo', 'c as bar']telle que la sortie renomme la colonne en btant que fooet la colonne en ctant que bar?
kuanb

5
df[['b', 'c']].rename(columns = {'b' : 'foo', 'c' : 'bar'})
Greg

62

Je me rends compte que cette question est assez ancienne, mais dans la dernière version de pandas, il existe un moyen facile de faire exactement cela. Les noms de colonnes (qui sont des chaînes) peuvent être tranchés comme vous le souhaitez.

columns = ['b', 'c']
df1 = pd.DataFrame(df, columns=columns)

6
Cela ne peut être fait qu'à la création. La question est de savoir si vous l'avez déjà dans une trame de données.
Banjocat

3
@Banjocat, il fonctionne avec une trame de données existante
mhery

23

Vous pouvez fournir une liste de colonnes à supprimer et renvoyer le DataFrame avec uniquement les colonnes nécessaires à l'aide de la drop()fonction sur un Pandas DataFrame.

Je dis juste

colsToDrop = ['a']
df.drop(colsToDrop, axis=1)

retournerait un DataFrame avec juste les colonnes bet c.

La dropméthode est documentée ici .


23

Avec les pandas,

avec des noms de colonne

dataframe[['column1','column2']]

pour sélectionner par iloc et colonnes spécifiques avec numéro d'index:

dataframe.iloc[:,[1,2]]

avec les noms des colonnes loc peuvent être utilisés comme

dataframe.loc[:,['column1','column2']]

20

J'ai trouvé cette méthode très utile:

# iloc[row slicing, column slicing]
surveys_df.iloc [0:3, 1:4]

Plus de détails peuvent être trouvés ici


Comment prendriez-vous, disons, uniquement les colonnes 2 et 5?
324

1
Ce serait surveys_df.iloc [:, [2,5]]alors.
Julian Gorfer

15

À partir de la version 0.21.0, l'utilisation .locou []une liste avec une ou plusieurs étiquettes manquantes est déconseillée .reindex. Donc, la réponse à votre question est:

df1 = df.reindex(columns=['b','c'])

Dans les versions précédentes, l'utilisation .loc[list-of-labels]fonctionnait aussi longtemps qu'au moins une des clés était trouvée (sinon elle soulèverait a KeyError). Ce comportement est obsolète et affiche désormais un message d'avertissement. L'alternative recommandée est d'utiliser .reindex().

En savoir plus sur Indexation et sélection de données


10

Vous pouvez utiliser des pandas. Je crée le DataFrame:

    import pandas as pd
    df = pd.DataFrame([[1, 2,5], [5,4, 5], [7,7, 8], [7,6,9]], 
                      index=['Jane', 'Peter','Alex','Ann'],
                      columns=['Test_1', 'Test_2', 'Test_3'])

Le DataFrame:

           Test_1  Test_2  Test_3
    Jane        1       2       5
    Peter       5       4       5
    Alex        7       7       8
    Ann         7       6       9

Pour sélectionner une ou plusieurs colonnes par nom:

    df[['Test_1','Test_3']]

           Test_1  Test_3
    Jane        1       5
    Peter       5       5
    Alex        7       8
    Ann         7       9

Vous pouvez aussi utiliser:

    df.Test_2

Et vous obtenez la colonne Test_2

    Jane     2
    Peter    4
    Alex     7
    Ann      6

Vous pouvez également sélectionner des colonnes et des lignes à partir de ces lignes à l'aide de .loc(). C'est ce qu'on appelle le "tranchage" . Notez que je prends de la colonne Test_1àTest_3

    df.loc[:,'Test_1':'Test_3']

La "tranche" est:

            Test_1  Test_2  Test_3
     Jane        1       2       5
     Peter       5       4       5
     Alex        7       7       8
     Ann         7       6       9

Et si vous voulez juste Peteret Anndes colonnes Test_1et Test_3:

    df.loc[['Peter', 'Ann'],['Test_1','Test_3']]

Vous obtenez:

           Test_1  Test_3
    Peter       5       5
    Ann         7       9

8

Si vous voulez obtenir un élément par index de ligne et nom de colonne, vous pouvez le faire comme df['b'][0]. C'est aussi simple que vous pouvez imaginer.

Ou vous pouvez utiliser df.ix[0,'b']une utilisation mixte d'index et d'étiquette.

Remarque: Depuis v0.20 ixa été déconseillé en faveur de loc/ iloc.


6

Une approche différente et facile: itérer les lignes

en utilisant des iterows

 df1= pd.DataFrame() #creating an empty dataframe
 for index,i in df.iterrows():
    df1.loc[index,'A']=df.loc[index,'A']
    df1.loc[index,'B']=df.loc[index,'B']
    df1.head()

5
Veuillez ne pas recommander l'utilisation de iterrows (). C'est un catalyseur flagrant du pire anti-modèle de l'histoire des pandas.
cs95

Pourriez-vous expliquer ce que vous entendez par «pire anti-modèle»?
Ankita

1
À mon humble avis, iterrows () devrait être la dernière option lors de l'utilisation de pandas.
Elf

5

Les différentes approches discutées dans les réponses ci-dessus sont basées sur l'hypothèse que l'utilisateur sait que les indices de colonne doivent être supprimés ou sous-définis, ou que l'utilisateur souhaite sous-définir une trame de données en utilisant une plage de colonnes (par exemple entre «C»: «E») . pandas.DataFrame.drop () est certainement une option pour sous-définir les données sur la base d'une liste de colonnes définies par l'utilisateur (bien que vous deviez être prudent de toujours utiliser une copie de la trame de données et les paramètres inplace ne devraient pas être définis sur True !!)

Une autre option consiste à utiliser pandas.columns.difference () , qui fait une différence définie sur les noms de colonnes et renvoie un type d'index de tableau contenant les colonnes souhaitées. Voici la solution:

df = pd.DataFrame([[2,3,4],[3,4,5]],columns=['a','b','c'],index=[1,2])
columns_for_differencing = ['a']
df1 = df.copy()[df.columns.difference(columns_for_differencing)]
print(df1)

La sortie serait: b c 1 3 4 2 4 5


1
La copie () n'est pas nécessaire. c'est-à-dire: df1 = df[df.columns.difference(columns_for_differencing)]renverra une nouvelle trame de données / copiée. Vous pourrez modifier df1sans altérer df. Merci btw. C'était exactement ce dont j'avais besoin.
Bazyli Debowski

4

vous pouvez également utiliser df.pop ()

>>> df = pd.DataFrame([('falcon', 'bird',    389.0),
...                    ('parrot', 'bird',     24.0),
...                    ('lion',   'mammal',   80.5),
...                    ('monkey', 'mammal', np.nan)],
...                   columns=('name', 'class', 'max_speed'))
>>> df
     name   class  max_speed
0  falcon    bird      389.0
1  parrot    bird       24.0
2    lion  mammal       80.5
3  monkey  mammal 

>>> df.pop('class')
0      bird
1      bird
2    mammal
3    mammal
Name: class, dtype: object

>>> df
     name  max_speed
0  falcon      389.0
1  parrot       24.0
2    lion       80.5
3  monkey        NaN

faites-moi savoir si cela vous est utile, veuillez utiliser df.pop (c)


3

J'ai vu plusieurs réponses à ce sujet, mais je suis resté peu clair. Comment choisiriez-vous ces colonnes d'intérêt? La réponse à cela est que si vous les avez réunis dans une liste, vous pouvez simplement référencer les colonnes à l'aide de la liste.

Exemple

print(extracted_features.shape)
print(extracted_features)

(63,)
['f000004' 'f000005' 'f000006' 'f000014' 'f000039' 'f000040' 'f000043'
 'f000047' 'f000048' 'f000049' 'f000050' 'f000051' 'f000052' 'f000053'
 'f000054' 'f000055' 'f000056' 'f000057' 'f000058' 'f000059' 'f000060'
 'f000061' 'f000062' 'f000063' 'f000064' 'f000065' 'f000066' 'f000067'
 'f000068' 'f000069' 'f000070' 'f000071' 'f000072' 'f000073' 'f000074'
 'f000075' 'f000076' 'f000077' 'f000078' 'f000079' 'f000080' 'f000081'
 'f000082' 'f000083' 'f000084' 'f000085' 'f000086' 'f000087' 'f000088'
 'f000089' 'f000090' 'f000091' 'f000092' 'f000093' 'f000094' 'f000095'
 'f000096' 'f000097' 'f000098' 'f000099' 'f000100' 'f000101' 'f000103']

J'ai la liste suivante / tableau numpy extracted_features, spécifiant 63 colonnes. L'ensemble de données d'origine comporte 103 colonnes, et j'aimerais extraire exactement celles-ci, puis j'utiliserais

dataset[extracted_features]

Et vous vous retrouverez avec cela

entrez la description de l'image ici

C'est quelque chose que vous utiliseriez assez souvent dans Machine Learning (plus précisément, dans la sélection des fonctionnalités). Je voudrais également discuter d'autres façons, mais je pense que cela a déjà été couvert par d'autres stackoverflowers. J'espère que cela vous a été utile!


2

Vous pouvez utiliser une pandas.DataFrame.filterméthode pour filtrer ou réorganiser les colonnes comme ceci:

df1 = df.filter(['a', 'b'])

0
df[['a','b']] # select all rows of 'a' and 'b'column 
df.loc[0:10, ['a','b']] # index 0 to 10 select column 'a' and 'b'
df.loc[0:10, ['a':'b']] # index 0 to 10 select column 'a' to 'b'
df.iloc[0:10, 3:5] # index 0 to 10 and column 3 to 5
df.iloc[3, 3:5] # index 3 of column 3 to 5
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.