pandas: meilleure façon de sélectionner toutes les colonnes dont les noms commencent par X


104

J'ai un DataFrame:

import pandas as pd
import numpy as np

df = pd.DataFrame({'foo.aa': [1, 2.1, np.nan, 4.7, 5.6, 6.8],
                   'foo.fighters': [0, 1, np.nan, 0, 0, 0],
                   'foo.bars': [0, 0, 0, 0, 0, 1],
                   'bar.baz': [5, 5, 6, 5, 5.6, 6.8],
                   'foo.fox': [2, 4, 1, 0, 0, 5],
                   'nas.foo': ['NA', 0, 1, 0, 0, 0],
                   'foo.manchu': ['NA', 0, 0, 0, 0, 0],})

Je souhaite sélectionner des valeurs de 1 dans les colonnes commençant par foo.. Y a-t-il une meilleure façon de le faire autre que:

df2 = df[(df['foo.aa'] == 1)|
(df['foo.fighters'] == 1)|
(df['foo.bars'] == 1)|
(df['foo.fox'] == 1)|
(df['foo.manchu'] == 1)
]

Quelque chose de similaire à écrire quelque chose comme:

df2= df[df.STARTS_WITH_FOO == 1]

La réponse doit imprimer un DataFrame comme celui-ci:

   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      5.0     1.0         0             0        2         NA      NA
1      5.0     2.1         0             1        4          0       0
2      6.0     NaN         0           NaN        1          0       1
5      6.8     6.8         1             0        5          0       0

[4 rows x 7 columns]

Réponses:


151

Effectuez simplement une compréhension de liste pour créer vos colonnes:

In [28]:

filter_col = [col for col in df if col.startswith('foo')]
filter_col
Out[28]:
['foo.aa', 'foo.bars', 'foo.fighters', 'foo.fox', 'foo.manchu']
In [29]:

df[filter_col]
Out[29]:
   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
3     4.7         0             0        0          0
4     5.6         0             0        0          0
5     6.8         1             0        5          0

Une autre méthode consiste à créer une série à partir des colonnes et à utiliser la méthode str vectorisée startswith:

In [33]:

df[df.columns[pd.Series(df.columns).str.startswith('foo')]]
Out[33]:
   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
3     4.7         0             0        0          0
4     5.6         0             0        0          0
5     6.8         1             0        5          0

Afin d'obtenir ce que vous voulez, vous devez ajouter les éléments suivants pour filtrer les valeurs qui ne correspondent pas à vos ==1critères:

In [36]:

df[df[df.columns[pd.Series(df.columns).str.startswith('foo')]]==1]
Out[36]:
   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      NaN       1       NaN           NaN      NaN        NaN     NaN
1      NaN     NaN       NaN             1      NaN        NaN     NaN
2      NaN     NaN       NaN           NaN        1        NaN     NaN
3      NaN     NaN       NaN           NaN      NaN        NaN     NaN
4      NaN     NaN       NaN           NaN      NaN        NaN     NaN
5      NaN     NaN         1           NaN      NaN        NaN     NaN

ÉDITER

OK après avoir vu ce que vous voulez, la réponse alambiquée est la suivante:

In [72]:

df.loc[df[df[df.columns[pd.Series(df.columns).str.startswith('foo')]] == 1].dropna(how='all', axis=0).index]
Out[72]:
   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      5.0     1.0         0             0        2         NA      NA
1      5.0     2.1         0             1        4          0       0
2      6.0     NaN         0           NaN        1          0       1
5      6.8     6.8         1             0        5          0       0

66

Maintenant que les index des pandas prennent en charge les opérations sur les chaînes, le moyen le plus simple et le meilleur de sélectionner des colonnes commençant par `` foo '' est simplement:

df.loc[:, df.columns.str.startswith('foo')]

Vous pouvez également filtrer les étiquettes de colonne (ou de ligne) avec df.filter(). Pour spécifier une expression régulière correspondant aux noms commençant par foo.:

>>> df.filter(regex=r'^foo\.', axis=1)
   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
3     4.7         0             0        0          0
4     5.6         0             0        0          0
5     6.8         1             0        5          0

Pour sélectionner uniquement les lignes requises (contenant a 1) et les colonnes, vous pouvez utiliser loc, en sélectionnant les colonnes en utilisant filter(ou toute autre méthode) et les lignes en utilisant any:

>>> df.loc[(df == 1).any(axis=1), df.filter(regex=r'^foo\.', axis=1).columns]
   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
5     6.8         1             0        5          0

7

Le moyen le plus simple est d'utiliser str directement sur les noms de colonnes, il n'est pas nécessaire de pd.Series

df.loc[:,df.columns.str.startswith("foo")]



1

Sur la base de la réponse de @ EdChum, vous pouvez essayer la solution suivante:

df[df.columns[pd.Series(df.columns).str.contains("foo")]]

Cela sera vraiment utile si toutes les colonnes que vous souhaitez sélectionner ne commencent pas foo. Cette méthode sélectionne toutes les colonnes qui contiennent la sous-chaîne fooet elle peut être placée à n'importe quel point du nom d'une colonne.

En substance, j'ai remplacé .startswith()par .contains().


0

Ma solution. Il peut être plus lent sur les performances:

a = pd.concat(df[df[c] == 1] for c in df.columns if c.startswith('foo'))
a.sort_index()


   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      5.0     1.0         0             0        2         NA      NA
1      5.0     2.1         0             1        4          0       0
2      6.0     NaN         0           NaN        1          0       1
5      6.8     6.8         1             0        5          0       0

0

Une autre option pour la sélection des entrées souhaitées consiste à utiliser map:

df.loc[(df == 1).any(axis=1), df.columns.map(lambda x: x.startswith('foo'))]

qui vous donne toutes les colonnes pour les lignes qui contiennent un 1:

   foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu
0     1.0         0             0        2         NA
1     2.1         0             1        4          0
2     NaN         0           NaN        1          0
5     6.8         1             0        5          0

La sélection des lignes se fait par

(df == 1).any(axis=1)

comme dans la réponse de @ ajcr qui vous donne:

0     True
1     True
2     True
3    False
4    False
5     True
dtype: bool

ce qui signifie que cette ligne 3et 4ne contient pas de 1et ne sera pas sélectionnée.

La sélection des colonnes se fait à l'aide d'une indexation booléenne comme ceci:

df.columns.map(lambda x: x.startswith('foo'))

Dans l'exemple ci-dessus, cela renvoie

array([False,  True,  True,  True,  True,  True, False], dtype=bool)

Ainsi, si une colonne ne commence pas par foo, Falseest renvoyée et la colonne n'est donc pas sélectionnée.

Si vous souhaitez simplement renvoyer toutes les lignes contenant un 1- comme le suggère la sortie souhaitée - vous pouvez simplement le faire

df.loc[(df == 1).any(axis=1)]

qui retourne

   bar.baz  foo.aa  foo.bars  foo.fighters  foo.fox foo.manchu nas.foo
0      5.0     1.0         0             0        2         NA      NA
1      5.0     2.1         0             1        4          0       0
2      6.0     NaN         0           NaN        1          0       1
5      6.8     6.8         1             0        5          0       0

0

Vous pouvez essayer l'expression régulière ici pour filtrer les colonnes commençant par "foo"

df.filter(regex='^foo*')

Si vous devez avoir la chaîne foo dans votre colonne, alors

df.filter(regex='foo*')

serait approprié.

Pour l'étape suivante, vous pouvez utiliser

df[df.filter(regex='^foo*').values==1]

pour filtrer les lignes où l'une des valeurs de la colonne «foo *» est 1.


0

Dans mon cas, j'avais besoin d'une liste de préfixes

colsToScale=["production", "test", "development"]
dc[dc.columns[dc.columns.str.startswith(tuple(colsToScale))]]
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.