งานสำคัญอย่างหนึ่งที่มักจะต้องทำในขั้นตอนแรกๆของการจัดการข้อมูลก็คือการคัดกรองข้อมูล เพราะบางครั้งเราก็ไม่ได้ต้องการใช้ข้อมูลทั้งหมดที่ถูกเตรียมไว้ หรือข้อมูลบางส่วนอาจบกพร่องไม่เหมาะแก่การใช้ก็ต้องคัดออก
การเลือกเฉพาะคอลัมน์หรือแถวที่ต้องการด้วยลิสต์ของดัชนี ใน
บทแรกได้พูดถึงการคัดข้อมูลเฉพาะแถวที่ต้องการในซีรีส์ไปแล้ว สำหรับเดตาเฟรมเองก็ใช้วิธีการเดียวกัน คือการใช้ดัชนีเป็นลิสต์ของชื่อแถวที่ต้องการเอา
เพียงแต่ว่าเดตาเฟรมนั้นนอกจากมีแถวแล้วก็ยังมีคอลัมน์ด้วย จึงซับซ้อนกว่า และต้องแยกแยะให้ดีว่าจะเลือกจากแถวหรือคอลัมน์
กรณีที่ต้องการคัดเลือกแค่คอลัมน์ที่ต้องการก็ให้ใส่ดัชนีเป็นลิสต์ของคอลัมน์ได้เลย
ตัวอย่าง มีตารางข้อมูลของโปเกมอนซึ่งประกอบไปด้วย สายพันธุ์, ชนิด, ส่วนสูง และน้ำหนัก
import pandas as pd
pokemon = pd.DataFrame([
['นาเอเทิล','พืช',0.4,10.2],
['ฮิโกะซารุ','ไฟ',0.5,6.2],
['พจจามะ','น้ำ',0.4,5.2],
['มุกกุรุ','ธรรมดา,บิน',0.3,2.0]],
columns=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'],
index=[387,390,393,396])
print(pokemon)
ได้
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
387 |
นาเอเทิล |
พืช |
0.4 |
10.2 |
390 |
ฮิโกะซารุ |
ไฟ |
0.5 |
6.2 |
393 |
พจจามะ |
น้ำ |
0.4 |
5.2 |
396 |
มุกกุรุ |
ธรรมดา,บิน |
0.3 |
2.0 |
จากนั้นลองตัดชนิดออกไป เอาแค่สายพันธุ์, ส่วนสูง และน้ำหนัก
pokemon = pokemon[['สายพันธุ์','ส่วนสูง','น้ำหนัก']]
print(pokemon)
ได้
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
387 |
นาเอเทิล |
0.4 |
10.2 |
390 |
ฮิโกะซารุ |
0.5 |
6.2 |
393 |
พจจามะ |
0.4 |
5.2 |
396 |
มุกกุรุ |
0.3 |
2.0 |
กรณีนี้จะต่างจากซีรีส์ ถ้าเป็นซีรีส์การใส่ดัชนีไปโดยตรงจะเป็นการคัดเลือกจากแถว
สำหรับเดตาเฟรมหากต้องการคัดเลือกจากแถวให้ใช้ .loc
ตัวอย่าง
pokemon = pd.DataFrame([
['ฮายาชิงาเมะ','พืช',1.1,97.0],
['โมวกะซารุ','ไฟ,ต่อสู้',0.9,22.0],
['พตไทชิ','น้ำ',0.8,23.0],
['มุกุเบิร์ด','ธรรมดา,บิน',0.6,15.5],
['บีดารุ','ธรรมดา,น้ำ',1.0,31.5]],
columns=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'],
index=[388,391,394,397,400])
print(pokemon)
ได้
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
388 |
ฮายาชิงาเมะ |
พืช |
1.1 |
97.0 |
391 |
โมวกะซารุ |
ไฟ,ต่อสู้ |
0.9 |
22.0 |
394 |
พตไทชิ |
น้ำ |
0.8 |
23.0 |
397 |
มุกุเบิร์ด |
ธรรมดา,บิน |
0.6 |
15.5 |
400 |
บีดารุ |
ธรรมดา,น้ำ |
1.0 |
31.5 |
เลือกเอาเฉพาะบางแถว
pokemon = pokemon.loc[[388,391,394]]
print(pokemon)
ได้
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
388 |
ฮายาชิงาเมะ |
พืช |
1.1 |
97.0 |
391 |
โมวกะซารุ |
ไฟ,ต่อสู้ |
0.9 |
22.0 |
394 |
พตไทชิ |
น้ำ |
0.8 |
23.0 |
หรืออาจใช้ iloc เพื่อใช้ดัชนีเป็นลำดับของแถวก็ได้ เช่นถ้าเขียนแบบนี้จะได้ผลเหมือนกัน
pokemon = pokemon.iloc[[0,1,2]]
ผลที่ได้จะออกมาเป็นเดตาเฟรมเสมอหากเราใส่เป็นลิสต์ ต่อให้เป็นลิสต์ที่มีเพียงตัวเดียวก็ตาม เช่น
print(pokemon.iloc[[0]])
ได้
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
388 |
ฮายาชิงาเมะ |
พืช |
1.1 |
97.0 |
จะเห็นว่าแม้ข้อมูลจะเหลือแค่แถวเดียวแต่ก็ยังเป็นเดตาเฟรมอยู่
ซึ่งจะต่างจากกรณีที่ดัชนีเป็นเลขเดี่ยวไม่ใช่ลิสต์ ซึ่งจะให้ผลเป็นซีรีส์ของข้อมูลของแถวนั้น
print(pokemon.iloc[0])
ได้
สายพันธุ์ ฮายาชิงาเมะ
ชนิด พืช
ส่วนสูง 1.1
น้ำหนัก 97
Name: 388, dtype: object
การคัดกรองจากเงื่อนไข เช่นเดียวกับซีรีส์ เดตาเฟรมก็สามารถใช้ลิสต์ของบูลมาเป็นดัชนีได้เพื่อที่จะคัดกรองสมาชิก
ตัวอย่างเช่น
pokemon = pd.DataFrame([
['โคโรโบชิ','แมลง',0.3,2.2],
['โคโรท็อก','แมลง',1.0,25.5],
['โคลิงก์','ไฟฟ้า',0.5,9.5],
['ลุกซิโอ','ไฟฟ้า',0.9,30.5],
['เรินโทราร์','ไฟฟ้า',1.4,42.0]],
columns=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'],
index=[401,402,403,404,405])
print(pokemon)
print('--------------------')
print(pokemon[[True,False,False,True,False]])
ได้
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
401 |
โคโรโบชิ |
แมลง |
0.3 |
2.2 |
402 |
โคโรท็อก |
แมลง |
1.0 |
25.5 |
403 |
โคลิงก์ |
ไฟฟ้า |
0.5 |
9.5 |
404 |
ลุกซิโอ |
ไฟฟ้า |
0.9 |
30.5 |
405 |
เรินโทราร์ |
ไฟฟ้า |
1.4 |
42.0 |
--------------------
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
401 |
โคโรโบชิ |
แมลง |
0.3 |
2.2 |
404 |
ลุกซิโอ |
ไฟฟ้า |
0.9 |
30.5 |
สังเกตได้ว่ากรณีที่ดัชนีเป็นลิสต์ของบูลจะเป็นการคัดกรองโดยดูจากแถว ไม่ใช่คอลัมน์ ต่อให้ไม่ได้ใส่ .loc หรือ .iloc ไปก็ตาม ซึ่งจะต่างจากกรณีอื่น ดังนั้นต้องระวังสับสน
เพียงแต่ว่าจะเติม .loc หรือ .iloc ไปก็ได้เหมือนกัน เช่น
print(pokemon.iloc[[True,False,False,True,False]])
ผลที่ได้จะไม่ต่างกันกับไม่ใส่
นอกจากลิสต์แล้วจะใช้เป็นข้อมูลชนิดกลุ่มแบบอื่นๆเช่นอาเรย์หรือซีรีส์ก็ได้เช่นกัน เช่นใช้ซีรีส์ แต่กรณีที่เป็นซีรีส์นั้นจะต้องเตรียมซีรีส์ที่มีชื่อแถวเหมือนกัน
เช่นลองใช้ซีรีส์ตามนี้ผลที่ได้จะเหมือนกับตัวอย่างที่แล้วที่ใช้ลิสต์
tfftf = pd.Series([True,False,False,True,False],index=[401,402,403,404,405])
print(pokemon[tfftf])
ซึ่งซีรีส์ของบูลในลักษณะเช่นนี้สามารถเตรียมได้ง่ายโดยการนำซีรีส์มาเข้าในนิพจน์ที่มี == > < >= <=
คอลัมน์แต่ละคอลัมน์ของเดตาเฟรมเมื่อแยกออกมาก็เป็นซีรีส์ ตัวอย่างเช่นลองดูที่คอลัมน์ "ชนิด"
print(pokemon['ชนิด'])
ได้
401 แมลง
402 แมลง
403 ไฟฟ้า
404 ไฟฟ้า
405 ไฟฟ้า
Name: ชนิด, dtype: object
ถ้าเราใช้คอลัมน์นี้มาตั้งเงื่อนไขว่าให้มีค่าเท่ากับ "ไฟฟ้า"
print(pokemon['ชนิด']=='ไฟฟ้า')
แบบนี้จะได้
401 False
402 False
403 True
404 True
405 True
Name: ชนิด, dtype: bool
และเมื่อเราเอาซีรีส์ที่ได้จากเงื่อนไขนี้ไปใช้เป็นดัชนีของเดตาเฟรมก็จะเป็นการคัดกรองเอาเฉพาะโปเกมอนที่เป็นชนิดไฟฟ้า
print(pokemon[pokemon['ชนิด']=='ไฟฟ้า'])
ได้
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
403 |
โคลิงก์ |
ไฟฟ้า |
0.5 |
9.5 |
404 |
ลุกซิโอ |
ไฟฟ้า |
0.9 |
30.5 |
405 |
เรินโทราร์ |
ไฟฟ้า |
1.4 |
42.0 |
หรือถ้าจะคัดกรองโดยคัดจากน้ำหนักก็ได้
print(pokemon[pokemon['น้ำหนัก']<20])
ได้
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
401 |
โคโรโบชิ |
แมลง |
0.3 |
2.2 |
403 |
โคลิงก์ |
ไฟฟ้า |
0.5 |
9.5 |
นี่เป็นวิธีการคัดกรองที่มีประสิทธิภาพและใช้บ่อยมากที่สุด
การสร้างซีรีส์ของบูลเพื่อคัดกรองนั้นอาจสร้างขึ้นจากเมธอดบางตัวได้
เช่นกรณีที่จะตั้งเงื่อนไขว่าจะคัดเอาแถวที่ข้อมูลในนั้นอยู่ภายในลิสต์ที่เตรียมไว้ก็อาจใช้เมธอด isin ซึ่งมีความหมายในทำนองเดียวกับคำสั่ง in คือใช้ตรวจสอบว่าออบเจ็กต์นั้นๆเป็นสมาชิกอยู่ในลิสต์หรือเปล่าแล้วคืนค่า True หรือ False
ตัวอย่างเช่นสมมุติว่ามีข้อมูลโปเกมอนอยู่จำนวนหนึ่งที่เป็นชนิดต่างๆ แล้วต้องการคัดเอาที่เป็นชนิดน้ำและไฟฟ้าเท่านั้น แบบนี้ก็อาจเหมาะที่จะใช้ isin
pokemon = pd.DataFrame([
['ปาจิริสึ','ไฟฟ้า',0.4,3.9],
['บุยเซล','น้ำ',0.7,29.5],
['แชริมโบะ','พืช',0.4,3.3],
['คาระนากุชิ','น้ำ',0.3,6.3],
['ฟุวันเต','ผี,บิน',0.4,1.2]],
columns=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'],
index=[417,418,420,422,425])
print(pokemon)
print('----------------------')
print(pokemon['ชนิด'].isin(['น้ำ','ไฟฟ้า']))
print('----------------------')
print(pokemon[pokemon['ชนิด'].isin(['น้ำ','ไฟฟ้า'])])
ได้
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
417 |
ปาจิริสึ |
ไฟฟ้า |
0.4 |
3.9 |
418 |
บุยเซล |
น้ำ |
0.7 |
29.5 |
420 |
แชริมโบะ |
พืช |
0.4 |
3.3 |
422 |
คาระนากุชิ |
น้ำ |
0.3 |
6.3 |
425 |
ฟุวันเต |
ผี,บิน |
0.4 |
1.2 |
----------------------
417 True
418 True
420 False
422 True
425 False
Name: ชนิด, dtype: bool
----------------------
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
417 |
ปาจิริสึ |
ไฟฟ้า |
0.4 |
3.9 |
418 |
บุยเซล |
น้ำ |
0.7 |
29.5 |
422 |
คาระนากุชิ |
น้ำ |
0.3 |
6.3 |
กรณีนี้หากต้องการแค่โปเกมอนน้ำก็อาจเขียนเป็น pokemon[pokemon['ชนิด']=='น้ำ'] ถ้าต้องการไฟฟ้าก็เป็น pokemon[pokemon['ชนิด']=='ไฟฟ้า'] แต่เมื่อต้องการทั้งสองอย่างพร้อมกัน การเขียนแบบที่ว่ามานี้ก็จะเหมาะกว่า
การรับมือกับข้อมูลที่อาจไม่มีอยู่ หากมีลิสต์ของดัชนีหรือชื่อคอลัมน์อยู่จำนวนหนึ่งและต้องการใช้เพื่อคัดเอาข้อมูลในเดตาเฟรมที่ที่มีดัชนีหรือชื่อคอลัมน์ตามนั้นแต่ปรากฏว่าชื่อหรือค่าที่ใส่ลงไปบางส่วนไม่มีอยู่ในนั้น กรณีแบบนี้แบบนี้ก็จะเกิดข้อผิดพลาดขึ้นทันที
เช่นเราต้องการเข้าถึงข้อมูลสายพันธุ์, ส่วนสูง และน้ำหนักของโปเกมอนจำนวนหนึ่ง แต่ว่าข้อมูลในเดตาเฟรมที่เตรียมไว้ไม่มีข้อมูลส่วนสูง
pokemon = pd.DataFrame([
['อุโซฮาจิ','หิน',15.0],
['พิมปุกุ','ธรรมดา',24.4],
['มิการุเงะ','ผี,มาร',108.0],
['กอนเบ','ธรรมดา',105.0]],
columns=['สายพันธุ์','ชนิด','น้ำหนัก'],
index=[438,440,442,446])
print(pokemon)
ได้
|
สายพันธุ์ |
ชนิด |
น้ำหนัก |
438 |
อุโซฮาจิ |
หิน |
15.0 |
440 |
พิมปุกุ |
ธรรมดา |
24.4 |
442 |
มิการุเงะ |
ผี,มาร |
108.0 |
446 |
กอนเบ |
ธรรมดา |
105.0 |
หากลองพิมพ์
print(pokemon[['สายพันธุ์','ส่วนสูง','น้ำหนัก']])
จะได้
KeyError: "['ส่วนสูง'] not in index"
กรณีแบบนี้อาจแก้ปัญหาได้โดยใช้ .loc ได้ คือเขียนเป็น
print(pokemon.loc[:,['สายพันธุ์','ส่วนสูง','น้ำหนัก']])
ได้
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
438 |
อุโซฮาจิ |
NaN |
15.0 |
440 |
พิมปุกุ |
NaN |
24.4 |
442 |
มิการุเงะ |
NaN |
108.0 |
446 |
กอนเบ |
NaN |
105.0 |
แบบนี้ก็จะไม่เกิดข้อผิดพลาดขึ้น แต่จะเห็นว่ามีแถวข้อมูลส่วนสูงซึ่งมี NaN ปรากฏขึ้นมา
กรณีที่ใช้เพื่อเข้าถึงข้อมูลตามแถวก็ให้ผลลักษณะเดียวกัน
print(pokemon.loc[[438,439,440,441]])
ได้
|
สายพันธุ์ |
ชนิด |
น้ำหนัก |
438 |
อุโซฮาจิ |
หิน |
15.0 |
439 |
NaN |
NaN |
NaN |
440 |
พิมปุกุ |
ธรรมดา |
24.4 |
441 |
NaN |
NaN |
NaN |
วิธีนี้ถึงจะไม่เกิดข้อผิดพลาด แต่ก็ทำให้มีแถวที่มี NaN ปรากฏขึ้นมา ซึ่งบางทีมันก็อาจไม่จำเป็นเพราะยังไงก็ไม่มีข้อมูลอะไรเลยอยู่ดี
กรณีที่ไม่ต้องการให้มีแถวที่ไม่มีข้อมูลโผล่ขึ้นมาเลยอาจใช้วิธีอื่น นั่นคืออาศัยเมธอด isin กับชื่อคอลัมน์หรือชื่อแถวของเดตาเฟรม
เช่นสำหรับแถวอาจเขียนเป็น .loc[เดตาเฟรม.index.isin()] เช่น
print(pokemon.loc[pokemon.index.isin([438,439,440,441])])
ได้
|
สายพันธุ์ |
ชนิด |
น้ำหนัก |
438 |
อุโซฮาจิ |
หิน |
15.0 |
440 |
พิมปุกุ |
ธรรมดา |
24.4 |
สำหรับคอลัมน์เขียนเป็น .loc[:,เดตาเฟรม.columns.isin()] เช่น
print(pokemon.loc[:,pokemon.columns.isin(['สายพันธุ์','ส่วนสูง','น้ำหนัก'])])
ได้
|
สายพันธุ์ |
น้ำหนัก |
438 |
อุโซฮาจิ |
15.0 |
440 |
พิมปุกุ |
24.4 |
442 |
มิการุเงะ |
108.0 |
446 |
กอนเบ |
105.0 |
อ้างอิง