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