Cara memfilter baris dalam panda dengan regex

169

Saya ingin memfilter dataframe menggunakan regex pada salah satu kolom.

Untuk contoh yang dibuat-buat:

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

Saya ingin memfilter baris ke yang dimulai dengan fmenggunakan regex. Go pertama:

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

Itu tidak terlalu berguna. Namun ini akan membuat saya indeks boolean saya:

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

Jadi saya bisa melakukan pembatasan dengan:

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

Itu membuat saya secara artifisial menempatkan grup ke dalam regex, dan sepertinya mungkin bukan cara yang bersih untuk pergi. Apakah ada cara yang lebih baik untuk melakukan ini?

justinvf
sumber
5
Jika Anda tidak terikat dengan regex, foo[foo.b.str.startswith("f")]akan berhasil.
DSM
IMHO saya pikir foo[foo.b.str.match('(f.*)').str.len() > 0]adalah solusi yang cukup bagus! Lebih dapat dikustomisasi dan bermanfaat daripada mulai dengan karena ini mengemas fleksibilitas regex di dalamnya.
tumultous_rooster
3
ini mungkin agak terlambat tetapi dalam versi panda yang lebih baru, masalahnya sudah diperbaiki. baris foo[foo.b.str.match('f.*')]bekerja di panda 0.24.2 untuk saya.
Behzad Mehrtash

Jawaban:

198

Gunakan mengandung sebagai gantinya:

In [10]: df.b.str.contains('^f')
Out[10]: 
0    False
1     True
2     True
3    False
Name: b, dtype: bool
waitingkuo
sumber
11
Bagaimana bisa boolean dibalik? Ditemukan: stackoverflow.com/questions/15998188/…
dmeu
4
Apakah mungkin untuk mendapatkan hanya baris-baris yang memiliki True?
shockwave
2
@shockwave, Anda harus menggunakan:df.loc[df.b.str.contains('^f'), :]
Rafa
1
@shockwave Anda juga bisa menggunakandf[df.b.str.contains('^f'), :]
David Jung
23

Sudah ada fungsi penanganan string Series.str.startswith(). Kamu harus mencobanyafoo[foo.b.str.startswith('f')] .

Hasil:

    a   b
1   2   foo
2   3   fat

Saya pikir apa yang Anda harapkan.

Atau Anda dapat menggunakan berisi dengan opsi regex. Sebagai contoh:

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

Hasil:

    a   b
1   2   foo

na=False adalah untuk mencegah Kesalahan jika ada nilai nan, null dll

Erkan Şirin
sumber
Saya memodifikasi ini dan itu berhasil untuk sayadf[~df.CITY.str.contains('~.*', regex= True, na=False)]
Patty Jula
Terima kasih! ini adalah solusi hebat
Kedar Joshi
20

Pencarian beberapa kolom dengan bingkai data:

frame[frame.filename.str.match('*.'+MetaData+'.*') & frame.file_path.str.match('C:\test\test.txt')]
lakshman senathirajah
sumber
2
frame? dan 'C:\test\test.txt'? Sepertinya Anda menjawab pertanyaan yang berbeda.
tumultous_rooster
frame adalah df. ini terkait dengan pertanyaan yang sama, tetapi menjawab bagaimana memfilter banyak kolom ('nama file' dan 'file_path') dalam satu kode baris.
lakshman senathirajah
12

Ini mungkin agak terlambat, tetapi ini sekarang lebih mudah dilakukan di Panda. Anda dapat memanggil pertandingan dengan as_indexer=Trueuntuk mendapatkan hasil boolean. Ini didokumentasikan (bersama dengan perbedaan antara matchdan contains) di sini .

Michael Siler
sumber
11

Terima kasih atas jawaban yang bagus @ user3136169, berikut adalah contoh bagaimana hal itu dapat dilakukan juga dengan menghapus nilai 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)]

Anda juga dapat menambahkan regex sebagai argumen:

def regex_filter(val,myregex):
    ...

df_filtered = df[df['col'].apply(res_regex_filter,regex=myregex)]
burung gereja
sumber
1
terima kasih, karena ini saya menemukan cara untuk menyaring kolom dengan predikat sewenang-wenang.
jman
9

Tulis fungsi Boolean yang memeriksa regex dan gunakan terapkan pada kolom

foo[foo['b'].apply(regex_function)]
pengguna3136169
sumber
1

Menggunakan str irisan

foo[foo.b.str[0]=='f']
Out[18]: 
   a    b
1  2  foo
2  3  fat
YOBEN_S
sumber