Jak filtrować wiersze w pandach według wyrażenia regularnego

169

Chciałbym czysto przefiltrować ramkę danych za pomocą wyrażenia regularnego w jednej z kolumn.

Oto wymyślony przykład:

In [210]: foo = pd.DataFrame({'a' : [1,2,3,4], 'b' : ['hi', 'foo', 'fat', 'cat']})
In [211]: foo
Out[211]: 
   a    b
0  1   hi
1  2  foo
2  3  fat
3  4  cat

Chcę przefiltrować wiersze do tych, które zaczynają się fod wyrażenia regularnego. Pierwsze podejście:

In [213]: foo.b.str.match('f.*')
Out[213]: 
0    []
1    ()
2    ()
3    []

To niezbyt przydatne. Jednak w ten sposób otrzymam mój indeks logiczny:

In [226]: foo.b.str.match('(f.*)').str.len() > 0
Out[226]: 
0    False
1     True
2     True
3    False
Name: b

Mogłem więc zrobić moje ograniczenie poprzez:

In [229]: foo[foo.b.str.match('(f.*)').str.len() > 0]
Out[229]: 
   a    b
1  2  foo
2  3  fat

To sprawia, że ​​sztucznie umieszczam grupę w wyrażeniu regularnym i wydaje się, że może nie jest to czysta droga. Czy jest lepszy sposób na zrobienie tego?

justinvf
źródło
5
Jeśli nie jesteś przywiązany do wyrażeń regularnych, foo[foo.b.str.startswith("f")]zadziała.
DSM
Myślę, że IMHO foo[foo.b.str.match('(f.*)').str.len() > 0]to całkiem dobre rozwiązanie! Bardziej konfigurowalny i przydatny niż startedwith, ponieważ zawiera w sobie wszechstronność wyrażenia regularnego.
tumultous_rooster
3
może to być trochę za późno, ale w nowszych wersjach pand problem został rozwiązany. linia foo[foo.b.str.match('f.*')]działa w pandach 0.24.2 dla mnie.
Behzad Mehrtash

Odpowiedzi:

198

Zamiast tego użyj zawiera :

In [10]: df.b.str.contains('^f')
Out[10]: 
0    False
1     True
2     True
3    False
Name: b, dtype: bool
waitkuo
źródło
11
Jak można odwrócić wartość logiczną? Znalazłem to: stackoverflow.com/questions/15998188/…
dmeu
4
Czy można uzyskać tylko te wiersze, które mają wartość True?
shockwave
2
@shockwave, którego powinieneś użyć:df.loc[df.b.str.contains('^f'), :]
Rafa
1
@shockwave Możesz też po prostu użyćdf[df.b.str.contains('^f'), :]
David Jung
24

Istnieje już funkcja obsługująca ciąg Series.str.startswith(). Powinieneś spróbowaćfoo[foo.b.str.startswith('f')] .

Wynik:

    a   b
1   2   foo
2   3   fat

Myślę, czego oczekujesz.

Alternatywnie możesz użyć zawiera z opcją regex. Na przykład:

foo[foo.b.str.contains('oo', regex= True, na=False)]

Wynik:

    a   b
1   2   foo

na=False ma na celu zapobieganie błędom w przypadku wartości nan, null itp

Erkan Şirin
źródło
Zmodyfikowałem do tego i u mnie zadziałałodf[~df.CITY.str.contains('~.*', regex= True, na=False)]
Patty Jula
Dziękuję Ci! to jest świetne rozwiązanie
Kedar Joshi
20

Wyszukiwanie w wielu kolumnach z ramką danych:

frame[frame.filename.str.match('*.'+MetaData+'.*') & frame.file_path.str.match('C:\test\test.txt')]
lakshman senathirajah
źródło
2
frame? i 'C:\test\test.txt'? Wygląda na to, że odpowiadasz na inne pytanie.
tumultous_rooster
ramka to df. jest powiązany z tym samym pytaniem, ale odpowiada, jak filtrować wiele kolumn („nazwa pliku” i „ścieżka_pliku”) w jednym kodzie linii.
lakshman senathirajah
12

Może to być trochę za późno, ale teraz jest to łatwiejsze do zrobienia na Pandach. Możesz wywołać match z, as_indexer=Trueaby uzyskać wyniki logiczne. Jest to udokumentowane (wraz z różnicą między matchi contains) tutaj .

Michael Siler
źródło
11

Dzięki za świetną odpowiedź @ user3136169, oto przykład tego, jak można to zrobić również usuwając wartości NoneType.

def regex_filter(val):
    if val:
        mo = re.search(regex,val)
        if mo:
            return True
        else:
            return False
    else:
        return False

df_filtered = df[df['col'].apply(regex_filter)]

Możesz również dodać wyrażenie regularne jako argument:

def regex_filter(val,myregex):
    ...

df_filtered = df[df['col'].apply(res_regex_filter,regex=myregex)]
wróbel
źródło
1
dzięki, z tego powodu wymyśliłem sposób na filtrowanie kolumny według dowolnego predykatu.
jman
9

Napisz funkcję boolowską, która sprawdza wyrażenie regularne i użyj zastosuj na kolumnie

foo[foo['b'].apply(regex_function)]
user3136169
źródło
1

Za pomocą str plasterka

foo[foo.b.str[0]=='f']
Out[18]: 
   a    b
1  2  foo
2  3  fat
YOBEN_S
źródło