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]
ccsv
la source

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
EdChum
la source
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
Alex Riley
la source
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")]


Mohammed Omar Elsiddieg
la source
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().

Arturo Sbr
la source
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
Robbie Liu
la source
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
Cleb
la source
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.

Ricky
la source
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))]]
Flavio Sousa
la source