panda: il modo migliore per selezionare tutte le colonne i cui nomi iniziano con X


104

Ho 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],})

Voglio selezionare i valori di 1 nelle colonne che iniziano con foo.. C'è un modo migliore per farlo oltre a:

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

Qualcosa di simile alla scrittura di qualcosa come:

df2= df[df.STARTS_WITH_FOO == 1]

La risposta dovrebbe stampare un DataFrame come questo:

   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]

Risposte:


151

Basta eseguire una comprensione dell'elenco per creare le colonne:

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

Un altro metodo consiste nel creare una serie dalle colonne e utilizzare il metodo str vettorizzato 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

Per ottenere ciò che desideri, devi aggiungere quanto segue per filtrare i valori che non soddisfano i tuoi ==1criteri:

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

MODIFICARE

OK dopo aver visto quello che vuoi la risposta contorta è questa:

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

Ora che gli indici dei panda supportano le operazioni sulle stringhe, probabilmente il modo più semplice e migliore per selezionare le colonne che iniziano con 'foo' è semplicemente:

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

In alternativa, puoi filtrare le etichette di colonna (o riga) con df.filter(). Per specificare un'espressione regolare che corrisponda ai nomi che iniziano con 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

Per selezionare solo le righe richieste (contenenti a 1) e le colonne, è possibile utilizzare loc, selezionando le colonne utilizzando filter(o qualsiasi altro metodo) e le righe utilizzando 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

Il modo più semplice è usare str direttamente sui nomi delle colonne, non è necessario pd.Series

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


1

Sulla base della risposta di @ EdChum, puoi provare la seguente soluzione:

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

Questo sarà davvero utile nel caso in cui non tutte le colonne che desideri selezionare iniziano con foo. Questo metodo seleziona tutte le colonne che contengono la sottostringa fooe può essere inserita in qualsiasi punto del nome di una colonna.

In sostanza, ho sostituito .startswith()con .contains().


0

La mia soluzione. Potrebbe essere più lento sulle prestazioni:

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

Un'altra opzione per la selezione delle voci desiderate è utilizzare map:

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

che ti dà tutte le colonne per le righe che contengono 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 selezione delle righe viene eseguita da

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

come nella risposta di @ ajcr che ti dà:

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

significa che la riga 3e 4non contengono un 1e non verranno selezionati.

La selezione delle colonne viene eseguita utilizzando l'indicizzazione booleana in questo modo:

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

Nell'esempio sopra, questo restituisce

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

Quindi, se una colonna non inizia con foo, Falseviene restituito e la colonna non viene quindi selezionata.

Se vuoi solo restituire tutte le righe che contengono un 1- come suggerisce l'output desiderato - puoi semplicemente farlo

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

che ritorna

   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

Puoi provare la regex qui per filtrare le colonne che iniziano con "foo"

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

Se hai bisogno di avere la stringa pippo nella tua colonna, allora

df.filter(regex='foo*')

sarebbe appropriato.

Per il passaggio successivo, puoi usare

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

per filtrare le righe in cui uno dei valori della colonna "foo *" è 1.


0

Nel mio caso avevo bisogno di un elenco di prefissi

colsToScale=["production", "test", "development"]
dc[dc.columns[dc.columns.str.startswith(tuple(colsToScale))]]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.