เดตาเฟรมของ pandas ยืนพื้นจากอาเรย์ของ numpy จึงใช้ความสามารถในการคำนวณเหมือนอย่างที่ใช้ในอาเรย์ได้
เรื่องของการคำนวณในอาเรย์อ่านได้ใน
numpy เบื้องต้นบทที่ ๓ การคำนวณของข้อมูลในตาราง ข้อมูลภายในเดตาเฟรมหรือซีรีส์สามารถนำมาบวกลบคูณหารกันได้โดยตรงเช่นเดียวกับอาเรย์ เพียงแต่เหนือกว่าตรงที่ว่าอาเรย์จะทำกันได้ในกรณีที่ขนาดเท่ากันหรือมีความสัมพันธ์กันเท่านั้น แต่เดตาเฟรมสามารถทำการคำนวณได้ไม่ว่าจะขนาดเท่าไหร่ เพียงแต่ว่าผลที่ได้จะเป็นไปตามที่ต้องการหรือเปล่านั้นก็ต้องมาดูกันเป็นกรณีไป
ตัวอย่างเช่น สร้างเดตาเฟรมเก็บข้อมูลจำนวนโปเกมอนที่ผู้เล่นสองคนชื่อซาโตชิกับชิเงรุจับได้และเคยเจอ
import pandas as pd
satoshi = pd.DataFrame([[15,12],[26,23],[33,33],[29,24],[9,8]],
columns=['เจอ','จับได้'],
index=['คาเตอร์ปี','บีเดิล','โป๊ปโป','โครัตตา','โอนิสึซึเมะ'])
shigeru = pd.DataFrame([[18,15],[39,36],[55,52],[12,9]],
columns=['เจอ','จับได้'],
index=['คาเตอร์ปี','บีเดิล','โป๊ปโป','อาร์โบ'])
print('ซาโตชิ')
print(satoshi)
print('ชิเงรุ')
print(shigeru)
print('นำของทั้งสองมาบวกกัน')
print(satoshi+shigeru)
ผลที่ได้
ซาโตชิ
|
เจอ |
จับได้ |
คาเตอร์ปี |
15 |
12 |
บีเดิล |
26 |
23 |
โป๊ปโป |
33 |
33 |
โครัตตา |
29 |
24 |
โอนิสึซึเมะ |
9 |
8 |
ชิเงรุ
|
เจอ |
จับได้ |
คาเตอร์ปี |
18 |
15 |
บีเดิล |
39 |
36 |
โป๊ปโป |
55 |
52 |
อาร์โบ |
12 |
9 |
นำของทั้งสองมาบวกกัน
|
เจอ |
จับได้ |
คาเตอร์ปี |
33.0 |
27.0 |
บีเดิล |
65.0 |
59.0 |
อาร์โบ |
NaN |
NaN |
โครัตตา |
NaN |
NaN |
โป๊ปโป |
88.0 |
85.0 |
โอนิสึซึเมะ |
NaN |
NaN |
จะเห็นได้ว่าเมื่อนำเดตาเฟรมมาบวกกันข้อมูลในตารางทั้งสองคอลัมน์ก็ถูกนำมาบวกกัน เจอบวกเจอ จับบวกจับ
แต่จะเห็นว่ามีปัญหาที่สังเกตได้ก็คือ แถวไหนที่มีเฉพาะตารางหนึ่งแต่ไม่มีในอีกตารางจะได้ค่าเป็น NaN ออกมา นั่นเพราะข้อมูลที่ไม่มีอยู่แม้จะพยายามบวกเข้ากับข้อมูลที่มีอยู่ก็ไม่รู้จะบวกด้วยค่าเท่าไหร่อยู่ดี
เพื่อแก้ปัญหานี้แทนที่จะใช้เครื่องหมายบวกเพื่อบวกโดยตรงก็ให้เปลี่ยนมาใช้เมธอด add แทน เมธอดนี้มีไว้ใช้แทนคำสั่งบวก เพียงแต่ว่าสามารถปรับแต่งรายละเอียดบางอย่างให้ต่างไปจากการบวกด้วยเครื่องหมายบวกธรรมดาได้
หากพิมพ์ satoshi.add(shigeru) แบบนี้จะมีค่าเท่ากับ satoshi+shigeru แต่หากเพิ่มคีย์เวิร์ดบางอย่างลงไปก็จะสามารถให้ผลที่ต่างออกไปได้
คีย์เวิร์ดที่จะต้องใช้ในคราวนี้ก็คือ fill_value เป็นคีย์เวิร์ดที่กำหนดว่าจะเติมข้อมูลที่เป็น NaN ด้วยอะไรดีเวลาที่นำมาคำนวณ
ในที่นี้ลองให้จำนวนที่ไม่มีเป็น 0 นั่นคือไม่เคยเจอไม่เคยจับเลย ก็จะเขียนเป็น
print(satoshi.add(shigeru,fill_value=0))
ผลลัพธ์ได้
|
เจอ |
จับได้ |
คาเตอร์ปี |
33.0 |
27.0 |
บีเดิล |
65.0 |
59.0 |
อาร์โบ |
12.0 |
9.0 |
โครัตตา |
29.0 |
24.0 |
โป๊ปโป |
88.0 |
85.0 |
โอนิสึซึเมะ |
9.0 |
8.0 |
นอกจากนี้ปัญหาอีกอย่างที่พบก็คือ ตัวเลขกลายเป็นจำนวนจริงซึ่งมีทศนิยม ทั้งๆที่ตอนแรกเป็นจำนวนเต็มอยู่ ที่เป็นแบบนี้เพราะเมื่อมี NaN อยู่ด้วยข้อมูลจะกลายเป็นชนิดจำนวนจริง ไม่สามารถเป็นจำนวนเต็มได้
อีกทั้งจะเห็นได้ว่าลำดับของข้อมูลถูกเรียงสับเปลี่ยนใหม่ไม่ได้เป็นไปตามลำดับที่เรียงในตอนแรก
ทางแก้ก็คือใช้ reindex ช่วย โดยปรับทั้งสองเดตาเฟรมให้มีข้อมูลเหมือนกันโดยที่ใช้ fill_value=0 เพื่อเติมข้อมูลที่ขาดไป จากนั้นจึงค่อยมาบวก
ตัวอย่างเช่น หากเราต้องการให้เรียงดัชนีตามลำดับโปเกมอนของซาโตชิก่อน ส่วนโปเกมอนที่ซาโตชิไม่มีค่อยเรียงตามของชิเงรุก็เขียนแบบนี้
index = pd.Series(list(satoshi.index)+list(shigeru.index)).unique()
satoshi = satoshi.reindex(index,fill_value=0)
shigeru = shigeru.reindex(index,fill_value=0)
print(satoshi+shigeru)
ได้
|
เจอ |
จับได้ |
คาเตอร์ปี |
33 |
27 |
บีเดิล |
65 |
59 |
โป๊ปโป |
88 |
85 |
โครัตตา |
29 |
24 |
โอนิสึซึเมะ |
9 |
8 |
อาร์โบ |
12 |
9 |
จะเห็นว่าการจัดเรียงเป็นไปตามที่ต้องการ อีกทั้งผลที่ได้ก็ยังคงความเป็นจำนวนเต็ม
และนอกจากเมธอด add ที่ใช้กับการบวกแล้ว ในทำนองเดียวกันก็ยังมีการลบ คูณ หาร และอื่นๆ รวมถึงการหาค่าความจริงเท็จก็มีเมธอดที่ใช้แทนได้เช่นกัน ชื่อเมธอดที่ใช้เป็นไปตามนี้
add |
บวก |
+ |
sub |
ลบ |
- |
mul |
คูณ |
* |
div |
หาร |
/ |
floordiv |
หารปัดเศษ |
// |
mod |
หารเอาเศษ |
% |
pow |
ยกกำลัง |
** |
eq |
เท่ากับ |
== |
ne |
ไม่เท่ากับ |
!= |
gt |
มากกว่า |
> |
lt |
น้อยกว่า |
< |
ge |
มากกว่าหรือเท่ากับ |
>= |
le |
น้อยกว่าหรือเท่ากับ |
<= |
ส่วนการเอาเดตาเฟรมมาคำนวณอะไรกับตัวเลขเดียวก็จะเป็นการเอาตัวเลขนั้นมาคำนวณกับทั้งเดตาเฟรม เช่น
satoshi = satoshi.mul(4) # หรือ satoshi*=4
print(satoshi)
ได้
|
เจอ |
จับได้ |
คาเตอร์ปี |
60 |
48 |
บีเดิล |
104 |
92 |
โป๊ปโป |
132 |
132 |
โครัตตา |
116 |
96 |
โอนิสึซึเมะ |
36 |
32 |
อาร์โบ |
0 |
0 |
การทำแบบนี้จะเป็นการเปลี่ยนแปลงทั้งหมด แต่หากต้องการเปลี่ยนแค่แถวใดแถวหนึ่งก็แค่เข้าถึงแค่แถวนั้น
satoshi['เจอ'] = satoshi['เจอ'].add(2)
print(satoshi)
ได้
|
เจอ |
จับได้ |
คาเตอร์ปี |
62 |
48 |
บีเดิล |
106 |
92 |
โป๊ปโป |
134 |
132 |
โครัตตา |
118 |
96 |
โอนิสึซึเมะ |
38 |
32 |
อาร์โบ |
2 |
0 |
การรวบยอดข้อมูล ซีรีส์และเดตาเฟรมมีเมธอดหลายตัวที่ใช้ทำการวิเคราะห์คำนวณข้อมูลในตาราง
ตัวอย่างเมธอดส่วนหนึ่งของเดตาเฟรมที่น่าจะได้ใช้บ่อย
min |
ค่าต่ำสุด |
max |
ค่าสูงสุด |
idxmin |
ค่าดัชนีของค่าต่ำสุด |
idxmax |
ค่าดัชนีของค่าสูงสุด |
sum |
ผลรวม |
mean |
ค่าเฉลี่ย |
median |
มัธยฐาน |
var |
ความแปรปรวน |
std |
ส่วนเบี่ยงเบนมาตรฐาน |
rank |
อันดับของค่าของข้อมูลเมื่อเรียง |
count |
จำนวนข้อมูลที่ไม่ใช่ NaN |
describe |
สรุปรวมค่าทางสถิติหลายอย่าง |
เมธอดเหล่านี้จะทำการรวบยอดข้อมูลทุกแถวในแต่ละคอลัมน์
decribe จะสรุปรวมข้อมูลหลายอย่าง เช่น count mean std min max
ตัวอย่าง จำนวนโปเกมอนที่จับได้ในสถานที่ต่างๆในเชียงใหม่เมื่อเล่นโปเกมอนโก
import numpy as np
pokemon = pd.DataFrame(
[[12,14,5],[16,10,6],[np.NaN,8,17],[3,2,7],[13,1,9]],
columns=['กาดรินคำ','กาดสวนแก้ว','กาดหลวง'],
index=['นาโซโนะคุสะ','คงปัง','เนียวโรโม','มาดัตสึโบมิ','ทามะทามะ'])
print(pokemon)
ได้
|
กาดรินคำ |
กาดสวนแก้ว |
กาดหลวง |
นาโซโนะคุสะ |
12.0 |
14 |
5 |
คงปัง |
16.0 |
10 |
6 |
เนียวโรโม |
NaN |
8 |
17 |
มาดัตสึโบมิ |
3.0 |
2 |
7 |
ทามะทามะ |
13.0 |
1 |
9 |
ลองใช้ describe
print(pokemon.describe())
ได้
|
กาดรินคำ |
กาดสวนแก้ว |
กาดหลวง |
count |
4.000000 |
5.000000 |
5.000000 |
mean |
11.000000 |
7.000000 |
8.800000 |
std |
5.597619 |
5.477226 |
4.816638 |
min |
3.000000 |
1.000000 |
5.000000 |
25% |
NaN |
2.000000 |
6.000000 |
50% |
NaN |
8.000000 |
7.000000 |
75% |
NaN |
10.000000 |
9.000000 |
max |
16.000000 |
14.000000 |
17.000000 |
rank จะแสดงลำดับที่ได้เมื่อมีการจัดเรียงค่า ซึ่งถ้าใช้เมธอด sort_values เพื่อเรียงก็จะถูกเรียงตามนั้น คีย์เวิร์ดที่ใช้ใน sort_values ได้เช่น ascending ก็สามารถใช้ใน rank ได้
ตัวอย่าง อันดับโปเกมอนที่เจอเป็นจำนวนมากในสถานที่ต่างๆ
print(pokemon.rank(ascending=0))
ได้
|
กาดรินคำ |
กาดสวนแก้ว |
กาดหลวง |
นาโซโนะคุสะ |
3.0 |
1.0 |
5.0 |
คงปัง |
1.0 |
2.0 |
4.0 |
เนียวโรโม |
NaN |
3.0 |
1.0 |
มาดัตสึโบมิ |
4.0 |
4.0 |
3.0 |
ทามะทามะ |
2.0 |
5.0 |
2.0 |
เมธอดต่างๆสามารถใส่คีย์เวิร์ดเพื่อปรับเปลี่ยนค่าได้ต่างกันออกไป เช่น axis ถ้าใส่ axis=1 จะเป็นการรวดยอดข้อมูลทุกคอลัมน์ในแต่ละแถว ในขณะที่ปกติถ้าไม่ใส่หรือใส่ axis=0 จะเป็นการรวบยอดข้อมูลทุกแถวในแต่ละคอลัมน์
เมธอดส่วนใหญ่ที่ยกมานั้นจะมีคีย์เวิร์ด axis เช่นลองดู idxmax ถ้าปกติจะคืนค่าโปเกมอนที่เจอมากที่สุดในแต่ละสถานที่ แต่ถ้าเติม axis=1 จะได้ค่าสถานที่ที่เจอโปเกมอนแต่ละตัวมากที่สุด
print('>>>> axis=0 <<<<')
print(pokemon.idxmax())
print('\n>>>> axis=1 <<<<')
print(pokemon.idxmax(axis=1))
ได้
>>>> axis=0 <<<<
กาดรินคำ คงปัง
กาดสวนแก้ว นาโซโนะคุสะ
กาดหลวง เนียวโรโม
dtype: object
>>>> axis=1 <<<<
นาโซโนะคุสะ กาดสวนแก้ว
คงปัง กาดรินคำ
เนียวโรโม กาดหลวง
มาดัตสึโบมิ กาดหลวง
ทามะทามะ กาดรินคำ
dtype: object
อ้างอิง