pandy: najlepszy sposób na zaznaczenie wszystkich kolumn, których nazwy zaczynają się od X

109

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

Chcę wybrać wartości 1 w kolumnach zaczynających się od foo.. Czy jest na to lepszy sposób niż:

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

Coś podobnego do pisania czegoś takiego:

df2= df[df.STARTS_WITH_FOO == 1]

Odpowiedź powinna wydrukować ramkę danych w następujący sposób:

   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
źródło

Odpowiedzi:

162

Po prostu wykonaj analizę listy, aby utworzyć kolumny:

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

Inną metodą jest utworzenie serii z kolumn i użycie metody zwektoryzowanej str 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

Aby osiągnąć to, co chcesz, musisz dodać następujące elementy, aby przefiltrować wartości, które nie spełniają Twoich ==1kryteriów:

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

EDYTOWAĆ

OK, po zobaczeniu, czego chcesz, zawiła odpowiedź brzmi:

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
źródło
68

Teraz, gdy indeksy pand obsługują operacje na łańcuchach, prawdopodobnie najprostszym i najlepszym sposobem na wybranie kolumn zaczynających się od „foo” jest po prostu:

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

Alternatywnie możesz filtrować etykiety kolumn (lub wierszy) za pomocą df.filter(). Aby określić wyrażenie regularne pasujące do nazw zaczynających się od 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

Aby wybrać tylko wymagane wiersze (zawierające a 1) i kolumny, możesz użyć loc, wybierając kolumny za pomocą filter(lub dowolnej innej metody), a wiersze za pomocą 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
źródło
To jest odpowiedź, po którą tu przyszedłem, która pasuje do tytułu pytania. To, czego faktycznie chciał OP, było bardziej jak „Najlepszy sposób wybierania wierszy z filtrem opartym tylko na kolumnach zaczynających się od x”.
scign
8

Najprostszym sposobem jest użycie str bezpośrednio w nazwach kolumn, nie ma takiej potrzeby pd.Series

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


Mohammed Omar Elsiddieg
źródło
2

Na podstawie odpowiedzi @ EdChum możesz wypróbować następujące rozwiązanie:

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

Będzie to bardzo pomocne, jeśli nie wszystkie kolumny, które chcesz wybrać, zaczynają się od foo. Ta metoda wybiera wszystkie kolumny, które zawierają podciąg fooi można go umieścić w dowolnym miejscu nazwy kolumny.

W istocie, wymieniłem .startswith()z .contains().

Arturo Sbr
źródło
1

Możesz wypróbować wyrażenie regularne tutaj, aby odfiltrować kolumny zaczynające się od „foo”

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

Jeśli potrzebujesz w swojej kolumnie napisu foo

df.filter(regex='foo*')

byłoby odpowiednie.

W następnym kroku możesz użyć

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

aby odfiltrować wiersze, w których jedna z wartości kolumny „foo *” to 1.

Ricky
źródło
0

Moje rozwiązanie. Wydajność może być wolniejsza:

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
źródło
0

Inną opcją wyboru żądanych wpisów jest użycie map:

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

co daje ci wszystkie kolumny dla wierszy zawierających 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

Selekcji wiersza dokonuje

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

jak w odpowiedzi @ ajcr, która daje:

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

co oznacza, że ​​wiersz 3i 4nie zawierają 1i nie zostaną wybrane.

Wybór kolumn odbywa się za pomocą Boolean indeksowania tak:

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

W powyższym przykładzie zwraca to

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

Jeśli więc kolumna nie zaczyna się od foo,False jest zwracana, a zatem kolumna nie jest wybierana.

Jeśli chcesz po prostu zwrócić wszystkie wiersze zawierające 1- jak sugeruje żądane wyjście - możesz po prostu to zrobić

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

który powraca

   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
źródło
0

W moim przypadku potrzebowałem listy przedrostków

colsToScale=["production", "test", "development"]
dc[dc.columns[dc.columns.str.startswith(tuple(colsToScale))]]
Flavio Sousa
źródło