web-dev-qa-db-fra.com

pandas: le meilleur moyen de sélectionner toutes les colonnes dont le nom commence par X

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 veux sélectionner des valeurs de 1 dans les colonnes commençant par foo.. Y a-t-il une meilleure façon de le faire 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 semblable à écrire quelque chose comme:

df2= df[df.STARTS_WITH_FOO == 1]

La réponse devrait imprimer un DataFrame comme ceci:

   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]
67
ccsv

Il suffit de réaliser 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

Pour obtenir ce que vous voulez, vous devez ajouter ce qui suit pour filtrer les valeurs qui ne correspondent pas à votre ==1 Critè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 compliqué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
101
EdChum

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

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 un 1) Et les colonnes, vous pouvez utiliser loc, en sélectionnant les colonnes à l'aide de filter (ou toute autre méthode) et les lignes à l'aide de 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
41
Alex Riley

Une autre option pour la sélection des entrées désiré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

Le sélection de la ligne est 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 la rangée 3 et 4 ne contient pas de 1 et ne sera pas sélectionné.

La sélection des colonnes est faite en utilisant une indexation booléenne comme ceci:

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

Dans l'exemple ci-dessus, cela retourne

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

Ainsi, si une colonne ne commence pas par foo, False est renvoyé 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 le résultat souhaité - 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
Cleb

Ma solution Il peut être plus lent sur la performance:

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
Robbie Liu