ใน
บทที่แล้วได้เขียนถึงวิธีการเอาข้อมูลจากตารางสองอันขึ้นไปมารวมเข้าด้วยกันแล้ว สำหรับในบทนี้จะเป็นเรื่องของการรวมสองตารางเข้าด้วยกันเช่นกันเพียงแต่ว่าเป็นการรวมในอีกลักษณะหนึ่ง นั่นคือการเชื่อมโดยผ่านความสัมพันธ์ของค่าภายในตาราง
การเชื่อมข้อมูลโดยผ่านคอลัมน์ที่ชื่อเหมือนกัน หากมีตารางเดตาเฟรมสองอันแล้วต้องการนำข้อมูลมาเชื่อมต่อรวมกันโดยอาศัยคอลัมน์หนึ่งเป็นตัวเชื่อมสามารถใช้ฟังก์ชัน pd.merge
ตัวอย่าง มีข้อมูลของโปเกมอนอยู่สองตาราง ตารางแรกบอกชนิดของโปเกมอน ส่วนอีกตารางบอกส่วนสูงและน้ำหนัก ทั้งสองตารางมีสองแถวแรกเป็นหมายเลขและสายพันธุ์ของโปเกมอนเหมือนกัน
import pandas as pd
pokemon_chanit = pd.DataFrame([
[69,'มาดัตสึโบมิ','พืช/พิษ'],
[72,'เมโนคุราเงะ','น้ำ/พิษ'],
[74,'อิชิตสึบุเตะ','หิน/ดิน']],
columns=['หมายเลข','สายพันธุ์','ชนิด'])
pokemon_sungnak = pd.DataFrame([
[69,'มาดัตสึโบมิ',0.7,4.0],
[72,'เมโนคุราเงะ',0.9,45.5],
[74,'อิชิตสึบุเตะ',0.4,20.0]],
columns=['หมายเลข','สายพันธุ์','ส่วนสูง','น้ำหนัก'])
print(pokemon_chanit)
print(pokemon_sungnak)
print(pd.merge(pokemon_chanit,pokemon_sungnak))
ได้
|
หมายเลข |
สายพันธุ์ |
ชนิด |
0 |
69 |
มาดัตสึโบมิ |
พืช/พิษ |
1 |
72 |
เมโนคุราเงะ |
น้ำ/พิษ |
2 |
74 |
อิชิตสึบุเตะ |
หิน/ดิน |
|
หมายเลข |
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
0 |
69 |
มาดัตสึโบมิ |
0.7 |
4.0 |
1 |
72 |
เมโนคุราเงะ |
0.9 |
45.5 |
2 |
74 |
อิชิตสึบุเตะ |
0.4 |
20.0 |
|
หมายเลข |
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
0 |
69 |
มาดัตสึโบมิ |
พืช/พิษ |
0.7 |
4.0 |
1 |
72 |
เมโนคุราเงะ |
น้ำ/พิษ |
0.9 |
45.5 |
2 |
74 |
อิชิตสึบุเตะ |
หิน/ดิน |
0.4 |
20.0 |
จะเห็นว่าพอรวมกันแล้วได้ตารางที่มีชนิด, ส่วนสูง และน้ำหนัก โดยที่หมายเลขและสายพันธุ์ก็ยังอยู่และเหมือนเดิม ในที่นี้หมายเลขและสายพันธุ์ถูกใช้เป็นตัวเชื่อม
ปกติถ้าไม่ได้ระบุอะไรละก็คอลัมน์ที่ซ้ำกันในทั้งสองตารางจะถูกใช้เป็นตัวเชื่อม แต่หากระบุไว้คอลัมน์ที่ถูกใช้เชื่อมก็จะมีแค่ตัวนั้น
การระบุตัวเชื่อมทำได้โดยใส่คีย์เวิร์ด on เช่นลอง
print(pd.merge(pokemon_chanit,pokemon_sungnak,on='สายพันธุ์'))
ได้
|
หมายเลข_x |
สายพันธุ์ |
ชนิด |
หมายเลข_y |
ส่วนสูง |
น้ำหนัก |
0 |
69 |
มาดัตสึโบมิ |
พืช/พิษ |
69 |
0.7 |
4.0 |
1 |
72 |
เมโนคุราเงะ |
น้ำ/พิษ |
72 |
0.9 |
45.5 |
2 |
74 |
อิชิตสึบุเตะ |
หิน/ดิน |
74 |
0.4 |
20.0 |
จะเห็นว่าในที่นี้กำหนดให้ใช้แค่ "สายพันธุ์" เป็นตัวเชื่อม "หมายเลข" ไม่ได้เป็นตัวเชื่อมแล้วเลยถูกแยกออกเป็น ๒ อันแม้ว่าค่าจะเหมือนกันอยู่ก็ตาม โดยถูกเติมชื่อท้ายให้โดยอัตโนมัติ
ชื่อท้ายที่ถูกเติมสามารถกำหนดเองตามที่ต้องการได้โดยใส่คีย์เวิร์ด suffixes เป็นลิสต์ของสายอักขระที่ต้องการเติมต่อท้ายทั้งสองอัน เช่น
print(pd.merge(pokemon_chanit,pokemon_sungnak,on='สายพันธุ์',suffixes=[' ^_^',' +_+']))
ได้
|
หมายเลข ^_^ |
สายพันธุ์ |
ชนิด |
หมายเลข +_+ |
ส่วนสูง |
น้ำหนัก |
0 |
69 |
มาดัตสึโบมิ |
พืช/พิษ |
69 |
0.7 |
4.0 |
1 |
72 |
เมโนคุราเงะ |
น้ำ/พิษ |
72 |
0.9 |
45.5 |
2 |
74 |
อิชิตสึบุเตะ |
หิน/ดิน |
74 |
0.4 |
20.0 |
กรณีที่คอลัมน์ที่ใช้เชื่อมมีค่าซ้ำกัน ความซับซ้อนมากขึ้นในการเชื่อมโยงตารางจะเกิดขึ้นเมื่อคอลัมน์ที่ใช้เชื่อมนั้นมีที่ค่าเหมือนกันสองตัวขึ้นไป กรณีแบบนี้ข้อมูลก็จะเกิดการซ้ำตามไปด้วย
ลองแยกดูเป็นกรณี เริ่มจากกรณีที่มีการซ้ำแค่ในตารางใดตารางหนึ่ง
ตัวอย่าง สร้างตารางขึ้นมาสองอัน ตารางแรกเป็นข้อมูลโปเกมอนที่มีอยู่ อีกตารางเป็นข้อมูลของโปเกมอนแต่ละสายพันธุ์
pokemon_thimi = pd.DataFrame([
['พูพุริน','พูคุง',11],
['พิชู','พีโกะ',15],
['พิชู','พีสึเกะ',6],
['พูพุริน','พูจัง',3],
['โทเงปี','โทโทโระ',19],
['พิชู','พีนัต',7]],
columns=['สายพันธุ์','ชื่อเล่น','เลเวล'])
khomun_pokemon = pd.DataFrame([
['พิชู','ไฟฟ้า',0.3,2.0],
['พี','ภูต',0.3,3.0],
['พูพุริน','ธรรมดา/ภูต',0.3,1.0],
['โทเงปี','ภูต',0.3,1.5]],
columns=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'])
print(pokemon_thimi)
print(khomun_pokemon)
ได้
|
สายพันธุ์ |
ชื่อเล่น |
เลเวล |
0 |
พูพุริน |
พูคุง |
11 |
1 |
พิชู |
พีโกะ |
15 |
2 |
พิชู |
พีสึเกะ |
6 |
3 |
พูพุริน |
พูจัง |
3 |
4 |
โทเงปี |
โทโทโระ |
19 |
5 |
พิชู |
พีนัต |
7 |
|
สายพันธุ์ |
ชนิด |
ส่วนสูง |
น้ำหนัก |
0 |
พิชู |
ไฟฟ้า |
0.3 |
2.0 |
1 |
พี |
ภูต |
0.3 |
3.0 |
2 |
พูพุริน |
ธรรมดา/ภูต |
0.3 |
1.0 |
3 |
โทเงปี |
ภูต |
0.3 |
1.5 |
จากนั้นก็ลองเอามารวมกัน
print(pd.merge(pokemon_thimi,khomun_pokemon))
ได้
|
สายพันธุ์ |
ชื่อเล่น |
เลเวล |
ชนิด |
ส่วนสูง |
น้ำหนัก |
0 |
พูพุริน |
พูคุง |
11 |
ธรรมดา/ภูต |
0.3 |
1.0 |
1 |
พูพุริน |
พูจัง |
3 |
ธรรมดา/ภูต |
0.3 |
1.0 |
2 |
พิชู |
พีโกะ |
15 |
ไฟฟ้า |
0.3 |
2.0 |
3 |
พิชู |
พีสึเกะ |
6 |
ไฟฟ้า |
0.3 |
2.0 |
4 |
พิชู |
พีนัต |
7 |
ไฟฟ้า |
0.3 |
2.0 |
5 |
โทเงปี |
โทโทโระ |
19 |
ภูต |
0.3 |
1.5 |
จะเห็นว่าข้อมูลจากตารางอันหลังจะปรากฏซ้ำหลายตัวในตารางที่ได้จากการรวมนี้
ซึ่งความหมายในที่นี้ก็คือ สมมุติว่าตอนแรกเรารู้ว่าเรามีโปเกมอน ๖ ตัวนี้อยู่ รู้ว่ามันเป็นสายพันธุ์อะไร แต่ไม่รู้ว่ามันเป็นชนิดอะไรส่วนสูงน้ำหนักเท่าไหร่ เราก็ต้องไปเปิดดูตารางข้อมูลโปเกมอนว่าสายพันธุ์นี้มีน้ำหนักเท่าไหร่สูงเท่าไหร่เป็นชนิดไหน
ในที่นี้พอรู้ว่าโปเกมอนตัวนั้นเป็นพิชู ไม่ว่ามันจะชื่อเล่นว่าอะไร เลเวลเท่าไหร่ ถ้ามันเป็นพิชูมันก็จะต้องเป็นชนิดไฟฟ้า สูง 0.3 หนัก 2.0 ดังนั้นข้อมูลตรงส่วนนี้จึงออกมาซ้ำกันทั้งหมด
ตรงจุดนี้เราจะเห็นได้ถึงประโยชน์ของการเชื่อมข้อมูล เพราะแทนที่เราจะต้องเก็บข้อมูลของพีชูแต่ละตัวซึ่งซ้ำๆกันทั้งหมด (ชนิด, ส่วนสูง, น้ำหนัก) เราก็เก็บแค่ที่ไม่ซ้ำกันไว้ในตารางหนึ่ง (ชื่อเล่น,เลเวล) แล้วเก็บที่ซ้ำไว้ในอีกตารางโดยเก็บแค่แถวเดียว จากนั้นพอต้องการก็ค่อยเอาตารางข้อมูลมาเชื่อมกัน แบบนี้ก็จะประหยัดพื้นที่เก็บข้อมูลได้มากมาย
อีกกรณีหนึ่งคือถ้าหากทั้งสองตารางต่างก็มีค่าของคอลัมน์นั้นซ้ำหลายค่าเหมือนกัน
chanit = pd.DataFrame([
['มิโนมุจจิ','แมลง'],
['มิโนมาดาม','แมลง/พืช'],
['มิโนมาดาม','แมลง/ดิน'],
['มิโนมาดาม','แมลง/เหล็ก'],
['การ์เมล','แมลง/บิน']],
columns=['สายพันธุ์','ชนิด'])
phalang = pd.DataFrame([
['มิโนมุจจิ',29,45],
['มิโนมาดาม',59,85],
['มิโนมาดาม',79,105],
['มิโนมาดาม',69,95],
['การ์เมล',94,50]],
columns=['สายพันธุ์','พลังโจมตี','พลังป้องกัน'])
print(chanit)
print(phalang)
ได้
|
สายพันธุ์ |
ชนิด |
0 |
มิโนมุจจิ |
แมลง |
1 |
มิโนมาดาม |
แมลง/พืช |
2 |
มิโนมาดาม |
แมลง/ดิน |
3 |
มิโนมาดาม |
แมลง/เหล็ก |
4 |
การ์เมล |
แมลง/บิน |
|
สายพันธุ์ |
พลังโจมตี |
พลังป้องกัน |
0 |
มิโนมุจจิ |
29 |
45 |
1 |
มิโนมาดาม |
59 |
85 |
2 |
มิโนมาดาม |
79 |
105 |
3 |
มิโนมาดาม |
69 |
95 |
4 |
การ์เมล |
94 |
50 |
โปเกมอนที่ชื่อมิโนมาดามมีอยู่ ๓ แบบ แต่ละแบบมีชนิดต่างกัน พลังโจมตีและพลังป้องกันก็ต่างกันด้วย ดังนั้นทั้ง ๒ ตารางนี้จึงมีมิโนมาดามอยู่ ๓ แถว
เมื่อลองมารวมกันดู
print(pd.merge(chanit,phalang))
จะได้
|
สายพันธุ์ |
ชนิด |
พลังโจมตี |
พลังป้องกัน |
0 |
มิโนมุจจิ |
แมลง |
29 |
45 |
1 |
มิโนมาดาม |
แมลง/พืช |
59 |
85 |
2 |
มิโนมาดาม |
แมลง/พืช |
79 |
105 |
3 |
มิโนมาดาม |
แมลง/พืช |
69 |
95 |
4 |
มิโนมาดาม |
แมลง/ดิน |
59 |
85 |
5 |
มิโนมาดาม |
แมลง/ดิน |
79 |
105 |
6 |
มิโนมาดาม |
แมลง/ดิน |
69 |
95 |
7 |
มิโนมาดาม |
แมลง/เหล็ก |
59 |
85 |
8 |
มิโนมาดาม |
แมลง/เหล็ก |
79 |
105 |
9 |
มิโนมาดาม |
แมลง/เหล็ก |
69 |
95 |
10 |
การ์เมล |
แมลง/บิน |
94 |
50 |
ผลที่ได้พบว่ามีมิโดมาดาม ๙ แถว ซึ่งได้จากการแจกแจงตัวที่ซ้ำจากทั้งสองฝั่ง
ที่จริงเราอาจจะต้องการให้ข้อมูลทั้ง ๓ แถวนั้นแค่รวมกันโดยเรียงตามลำดับ แต่ในความเป็นจริง pd.merge จะไม่ได้มองแบบนั้น เมื่อเจอค่าที่ซ้ำกันมันไม่อาจรู้ได้ว่าเราต้องการให้แถวไหนเชื่อมกับแถวไหน ทำได้ก็เพียงแค่แจกแจงมันทั้งหมดเลย
หากต้องการจะแค่เอาคอลัมน์มาต่อกันจริงๆอาจลองใช้ concat จะเหมาะกว่า
print(pd.concat([chanit,phalang],axis=1))
ได้
|
สายพันธุ์ |
ชนิด |
สายพันธุ์ |
พลังโจมตี |
พลังป้องกัน |
0 |
มิโนมุจจิ |
แมลง |
มิโนมุจจิ |
29 |
45 |
1 |
มิโนมาดาม |
แมลง/พืช |
มิโนมาดาม |
59 |
85 |
2 |
มิโนมาดาม |
แมลง/ดิน |
มิโนมาดาม |
79 |
105 |
3 |
มิโนมาดาม |
แมลง/เหล็ก |
มิโนมาดาม |
69 |
95 |
4 |
การ์เมล |
แมลง/บิน |
การ์เมล |
94 |
50 |
รูปแบบของการเชื่อมโยง จากตัวอย่างที่ผ่านมา ข้อมูลในคอลัมน์ที่ใช้เชื่อมจากทั้ง ๒ ตารางนั้นส่วนใหญ่ต่างมีค่าเหมือนกันหมด แต่ในความเป็นจริงไม่จำเป็นต้องเป็นเช่นนั้น ในตารางหนึ่งอาจมีค่าที่ในอีกตารางไม่มีอยู่
ตัวอย่าง มีตารางข้อมูลโปเกมอนสองตาราง อันนึงให้ข้อมูลส่วนสูงและน้ำหนัก อีกอันเป็นข้อมูลค่าพลังต่างๆ ทั้งสองอันนี้มีสมาชิกบางตัวไม่เหมือนกัน
sungnak = pd.DataFrame([
['โซนาโน',0.6,14.0],
['โซนานส์',1.3,28.5],
['ยุกิวาราชิ',0.7,16.8],
['โอนิโกริ',1.5,256.5]],
columns=['สายพันธุ์','ส่วนสูง','น้ำหนัก'])
phalang = pd.DataFrame([
['โซนาโน',23,48,95],
['ยุกิวาราชิ',50,50,50],
['โอนิโกริ',80,80,80],
['ยุกิเมะโนะโกะ',70,80,70]],
columns=['สายพันธุ์','พลังโจมตี','พลังป้องกัน','HP'])
print(sungnak)
print(phalang)
ได้
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
0 |
โซนาโน |
0.6 |
14.0 |
1 |
โซนานส์ |
1.3 |
28.5 |
2 |
ยุกิวาราชิ |
0.7 |
16.8 |
3 |
โอนิโกริ |
1.5 |
256.5 |
|
สายพันธุ์ |
พลังโจมตี |
พลังป้องกัน |
HP |
0 |
โซนาโน |
23 |
48 |
95 |
1 |
ยุกิวาราชิ |
50 |
50 |
50 |
2 |
โอนิโกริ |
80 |
80 |
80 |
3 |
ยุกิเมะโนะโกะ |
70 |
80 |
70 |
ลองเอามาเชื่อมกัน
print(pd.merge(sungnak,phalang))
ผลที่ได้
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
พลังโจมตี |
พลังป้องกัน |
HP |
0 |
โซนาโน |
0.6 |
14.0 |
23 |
48 |
95 |
1 |
ยุกิวาราชิ |
0.7 |
16.8 |
50 |
50 |
50 |
2 |
โอนิโกริ |
1.5 |
256.5 |
80 |
80 |
80 |
จะเห็นว่าข้อมูลที่ไม่ได้มีอยู่ในทั้งสองตารางเหมือนกันจะหายไปเลย ทำให้เหลือแค่ ๓ แถว
ทั้งนี้เป็นเพราะว่าโดยทั่วไปแล้วการเชื่อมโยงตารางถูกตั้งค่าเริ่มต้นให้เป็นแบบ inner นั่นคือเก็บเฉพาะที่มีเหมือนกันทั้งสองตาราง
แต่นอกจากนี้แล้วก็ยังมีอีก ๓ แบบ คือ
left เก็บที่ตัวซ้ายมีทั้งหมดโดยไม่สนตัวขวา
right เก็บที่ตัวขวามีทั้งหมดโดยไม่สนตัวซ้าย
outer เก็บทั้งที่ตัวซ้ายมีและตัวขวามี
สามารถกำหนดว่าจะเชื่อมโยงแบบไหนโดยตั้งที่คีย์เวิร์ด how โดยที่ถ้าไม่ใส่จะเท่ากับ how='inner'
ตัวอย่าง
print(pd.merge(sungnak,phalang,how='outer'))
ได้
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
พลังโจมตี |
พลังป้องกัน |
HP |
0 |
โซนาโน |
0.6 |
14.0 |
23.0 |
48.0 |
95.0 |
1 |
โซนานส์ |
1.3 |
28.5 |
NaN |
NaN |
NaN |
2 |
ยุกิวาราชิ |
0.7 |
16.8 |
50.0 |
50.0 |
50.0 |
3 |
โอนิโกริ |
1.5 |
256.5 |
80.0 |
80.0 |
80.0 |
4 |
ยุกิเมะโนะโกะ |
NaN |
NaN |
70.0 |
80.0 |
70.0 |
จะเห็นว่าข้อมูลที่มีอยู่แค่ฝั่งใดฝั่งหนึ่งก็มาโผล่ในนี้ด้วย แต่ส่วนที่ขาดไปก็จะขึ้นเป็น NaN
นอกจากนี้หากลอง left กับ right ก็ออกมาในทำนองเดียวกัน
print('ซ้าย')
print(pd.merge(sungnak,phalang,how='left'))
print('ขวา')
print(pd.merge(sungnak,phalang,how='right'))
ได้
ซ้าย
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
พลังโจมตี |
พลังป้องกัน |
HP |
0 |
โซนาโน |
0.6 |
14.0 |
23.0 |
48.0 |
95.0 |
1 |
โซนานส์ |
1.3 |
28.5 |
NaN |
NaN |
NaN |
2 |
ยุกิวาราชิ |
0.7 |
16.8 |
50.0 |
50.0 |
50.0 |
3 |
โอนิโกริ |
1.5 |
256.5 |
80.0 |
80.0 |
80.0 |
ขวา
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
พลังโจมตี |
พลังป้องกัน |
HP |
0 |
โซนาโน |
0.6 |
14.0 |
23 |
48 |
95 |
1 |
ยุกิวาราชิ |
0.7 |
16.8 |
50 |
50 |
50 |
2 |
โอนิโกริ |
1.5 |
256.5 |
80 |
80 |
80 |
3 |
ยุกิเมะโนะโกะ |
NaN |
NaN |
70 |
80 |
70 |
เมื่อชื่อคอลัมน์ที่ต้องการเชื่อมไม่เหมือนกันในสองตาราง ไม่ใช่ว่าสองคอลัมน์จากสองตารางที่เอามาเชื่อมกันจะมีค่าชื่อคอลัมน์เหมือนกันเสมอไป ในกรณีที่ต่างกันแบบนี้จะต้องกำหนดชื่อคอลัมน์ที่ต้องการเชื่อมโดยใช้คีย์เวิร์ด left_on=ชื่อคอลัมน์ในตารางซ้าย และ right_on=ชื่อคอลัมน์ในตารางขวา
ตัวอย่าง
chue = pd.DataFrame([
[480,'ยุกซี'],
[481,'เอมริต'],
[482,'อักนอม']],
columns=['เลข','สายพันธุ์'])
phalang = pd.DataFrame([
[480,75,130],
[481,105,105],
[482,125,70]],
columns=['หมายเลข','พลังโจมตี','พลังป้องกัน'])
print(chue)
print(phalang)
ได้
|
เลข |
สายพันธุ์ |
0 |
480 |
ยุกซี |
1 |
481 |
เอมริต |
2 |
482 |
อักนอม |
|
หมายเลข |
พลังโจมตี |
พลังป้องกัน |
0 |
480 |
75 |
130 |
1 |
481 |
105 |
105 |
2 |
482 |
125 |
70 |
ในที่นี้จะเชื่อมสองตารางซ้ายใช้ "เลข" ตารางขวาใช้ "หมายเลข"
print(pd.merge(chue,phalang,left_on='เลข',right_on='หมายเลข'))
ผลที่ได้จะเห็นทั้งสองคอลัมน์อยู่พร้อมกันโดยมีค่าเท่ากัน
|
เลข |
สายพันธุ์ |
หมายเลข |
พลังโจมตี |
พลังป้องกัน |
0 |
480 |
ยุกซี |
480 |
75 |
130 |
1 |
481 |
เอมริต |
481 |
105 |
105 |
2 |
482 |
อักนอม |
482 |
125 |
70 |
การใช้ดัชนีเป็นตัวเชื่อม ปกติแล้ว merge จะใช้คอลัมน์อันหนึ่งหรือจำนวนหนึ่งเป็นตัวเชื่อมโยงข้อมูลสองตารางเข้าด้วยกัน โดยที่ค่าดัชนีจะถูกเพิกเฉยและโยนทิ้งไปเลย
แต่หากต้องการใช้ดัชนีมาเป็นตัวเชื่อมก็ทำได้เช่นกัน โดยจะต้องใส่คีย์เวิร์ด left_index=1 หากต้องการให้ตัวซ้ายใช้ดัชนี และ right_index=1 หากต้องการให้ตัวขวาใช้ดัชนี
ส่วนอีกตัวที่ไม่ได้ใช้ดัชนีก็ให้กำหนดคอลัมน์ที่ต้องการเชื่อมด้วย left_on และ right_on
ตัวอย่าง
pokemon = pd.DataFrame([
['ชิโครีตา','พืช'],['ฮิโนอาราชิ','ไฟ'],['วานิโนโกะ','น้ำ'],
['คิโมริ','พืช'],['อาชาโม','ไฟ'],['มิซึโงโรว','น้ำ']],
columns=['สายพันธุ์','ชนิด'],
index=[152,155,158,252,255,258])
phaechana = pd.DataFrame([
['เสมอ','แพ้','ชนะ'],
['ชนะ','เสมอ','แพ้'],
['แพ้','ชนะ','เสมอ'],
['แพ้','ชนะ','แพ้']],
columns=['เจอพืช','เจอไฟ','เจอน้ำ'],
index=['พืช','ไฟ','น้ำ','ดิน'])
print(pokemon)
print(phaechana)
print(pd.merge(pokemon,phaechana,left_on='ชนิด',right_index=1))
ได้
|
สายพันธุ์ |
ชนิด |
152 |
ชิโครีตา |
พืช |
155 |
ฮิโนอาราชิ |
ไฟ |
158 |
วานิโนโกะ |
น้ำ |
252 |
คิโมริ |
พืช |
255 |
อาชาโม |
ไฟ |
258 |
มิซึโงโรว |
น้ำ |
|
เจอพืช |
เจอไฟ |
เจอน้ำ |
พืช |
เสมอ |
แพ้ |
ชนะ |
ไฟ |
ชนะ |
เสมอ |
แพ้ |
น้ำ |
แพ้ |
ชนะ |
เสมอ |
ดิน |
แพ้ |
ชนะ |
แพ้ |
|
สายพันธุ์ |
ชนิด |
เจอพืช |
เจอไฟ |
เจอน้ำ |
152 |
ชิโครีตา |
พืช |
เสมอ |
แพ้ |
ชนะ |
252 |
คิโมริ |
พืช |
เสมอ |
แพ้ |
ชนะ |
155 |
ฮิโนอาราชิ |
ไฟ |
ชนะ |
เสมอ |
แพ้ |
255 |
อาชาโม |
ไฟ |
ชนะ |
เสมอ |
แพ้ |
158 |
วานิโนโกะ |
น้ำ |
แพ้ |
ชนะ |
เสมอ |
258 |
มิซึโงโรว |
น้ำ |
แพ้ |
ชนะ |
เสมอ |
แต่หากใช้ left_index=1 และ right_index=1 พร้อมกันผลที่ได้จะไม่ต่างจาก concat
สรุปคีย์เวิร์ดทั้งหมด
คีย์เวิร์ด |
ความหมาย |
ค่าตั้งต้น |
how |
วิธีการในการรวม |
inner |
on |
ชื่อคอลัมน์ที่ใช้เป็นตัวเชื่อม ไม่ใช้พร้อมกับ left_on และ right_on |
คอลัมน์ที่ชื่อซ้ำกันทั้งหมด |
left_on |
ชื่อคอลัมน์ของตารางซ้ายที่ใช้เป็นตัวเชื่อม ไม่ใช้พร้อมกับ on |
|
right_on |
ชื่อคอลัมน์ของตารางขวาที่ใช้เป็นตัวเชื่อม ไม่ใช้พร้อมกับ on |
|
left_index |
ใชัดัชนีของตารางซ้ายเป็นตัวเชื่อม |
0 |
right_index |
ใชัดัชนีของตารางขวาเป็นตัวเชื่อม |
0 |
sort |
จัดเรียงแถวในตารางตามค่าดัชนีด้วย หลังจากที่เชื่อมเสร็จ |
0 |
suffixes |
คำต่อท้ายหากชื่อคอลัมน์ที่ไม่ใช่ตัวเชื่อมเกิดซ้ำกัน |
[_x,_y] |
การใช้เมธอด join นอกจากจะใช้ฟังก์ชัน pd.merge ในการเชื่อมตารางแล้ว ยังอาจใช้เมธอด join ของตัวเดตาเฟรมได้ วิธีการใช้ใกล้เคียงกัน เพียงแต่จะใช้เดตาเฟรมตัวหนึ่งเป็นฐานของเมธอด แล้วใช้อีกอันเป็นอาร์กิวเมนต์
join ยังต่างจาก pd.merge ตรงที่ว่าหากไม่ระบุคีย์เวิร์ด on จะเป็นการเชื่อมโดยใช้ดัชนีเป็นตัวเชื่อม ต่างจาก pd.merge ที่ใช้คอลัมน์ที่ซ้ำ
ตัวอย่าง
sungnak = pd.DataFrame([
['เดียลกา',5.4,683.0],
['พาลเกีย',4.2,336.0],
['กิราทีนา',4.5,750.0]],
columns=['สายพันธุ์','ส่วนสูง','น้ำหนัก'],
index=[483,484,487])
phalang = pd.DataFrame([
[120,120],[120,100],[100,120]],
columns=['พลังโจมตี','พลังป้องกัน'],
index=[483,484,487])
print(sungnak)
print(phalang)
print(sungnak.join(phalang))
ได้
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
483 |
เดียลกา |
5.4 |
683.0 |
484 |
พาลเกีย |
4.2 |
336.0 |
487 |
กิราทีนา |
4.5 |
750.0 |
|
พลังโจมตี |
พลังป้องกัน |
483 |
120 |
120 |
484 |
120 |
100 |
487 |
100 |
120 |
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
พลังโจมตี |
พลังป้องกัน |
483 |
เดียลกา |
5.4 |
683.0 |
120 |
120 |
484 |
พาลเกีย |
4.2 |
336.0 |
120 |
100 |
487 |
กิราทีนา |
4.5 |
750.0 |
100 |
120 |
ถ้าหากใส่คีย์เวิรืด on ลงไปจะเป็นการระบุคอลัมน์ของเดตาเฟรมที่เป็นเจ้าของเมธอด ส่วนเดตาเฟรมที่มาเติมนั้นยังไงก็ยังต้องเติมด้วยค่าดัชนี ไม่สามารถเชื่อมตามคอลัมน์ได้
ตัวอย่าง ถ้าเรากำหนดเดตาเฟรมตัวที่มาเติมใหม่เป็น
phalang2 = pd.DataFrame([
[120,120],[120,100],[100,120]],
columns=['พลังโจมตี','พลังป้องกัน'],
index=['เดียลกา','พาลเกีย','กิราทีนา'])
print(phalang2)
ได้
|
พลังโจมตี |
พลังป้องกัน |
เดียลกา |
120 |
120 |
พาลเกีย |
120 |
100 |
กิราทีนา |
100 |
120 |
ต่างจากตัวอย่างที่แล้วตรงที่ว่า index ไม่ใช่หมายเลขแต่กลายเป็นชื่อสายพันธุ์ของโปเกมอน แบบนี้จะต้องพิมพ์
print(sungnak.join(phalang2,on='สายพันธุ์'))
ก็จะได้ผลเหมือนกับตัวอย่างที่แล้ว
และ join ไม่ได้ใช้แค่กับเดตาเฟรมแต่ยังใช้กับซีรีส์ได้ด้วย โดยจะเป็นการเพิ่มคอลัมน์ให้กับเดตาเฟรม อีกทั้งยังใส่หลายตัวพร้อมกันได้ด้วย โดยต้องใส่เป็นลิสต์ของเดตาเฟรมหรือซีรีส์ที่ต้องการเพิ่ม
ตัวอย่าง ลองสร้างซีรีส์ที่เก็บชนิดของโปเกมอน
chanit = pd.Series(['เหล็ก/มังกร','น้ำ/มังกร','ผี/มังกร'],
index=[483,484,487],name='ชนิด')
print(chanit)
ได้
483 เหล็ก/มังกร
484 น้ำ/มังกร
487 ผี/มังกร
Name: ชนิด, dtype: object
จากนั้นก็นำมาใช้เชื่อมกับข้อมูลของสองตารางที่สร้างขึ้นมาก่อนหน้าด้วย เป็นการเชื่อมข้อมูล ๓ ตารางในทีเดียว
print(phalang.join([chanit,sungnak]))
ได้
|
พลังโจมตี |
พลังป้องกัน |
ชนิด |
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
483 |
120 |
120 |
เหล็ก/มังกร |
เดียลกา |
5.4 |
683.0 |
484 |
120 |
100 |
น้ำ/มังกร |
พาลเกีย |
4.2 |
336.0 |
487 |
100 |
120 |
ผี/มังกร |
กิราทีนา |
4.5 |
750.0 |
สามารถใช้คีย์เวิร์ด how เพื่อกำหนดรูปแบบการเชื่อมโยงได้ เพียงแต่ว่าหากไม่กำหนดไว้ค่าเริ่มต้นจะเป็น left นั่นคือยึดเอาเดตาเฟรามที่เป็นฐานของเมธอดเป็นหลัก
นอกจากนี้ยังมีข้อแตกต่างเล็กๆน้อยๆอีกเช่น คีย์เวิร์ด suffixes ใน pd.merge นั้นจะเทียบเท่ากับคีย์เวิร์ด lsuffix กับ rsuffix ของ join กล่าวคือ join ใช้คีย์เวิร์ดแยก ๒ ตัวแทนที่จะรวมกันเป็นลิสต์
ตัวอย่าง
pokemon1 = pd.DataFrame([
['มานิวลา',1.1,34.0,'มาร'],
['จิบะคอยล์',1.2,180.0,'ไฟฟ้า'],
['เบโรเบลต์',1.7,140.0,'ธรรมดา'],
['โดไซดอน',2.4,282.8,'ดิน']],
columns=['สายพันธุ์','ส่วนสูง','น้ำหนัก','ชนิด'],
index=[461,462,463,464])
pokemon2 = pd.DataFrame([
['น้ำแข็ง',120,65],
['เหล็ก',70,115],
['หิน',140,130],
['พืช',100,125]],
columns=['ชนิด','พลังโจมตี','พลังป้องกัน'],
index=[461,462,464,465])
print(pokemon1)
print(pokemon2)
print(pokemon1.join(pokemon2,lsuffix=' 1',rsuffix=' 2'))
ได้
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
ชนิด |
461 |
มานิวลา |
1.1 |
34.0 |
มาร |
462 |
จิบะคอยล์ |
1.2 |
180.0 |
ไฟฟ้า |
463 |
เบโรเบลต์ |
1.7 |
140.0 |
ธรรมดา |
464 |
โดไซดอน |
2.4 |
282.8 |
ดิน |
|
ชนิด |
พลังโจมตี |
พลังป้องกัน |
461 |
น้ำแข็ง |
120 |
65 |
462 |
เหล็ก |
70 |
115 |
464 |
หิน |
140 |
130 |
465 |
พืช |
100 |
125 |
|
สายพันธุ์ |
ส่วนสูง |
น้ำหนัก |
ชนิด 1 |
ชนิด 2 |
พลังโจมตี |
พลังป้องกัน |
461 |
มานิวลา |
1.1 |
34.0 |
มาร |
น้ำแข็ง |
120.0 |
65.0 |
462 |
จิบะคอยล์ |
1.2 |
180.0 |
ไฟฟ้า |
เหล็ก |
70.0 |
115.0 |
463 |
เบโรเบลต์ |
1.7 |
140.0 |
ธรรมดา |
NaN |
NaN |
NaN |
464 |
โดไซดอน |
2.4 |
282.8 |
ดิน |
หิน |
140.0 |
130.0 |
จะเห็นว่า 465 ซึ่งไม่มีในตารางซ้ายถูกตัดทิ้งไป แต่ 463 ซึ่งไม่มีในตารางขวาก็ยังคงถูกเก็บไว้ ส่วน "ชนิด" ซึ่งเป็นชื่อคอลัมน์ที่ซ้ำกันก็ถูกเติม 1 และ 2 ต่อท้าย
อ้างอิง