วิธีกรองแถวในแพนด้าโดย regex


169

ฉันต้องการกรองดาต้าเฟรมอย่างละเอียดโดยใช้ regex ในหนึ่งในคอลัมน์

สำหรับตัวอย่างที่วางแผนไว้:

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

ฉันต้องการกรองแถวเป็นแถวที่ขึ้นต้นด้วยการfใช้ regex ไปก่อน:

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

นั่นไม่ได้มีประโยชน์มากเกินไป อย่างไรก็ตามสิ่งนี้จะได้รับดัชนีบูลีนของฉัน:

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

ดังนั้นฉันสามารถทำข้อ จำกัด ของฉันโดย:

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

นั่นทำให้ฉันใส่กลุ่มเข้าไปใน regex อย่างดุเดือดและดูเหมือนว่าอาจจะไม่ใช่วิธีที่สะอาด มีวิธีที่ดีกว่าในการทำเช่นนี้?


5
หากคุณไม่ได้แต่งงานกับ regexes foo[foo.b.str.startswith("f")]จะทำงานได้
DSM

IMHO ฉันคิดว่าfoo[foo.b.str.match('(f.*)').str.len() > 0]เป็นทางออกที่ดีพอสมควร! ปรับแต่งได้และมีประโยชน์มากกว่า startswith เพราะมันบรรจุความสามารถรอบด้านของ regex
tumultous_rooster

3
อาจจะช้าไปหน่อย แต่ในแพนด้ารุ่นใหม่ปัญหาได้รับการแก้ไขแล้ว สายใช้foo[foo.b.str.match('f.*')]งานได้ใน pandas 0.24.2 สำหรับฉัน
Behzad Mehrtash

คำตอบ:


198

ใช้มีแทน:

In [10]: df.b.str.contains('^f')
Out[10]: 
0    False
1     True
2     True
3    False
Name: b, dtype: bool

11
บูลีนจะกลับด้านได้อย่างไร พบมัน: stackoverflow.com/questions/15998188/…
dmeu

4
เป็นไปได้ไหมที่จะได้แถวที่มีจริงเท่านั้น?
shockwave

2
@shockwave คุณควรใช้:df.loc[df.b.str.contains('^f'), :]
Rafa

1
@shockwave นอกจากนี้คุณสามารถใช้df[df.b.str.contains('^f'), :]
David Jung

24

Series.str.startswith()มีอยู่แล้วฟังก์ชั่นการจัดการสตริง foo[foo.b.str.startswith('f')]คุณควรพยายาม

ผลลัพธ์:

    a   b
1   2   foo
2   3   fat

ฉันคิดว่าสิ่งที่คุณคาดหวัง

หรือคุณสามารถใช้ประกอบด้วยกับตัวเลือก regex ตัวอย่างเช่น:

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

ผลลัพธ์:

    a   b
1   2   foo

na=False คือการป้องกันข้อผิดพลาดในกรณีที่มีค่า nan, null ฯลฯ


ฉันแก้ไขสิ่งนี้และใช้งานได้สำหรับฉันdf[~df.CITY.str.contains('~.*', regex= True, na=False)]
Patty Jula

ขอบคุณ! นี่เป็นทางออกที่ยอดเยี่ยม
Kedar Joshi

20

ค้นหาหลายคอลัมน์ด้วยดาต้าเฟรม:

frame[frame.filename.str.match('*.'+MetaData+'.*') & frame.file_path.str.match('C:\test\test.txt')]

2
frame? และ'C:\test\test.txt'? ดูเหมือนว่าคุณกำลังตอบคำถามอื่น
tumultous_rooster

เฟรมคือ df มันเกี่ยวข้องกับคำถามเดียวกัน แต่จะตอบวิธีการกรองหลายคอลัมน์ ('ชื่อไฟล์' และ 'file_path') ในรหัสบรรทัดเดียว
lakshman senathirajah

12

นี่อาจจะสายไปซักหน่อย แต่ตอนนี้คุณสามารถทำได้ง่ายกว่าใน Pandas คุณสามารถเรียกการจับคู่ด้วยas_indexer=Trueเพื่อให้ได้ผลลัพธ์แบบบูล นี้เป็นเอกสาร (พร้อมกับความแตกต่างระหว่างmatchและcontains) ที่นี่


11

ขอบคุณสำหรับคำตอบที่ยอดเยี่ยม @ user3136169 ต่อไปนี้เป็นตัวอย่างของวิธีการที่อาจทำเช่นนั้นได้ด้วยการลบค่า 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)]

นอกจากนี้คุณยังสามารถเพิ่ม regex เป็น arg:

def regex_filter(val,myregex):
    ...

df_filtered = df[df['col'].apply(res_regex_filter,regex=myregex)]

1
ขอบคุณด้วยเหตุนี้ฉันจึงหาวิธีกรองคอลัมน์โดยใช้คำกริยา
jman

9

เขียนฟังก์ชั่นบูลีนที่ตรวจสอบ regex และใช้นำไปใช้ในคอลัมน์

foo[foo['b'].apply(regex_function)]

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.