แพนด้า: ตัวกรองที่ซับซ้อนในแถวของ DataFrame


87

ฉันต้องการกรองแถวตามฟังก์ชันของแต่ละแถวเช่น

def f(row):
  return sin(row['velocity'])/np.prod(['masses']) > 5

df = pandas.DataFrame(...)
filtered = df[apply_to_all_rows(df, f)]

หรือสำหรับตัวอย่างอื่นที่ซับซ้อนกว่าที่สร้างขึ้น

def g(row):
  if row['col1'].method1() == 1:
    val = row['col1'].method2() / row['col1'].method3(row['col3'], row['col4'])
  else:
    val = row['col2'].method5(row['col6'])
  return np.sin(val)

df = pandas.DataFrame(...)
filtered = df[apply_to_all_rows(df, g)]

ฉันจะทำเช่นนั้นได้อย่างไร?

คำตอบ:


123

คุณสามารถทำได้โดยใช้DataFrame.applyซึ่งใช้ฟังก์ชันตามแกนที่กำหนด

In [3]: df = pandas.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])

In [4]: df
Out[4]: 
          a         b         c
0 -0.001968 -1.877945 -1.515674
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

In [6]: df[df.apply(lambda x: x['b'] > x['c'], axis=1)]
Out[6]: 
          a         b         c
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

16
ไม่จำเป็นสำหรับapplyในสถานการณ์นี้ ดัชนีบูลีนปกติจะทำงานได้ดี df[df['b] > df['c']]. มีสถานการณ์น้อยมากที่ต้องการจริงๆapplyและแม้แต่น้อยที่ต้องการด้วยaxis=1
Ted Petrou

@TedPetrou จะเกิดอะไรขึ้นถ้าคุณไม่แน่ใจว่าทุกองค์ประกอบในดาต้าเฟรมของคุณเป็นประเภทที่ถูกต้อง ดัชนีบูลีนปกติรองรับการจัดการข้อยกเว้นหรือไม่
. ร.

13

สมมติว่าฉันมี DataFrame ดังนี้:

In [39]: df
Out[39]: 
      mass1     mass2  velocity
0  1.461711 -0.404452  0.722502
1 -2.169377  1.131037  0.232047
2  0.009450 -0.868753  0.598470
3  0.602463  0.299249  0.474564
4 -0.675339 -0.816702  0.799289

ฉันสามารถใช้ sin และ DataFrame.prod เพื่อสร้างบูลีนมาสก์:

In [40]: mask = (np.sin(df.velocity) / df.ix[:, 0:2].prod(axis=1)) > 0

In [41]: mask
Out[41]: 
0    False
1    False
2    False
3     True
4     True

จากนั้นใช้มาสก์เพื่อเลือกจาก DataFrame:

In [42]: df[mask]
Out[42]: 
      mass1     mass2  velocity
3  0.602463  0.299249  0.474564
4 -0.675339 -0.816702  0.799289

2
อันที่จริงนี่อาจเป็นตัวอย่างที่ไม่ดี: np.sinออกอากาศไปยังองค์ประกอบทั้งหมดโดยอัตโนมัติ จะเกิดอะไรขึ้นถ้าฉันแทนที่ด้วยฟังก์ชันที่ชาญฉลาดน้อยกว่าที่สามารถจัดการได้ครั้งละหนึ่งอินพุต?
duckworthd


5

ฉันสามารถแสดงความคิดเห็นเกี่ยวกับคำตอบของ duckworthdแต่มันทำงานได้ไม่สมบูรณ์ มันล้มเหลวเมื่อ dataframe ว่างเปล่า:

df = pandas.DataFrame(columns=['a', 'b', 'c'])
df[df.apply(lambda x: x['b'] > x['c'], axis=1)]

ผลลัพธ์:

ValueError: Must pass DataFrame with boolean values only

สำหรับฉันมันดูเหมือนบั๊กในแพนด้าเนื่องจาก {} เป็นชุดค่าบูลีนที่ถูกต้อง สำหรับการแก้ปัญหาการอ้างถึงคำตอบที่รอย Hyunjin ฮัน


3

วิธีที่ดีที่สุดที่ฉันพบคือแทนที่จะใช้reduce=Trueเพื่อหลีกเลี่ยงข้อผิดพลาดสำหรับ df ว่าง (เนื่องจากอาร์กิวเมนต์นี้เลิกใช้แล้ว) ให้ตรวจสอบขนาด df> 0 ก่อนใช้ตัวกรอง:

def my_filter(row):
    if row.columnA == something:
        return True

    return False

if len(df.index) > 0:
    df[df.apply(my_filter, axis=1)]

0

คุณสามารถใช้ไฟล์ locคุณสมบัติเพื่อแบ่งส่วนดาต้าเฟรมของคุณ

ตาม เอกสาร , locสามารถมีความcallable functionเป็นอาร์กิวเมนต์

In [3]: df = pandas.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])

In [4]: df
Out[4]: 
          a         b         c
0 -0.001968 -1.877945 -1.515674
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

# define lambda function
In [5]: myfilter = lambda x: x['b'] > x['c']

# use my lambda in loc
In [6]: df1 = df.loc[fif]

หากคุณต้องการรวมฟังก์ชันตัวกรองของคุณfifกับเกณฑ์การกรองอื่น ๆ

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