ลบแถวออกจาก DataFrame แพนด้าตามนิพจน์เงื่อนไขที่เกี่ยวข้องกับ len (สตริง) ที่ให้ KeyError


303

ฉันมี DataFrame แพนด้าและฉันต้องการลบแถวจากที่ซึ่งความยาวของสตริงในคอลัมน์ใดคอลัมน์หนึ่งมีค่ามากกว่า 2

ฉันคาดว่าจะสามารถทำเช่นนี้ (ต่อคำตอบนี้ ):

df[(len(df['column name']) < 2)]

แต่ฉันเพิ่งได้รับข้อผิดพลาด:

KeyError: u'no item named False'

ผมทำอะไรผิดหรือเปล่า?

(หมายเหตุ: ฉันรู้ว่าฉันสามารถใช้df.dropna()เพื่อกำจัดแถวที่มีNaNแต่ฉันไม่เห็นวิธีการลบแถวตามนิพจน์เงื่อนไข)

คำตอบ:


168

เมื่อคุณทำlen(df['column name'])คุณจะได้รับหนึ่งหมายเลขคือจำนวนแถวใน DataFrame (เช่นความยาวของคอลัมน์เอง) ถ้าคุณต้องการที่จะนำไปใช้กับแต่ละองค์ประกอบในคอลัมน์ที่ใช้len df['column name'].map(len)ลองดูสิ

df[df['column name'].map(len) < 2]

3
ฉันคิดวิธีการใช้ list comprehension df[[(len(x) < 2) for x in df['column name']]]แต่คุณดีกว่ามาก ขอบคุณสำหรับความช่วยเหลือของคุณ!
sjs

13
ในกรณีที่มีคนต้องการเปรียบเทียบที่ซับซ้อนมากขึ้นแลมบ์ดาสามารถใช้ได้เสมอ df[df['column name'].map(lambda x: str(x)!=".")]
4 ธันวาคมถึง

1
ด้วยเหตุผลบางอย่างไม่มีตัวเลือกอื่นที่ใช้งานได้สำหรับฉันยกเว้นตัวเลือกที่โพสต์โดย @ 4lberto ฉันอยู่pandas 0.23.4และงูหลาม 3.6
goelakash

1
ฉันจะเพิ่ม.copy()ในตอนท้ายในกรณีที่คุณต้องการแก้ไขภายหลัง dataframe นี้ (ตัวอย่างเช่นการกำหนดคอลัมน์ใหม่จะเพิ่ม "ค่าพยายามที่จะตั้งค่าในสำเนาของชิ้นจากคำเตือน DataFrame"
PlasmaBinturong

806

เพื่อตอบคำถามเดิมของคำถามนี้โดยตรง "วิธีการลบแถวออกจาก DataFrame แพนด้าตามนิพจน์เงื่อนไข" (ซึ่งฉันเข้าใจไม่จำเป็นต้องเป็นปัญหาของ OP แต่สามารถช่วยผู้ใช้รายอื่นที่เจอคำถามนี้) วิธีหนึ่งในการใช้คือลดลงวิธีการ:

df = df.drop(some labels)

df = df.drop(df[<some boolean condition>].index)

ตัวอย่าง

วิธีลบแถวทั้งหมดโดยที่ 'คะแนน' ของคอลัมน์คือ <50:

df = df.drop(df[df.score < 50].index)

ในเวอร์ชันของสถานที่ (ตามที่ระบุไว้ในความคิดเห็น)

df.drop(df[df.score < 50].index, inplace=True)

หลายเงื่อนไข

(ดูการทำดัชนีบูลีน )

ผู้ประกอบการที่มี: |สำหรับor, &สำหรับand, และสำหรับ~ notเหล่านี้จะต้องจัดกลุ่มโดยใช้วงเล็บ

หากต้องการลบแถวทั้งหมดโดยที่ 'คะแนน' ของคอลัมน์คือ <50 และ> 20

df = df.drop(df[(df.score < 50) & (df.score > 20)].index)


32
ฉันแค่อยากจะพูดว่าฟังก์ชั่นการตกรองรับการแทนที่ภายใน เช่น ,. โซลูชันของคุณเหมือนกับ df.drop (df [df.score <50] .index, inplace = True) อย่างไรก็ตามไม่รู้เคล็ดลับ "ดัชนี" ช่วยฉันได้มาก
Quickbeam2k1

9
เพียงต้องการชี้ให้เห็นว่าก่อนที่คุณจะใช้เคล็ดลับดัชนีนี้คุณต้องแน่ใจว่าค่าดัชนีของคุณไม่ซ้ำกัน (หรือโทรreset_index()) ฉันพบสิ่งนี้ยากเมื่อทางไปหลายแถวหลุดจากดาต้าเฟรมของฉัน
Jay

3
ฉันจะวางแถวทั้งหมดที่ประเภทคอลัมน์เป็น str ได้อย่างไร ฉันต้องการเก็บเฉพาะคอลัมน์ประเภทรายการเท่านั้น ฉันได้ลองแล้ว test = df.drop(df[df['col1'].dtype == str].index)แต่ฉันพบข้อผิดพลาดKeyError: False ฉันก็ลองdf.drop(df[df.col1.dtype == str].index)แล้วdf.drop(df[type(df.cleaned_norm_email) == str].index)แต่ดูเหมือนจะไม่มีอะไรทำงาน ทุกคนสามารถให้คำแนะนำ ขอบคุณ! @User
PyRsquared

1
นี่เป็นคำถามเก่า แต่ ... @ @ aquatically-challenged-fish เร็วกว่านี้มาก โปรดทราบว่าคุณคำนวณdf[(df.score < 50) & (df.score > 20)]เป็นส่วนหนึ่งของคำตอบของคุณ หากคุณกลับรายการนี้df = df[(df.score >= 50) | (df.score <= 20)]คุณจะได้รับคำตอบเร็วขึ้น
Roobie Nuby

1
@RoobieNuby - พวกเขาไม่เหมือนกัน
อัล

106

คุณสามารถกำหนดDataFrameรุ่นที่ถูกกรองของตัวเอง:

df = df[df.score > 50]

นี่เร็วกว่าdrop:

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test[test.x < 0]
# 54.5 ms ± 2.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test.drop(test[test.x > 0].index, inplace=True)
# 201 ms ± 17.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test.drop(test[test.x > 0].index)
# 194 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

ฉันจะตรวจสอบการใช้หรือเงื่อนไขหลายคอลัมน์ได้อย่างไร
Piyush S. Wanare


9

ฉันจะขยายโซลูชั่นทั่วไปของ @ User เพื่อให้dropทางเลือกฟรี สิ่งนี้มีไว้สำหรับผู้กำกับที่นี่ตามชื่อคำถาม (ไม่ใช่ปัญหาของ OP)

สมมติว่าคุณต้องการลบแถวทั้งหมดด้วยค่าลบ วิธีแก้ปัญหาหนึ่งคือ: -

df = df[(df > 0).all(axis=1)]

คำอธิบายทีละขั้นตอน: -

มาสร้างกรอบข้อมูลการแจกแจงแบบปกติ 5x5

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,5), columns=list('ABCDE'))
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
1 -0.977278  0.950088 -0.151357 -0.103219  0.410599
2  0.144044  1.454274  0.761038  0.121675  0.443863
3  0.333674  1.494079 -0.205158  0.313068 -0.854096
4 -2.552990  0.653619  0.864436 -0.742165  2.269755

ปล่อยให้เงื่อนไขถูกลบเนกาทีฟ บูลีน df เป็นไปตามเงื่อนไข: -

df > 0
      A     B      C      D      E
0   True  True   True   True   True
1  False  True  False  False   True
2   True  True   True   True   True
3   True  True  False   True  False
4  False  True   True  False   True

ชุดบูลีนสำหรับแถวทั้งหมดเป็นไปตามเงื่อนไข หมายเหตุหากองค์ประกอบใด ๆ ในแถวล้มเหลวตามเงื่อนไขแถวนั้นจะถูกทำเครื่องหมายเป็นเท็จ

(df > 0).all(axis=1)
0     True
1    False
2     True
3    False
4    False
dtype: bool

ในที่สุดก็กรองแถวออกจากกรอบข้อมูลตามเงื่อนไข

df[(df > 0).all(axis=1)]
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
2  0.144044  1.454274  0.761038  0.121675  0.443863

คุณสามารถกำหนดกลับเป็น df เพื่อลบตัวกรอง vs ที่ทำไว้ด้านบน
df = df[(df > 0).all(axis=1)]

สามารถขยายได้อย่างง่ายดายเพื่อกรองแถวที่มี NaN s (รายการที่ไม่ใช่ตัวเลข): ​​-
df = df[(~df.isnull()).all(axis=1)]

สิ่งนี้สามารถทำให้ง่ายขึ้นสำหรับกรณีเช่น: ลบแถวทั้งหมดที่คอลัมน์ E เป็นลบ

df = df[(df.E>0)]

ฉันอยากจะจบด้วยสถิติการทำโปรไฟล์ว่าทำไมdropโซลูชั่นของ @ User นั้นช้ากว่าการกรองตามคอลัมน์แบบดิบ

%timeit df_new = df[(df.E>0)]
345 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit dft.drop(dft[dft.E < 0].index, inplace=True)
890 µs ± 94.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

คอลัมน์นั้นโดยทั่วไปSeriesคือNumPyอาร์เรย์มันสามารถสร้างดัชนีได้โดยไม่ต้องเสียค่าใช้จ่ายใด ๆ สำหรับผู้ที่สนใจว่าองค์กรหน่วยความจำพื้นฐานเล่นด้วยความเร็วในการดำเนินการที่นี่เป็นลิงค์ที่ดีในการเร่ง Pandas :


6

ในแพนด้าคุณสามารถทำstr.lenตามขอบเขตของคุณและใช้ผลบูลีนเพื่อกรอง

df[df['column name'].str.len().lt(2)]

3

หากคุณต้องการวางแถวของกรอบข้อมูลบนพื้นฐานของเงื่อนไขที่ซับซ้อนบางอย่างเกี่ยวกับค่าคอลัมน์จากนั้นเขียนว่าวิธีที่แสดงข้างต้นอาจมีความซับซ้อน ฉันมีวิธีแก้ปัญหาที่ง่ายกว่าซึ่งใช้ได้เสมอ ให้เราสมมติว่าคุณต้องการวางคอลัมน์ด้วย 'ส่วนหัว' เพื่อรับคอลัมน์นั้นในรายการก่อน

text_data = df['name'].tolist()

ตอนนี้ใช้ฟังก์ชั่นบางอย่างในทุกองค์ประกอบของรายการและวางลงในซีรีย์แพนด้า:

text_length = pd.Series([func(t) for t in text_data])

ในกรณีของฉันฉันแค่พยายามรับจำนวนโทเค็น:

text_length = pd.Series([len(t.split()) for t in text_data])

ตอนนี้เพิ่มหนึ่งคอลัมน์พิเศษด้วยชุดข้างต้นในกรอบข้อมูล:

df = df.assign(text_length = text_length .values)

ตอนนี้เราสามารถใช้เงื่อนไขในคอลัมน์ใหม่เช่น:

df = df[df.text_length  >  10]
def pass_filter(df, label, length, pass_type):

    text_data = df[label].tolist()

    text_length = pd.Series([len(t.split()) for t in text_data])

    df = df.assign(text_length = text_length .values)

    if pass_type == 'high':
        df = df[df.text_length  >  length]

    if pass_type == 'low':
        df = df[df.text_length  <  length]

    df = df.drop(columns=['text_length'])

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