φυβλαςのβλογ
บล็อกของ phyblas



จัดการข้อมูลด้วย pandas เบื้องต้น บทที่ ๑๓: การเชื่อมตารางข้อมูล
เขียนเมื่อ 2016/09/25 15:39
ในบทที่แล้วได้เขียนถึงวิธีการเอาข้อมูลจากตารางสองอันขึ้นไปมารวมเข้าด้วยกันแล้ว สำหรับในบทนี้จะเป็นเรื่องของการรวมสองตารางเข้าด้วยกันเช่นกันเพียงแต่ว่าเป็นการรวมในอีกลักษณะหนึ่ง นั่นคือการเชื่อมโดยผ่านความสัมพันธ์ของค่าภายในตาราง



การเชื่อมข้อมูลโดยผ่านคอลัมน์ที่ชื่อเหมือนกัน
หากมีตารางเดตาเฟรมสองอันแล้วต้องการนำข้อมูลมาเชื่อมต่อรวมกันโดยอาศัยคอลัมน์หนึ่งเป็นตัวเชื่อมสามารถใช้ฟังก์ชัน 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 ต่อท้าย



อ้างอิง


<< บทที่แล้ว      บทถัดไป >>
หน้าสารบัญ


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> pandas

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
python
-- numpy
-- matplotlib

-- pandas
-- pytorch
maya
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
เรียนภาษาจีน
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

หลักการเขียนทับศัพท์ภาษาจีนกวางตุ้ง
การใช้ unix shell เบื้องต้น ใน linux และ mac
หลักการเขียนทับศัพท์ภาษาจีนกลาง
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
บันทึกการเที่ยวสวีเดน 1-12 พ.ค. 2014
แนะนำองค์การวิจัยและพัฒนาการสำรวจอวกาศญี่ปุ่น (JAXA)
เล่าประสบการณ์ค่ายอบรมวิชาการทางดาราศาสตร์โดยโซวเคนได 10 - 16 พ.ย. 2013
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
บันทึกการเที่ยวญี่ปุ่นครั้งแรกในชีวิต - ทุกอย่างเริ่มต้นที่สนามบินนานาชาติคันไซ
หลักการเขียนคำทับศัพท์ภาษาญี่ปุ่น
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ
ทำไมถึงอยากมาเรียนต่อนอก
เหตุผลอะไรที่ต้องใช้ภาษาวิบัติ?

บทความแต่ละเดือน

2019年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2018年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2017年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2016年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2015年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文