Bagaimana cara menguji apakah sebuah string berisi salah satu substring dalam daftar, di panda?

119

Apakah ada fungsi yang setara dengan kombinasi df.isin()dan df[col].str.contains()?

Sebagai contoh, katakanlah saya memiliki seri s = pd.Series(['cat','hat','dog','fog','pet']), dan saya ingin menemukan semua tempat yang sberisi salah satu ['og', 'at'], saya ingin mendapatkan semuanya kecuali 'hewan peliharaan'.

Saya punya solusi, tapi agak janggal:

searchfor = ['og', 'at']
found = [s.str.contains(x) for x in searchfor]
result = pd.DataFrame[found]
result.any()

Apakah ada cara yang lebih baik untuk melakukan ini?

ari
sumber
Catatan : Ada solusi yang dijelaskan oleh @unutbu yang lebih efisien daripada menggunakan pd.Series.str.contains. Jika kinerja menjadi masalah, maka ini mungkin perlu diselidiki.
jpp
Sangat disarankan untuk memeriksa jawaban ini untuk pencarian string parsial menggunakan beberapa kata kunci / regexes (gulir ke bawah ke subpos " Pencarian Substring Banyak ").
cs95

Jawaban:

219

Salah satu opsinya adalah dengan menggunakan |karakter regex untuk mencoba mencocokkan setiap substring dalam kata-kata di Seri Anda s(masih menggunakan str.contains).

Anda dapat membuat regex dengan menggabungkan kata-kata di searchforwith |:

>>> searchfor = ['og', 'at']
>>> s[s.str.contains('|'.join(searchfor))]
0    cat
1    hat
2    dog
3    fog
dtype: object

Seperti yang dicatat oleh @AndyHayden pada komentar di bawah, berhati-hatilah jika substring Anda memiliki karakter khusus seperti $dan ^yang ingin Anda cocokkan secara harfiah. Karakter ini memiliki arti khusus dalam konteks ekspresi reguler dan akan memengaruhi pencocokan.

Anda dapat membuat daftar substring lebih aman dengan keluar dari karakter non-alfanumerik dengan re.escape:

>>> import re
>>> matches = ['$money', 'x^y']
>>> safe_matches = [re.escape(m) for m in matches]
>>> safe_matches
['\\$money', 'x\\^y']

String dengan dalam daftar baru ini akan cocok dengan setiap karakter secara harfiah saat digunakan dengan str.contains.

Alex Riley
sumber
4
mungkin bagus untuk menambahkan tautan ini pandas.pydata.org/pandas-docs/stable/… juga. Mulai dari panda 0,15, operasi string bahkan lebih mudah
goofd
6
satu hal yang harus Anda perhatikan adalah jika string dalam pencarian memiliki karakter regex khusus (Anda dapat memetakan dengan re.escape ).
Andy Hayden
@AndyHayden Terima kasih, saya telah meningkatkan jawaban saya untuk memperhitungkan komplikasi ini.
Alex Riley
Saya tidak tahu mengapa metode Anda tidak bekerja dengan "str.startswith ('|' .join (searchfor))"
Doo Hyun Shin
48

Anda dapat menggunakan str.containssendiri dengan pola regex menggunakan OR (|):

s[s.str.contains('og|at')]

Atau Anda bisa menambahkan seri untuk dataframekemudian digunakan str.contains:

df = pd.DataFrame(s)
df[s.str.contains('og|at')] 

Keluaran:

0 cat
1 hat
2 dog
3 fog 
l'L'l
sumber
bagaimana melakukannya untuk DAN?
JacoSolari
1
@JacoSolari lihat jawaban ini stackoverflow.com/questions/37011734/…
Yakobus
1
@ James ya, terima kasih. Untuk penyelesaiannya di sini adalah satu-satu yang paling disukai dalam jawaban itu. df.col.str.contains(r'(?=.*apple)(?=.*banana)',regex=True)
JacoSolari
1

Berikut adalah satu baris lambda yang juga berfungsi:

df["TrueFalse"] = df['col1'].apply(lambda x: 1 if any(i in x for i in searchfor) else 0)

Memasukkan:

searchfor = ['og', 'at']

df = pd.DataFrame([('cat', 1000.0), ('hat', 2000000.0), ('dog', 1000.0), ('fog', 330000.0),('pet', 330000.0)], columns=['col1', 'col2'])

   col1  col2
0   cat 1000.0
1   hat 2000000.0
2   dog 1000.0
3   fog 330000.0
4   pet 330000.0

Terapkan Lambda:

df["TrueFalse"] = df['col1'].apply(lambda x: 1 if any(i in x for i in searchfor) else 0)

Keluaran:

    col1    col2        TrueFalse
0   cat     1000.0      1
1   hat     2000000.0   1
2   dog     1000.0      1
3   fog     330000.0    1
4   pet     330000.0    0
Berikan Shannon
sumber