φυβλαςのβλογ
phyblas的博客



จัดการข้อมูลด้วย pandas เบื้องต้น บทที่ ๑๒: การรวมข้อมูลเป็นตารางเดียว
เขียนเมื่อ 2016/09/25 15:30
แก้ไขล่าสุด 2021/09/28 16:42
มีหลากหลายวิธีในการนำข้อมูลที่มีอยู่ในซีรีส์หรือเดตาเฟรมสองอันขึ้นไปมารวมเข้าด้วยกัน ในบทนี้จะพูดถึงวิธีการรวมโดยใช้ฟังก์ชัน pd.concat และเมธอด append



การเอาซีรีส์มาต่อกัน
เมื่อมีซีรีส์สองอันขึ้นไปแล้วจะเอามารวมกันอาจทำได้โดยใช้ฟังก์ชัน pd.concat โดยใส่ลิสต์ของซีรีส์ที่ต้องการรวมลงไป

ตัวอย่าง สร้างซีรีส์ขึ้นมา ๒ อัน
import pandas as pd
pokemon1 = pd.Series(['โป๊ปโป','พีเจียน','พีจ็อต'])
pokemon2 = pd.Series(['โอนิสึซึเมะ','โอนิดริล'])
print(pokemon1)
print(pokemon2)




ได้
0     โป๊ปโป
1    พีเจียน
2     พีจ็อต
dtype: object
0    โอนิสึซึเมะ
1       โอนิดริล
dtype: object

จากนั้นใช้ concat
print(pd.concat([pokemon1,pokemon2]))

ได้
0         โป๊ปโป
1        พีเจียน
2         พีจ็อต
0    โอนิสึซึเมะ
1       โอนิดริล
dtype: object

ซีรีส์จะถูกนำมาต่อกัน แต่ก็จะเห็นว่าดัชนีมีการซ้ำกันเกิดขึ้น โดยยึดตามดัชนีเดิมโดยไม่มีการเปลี่ยนให้อัตโนมัติ

หากต้องการให้เวลาที่ต่อกันแล้วลืมดัชนีเดิมทิ้งไปเลยก็ให้ใส่ ignore_index=1 เท่านี้ดัชนีก็จะจัดเรียงใหม่ไล่ตั้งแต่ 0 ไม่ว่าจะเคยเป็นอะไรมาก่อน
print(pd.concat([pokemon1,pokemon2],ignore_index=1))

ได้
0         โป๊ปโป
1        พีเจียน
2         พีจ็อต
3    โอนิสึซึเมะ
4       โอนิดริล
dtype: object



การรวมซีรีส์เป็นเดตาเฟรม
การรวมซีรีส์นั้นนอกจากจะเอามาต่อกันให้เป็นสายยาวขึ้นแล้วยังสามารถรวมกันในลักษณะที่ใช้ซีรีส์อันหนึ่งเป็นคอลัมน์หนึ่งเพื่อสร้างเดตาเฟรมขึ้นมา

ในกรณีแบบนี้ให้ใส่ axis=1 แล้วก็ควรจะเป็นซีรีส์ที่มีการตั้งชื่อไว้ด้วย แล้วชื่อนั้นจะกลายมาเป็นชื่อคอลัมน์ ถ้าไม่ได้ตั้งก็จะถูกตั้งเองเป็นตัวเลข

ตัวอย่าง
saiphan = pd.Series(['อูริมู','อิโนมู','แมมมู'],name='สายพันธุ์')
mailek = pd.Series([220,221,473],name='หมายเลข')
print(saiphan)
print(mailek)

ได้
0    อูริมู
1    อิโนมู
2     แมมมู
Name: สายพันธุ์, dtype: object
0    220
1    221
2    473
Name: หมายเลข, dtype: int64



นำมา concat
print(pd.concat([saiphan,mailek],axis=1))

ได้
  สายพันธุ์ หมายเลข
0 อูริมู 220
1 อิโนมู 221
2 แมมมู 473

การรวมกันในกรณี axis=1 นี้จะยึดตามเลขดัชนี คือรวมแถวที่เลขดัชนีเหมือนกันเข้าด้วยกัน ถ้าดัชนีของทั้ง ๒ ซีรีส์ต่างกันผลที่ได้ก็จะมี NaN

ตัวอย่างเช่น
saiphan = pd.Series(['โนคจจิ','ฮารีเซน','ทสึโบะทสึโบะ'],index=[206,211,213],name='สายพันธุ์')
chanit = pd.Series(['ธรรมดา','น้ำ/พิษ','แมลง/ต่อสู้'],index=[206,211,214],name='ชนิด')
print(saiphan)
print(chanit)
print(pd.concat([saiphan,chanit],axis=1))




ได้
206          โนคจจิ
211         ฮารีเซน
213    ทสึโบะทสึโบะ
Name: สายพันธุ์, dtype: object
206         ธรรมดา
211        น้ำ/พิษ
214    แมลง/ต่อสู้
Name: ชนิด, dtype: object
  สายพันธุ์ ชนิด
206 โนคจจิ ธรรมดา
211 ฮารีเซน น้ำ/พิษ
213 ทสึโบะทสึโบะ NaN
214 NaN แมลง/ต่อสู้



การรวมเดตาเฟรมเข้าด้วยกันเป็นตารางเดียว
เช่นเดียวกับซีรีส์ เมื่อจะเอาเดตาเฟรมสองอันขึ้นไปมารวมกันก็สามารถใช้ pd.concat โดยใส่ลิสต์ของเดตาเฟรมที่ต้องการรวมลงไป

ปกติแล้วถ้าไม่ได้กำหนดคีย์เวิร์ด axis หรือกำหนด axis=0 จะเป็นการรวมในแนวตั้ง คือรวมที่มีคอลัมน์ชื่อเหมือนกันแต่ดัชนีต่างกัน ผลที่ได้จะได้เดตาเฟรมที่ต่อกันเป็นแถวยาวมีจำนวนดัชนีเท่ากับเดตาเฟรมทั้งหมดรวมกัน

ถ้ากำหนด axis=1 จะเป็นการรวมกันในแนวนอน คือรวมที่มีชื่อดัชนีเหมือนกันแต่เป็นคนละชื่อคอลัมน์ แล้วก็จะได้เดตาเฟรมที่มีจำนวนคอลัมน์มากขึ้น

๒ แบบนี้มีหลักการเหมือนกันเพียงแค่กลับแนวตั้งแนวนอนกัน ดังนั้นในที่นี้จะยกตัวอย่างแค่กรณีแนวตั้ง

ตัวอย่าง สร้างเดตาเฟรมเก็บข้อมูลโปเกมอนขึ้นมา ๓ ตาราง
pokemon1 = pd.DataFrame([
        ['ธรรมดา',55,55,55],
        ['น้ำ',130,65,65],
        ['ไฟฟ้า',65,65,130],
        ['ไฟ',65,130,65]],
    index=['อีวุย','เชาเวอร์ส','ธันเดอร์ส','บูสเตอร์'],
    columns=['ชนิด','HP','พลังโจมตี','ความไว'])
pokemon2 = pd.DataFrame([
        ['พลังจิต',65,60,110],
        ['มาร',95,110,65]],
    index=['เอย์ฟี','แบล็กกี'],
    columns=['ชนิด','HP','พลังป้องกัน','ความไว'])
pokemon3 = pd.DataFrame({
        'ชนิด':['พืช','น้ำแข็ง'],
        'HP':[65,65],
        'พลังโจมตี':[110,60],
        'พลังป้องกัน':[130,110]},
    index=['ลีเฟีย','เกลเซีย'])
print(pokemon1)
print(pokemon2)
print(pokemon3)





ได้
  ชนิด HP พลังโจมตี ความไว
อีวุย ธรรมดา 55 55 55
เชาเวอร์ส น้ำ 130 65 65
ธันเดอร์ส ไฟฟ้า 65 65 130
บูสเตอร์ ไฟ 65 130 65

  ชนิด HP พลังป้องกัน ความไว
เอย์ฟี พลังจิต 65 60 110
แบล็กกี มาร 95 110 65

  HP ชนิด พลังป้องกัน พลังโจมตี
ลีเฟีย 65 พืช 130 110
เกลเซีย 65 น้ำแข็ง 110 60

นำมารวมกัน
print(pd.concat([pokemon1,pokemon2,pokemon3]))

ได้
  HP ความไว ชนิด พลังป้องกัน พลังโจมตี
อีวุย 55 55.0 ธรรมดา NaN 55.0
เชาเวอร์ส 130 65.0 น้ำ NaN 65.0
ธันเดอร์ส 65 130.0 ไฟฟ้า NaN 65.0
บูสเตอร์ 65 65.0 ไฟ NaN 130.0
เอย์ฟี 65 110.0 พลังจิต 60.0 NaN
แบล็กกี 95 65.0 มาร 110.0 NaN
ลีเฟีย 65 NaN พืช 130.0 110.0
เกลเซีย 65 NaN น้ำแข็ง 110.0 60.0

จะเห็นว่าทั้ง ๓ เดตาเฟรมก่อนรวมมีคอลัมน์ที่ทั้งเหมือนกันและต่างกัน เมื่อรวมกันแล้วคอลัมน์ที่ไม่มีจะได้ค่าเป็น NaN และลำดับของคอลัมน์ก็ถูกจัดเรียงให้ใหม่โดยอัตโนมัติไม่ได้เป็นไปตามที่เรียงตอนแรกด้วย

บางครั้งเราอาจไม่ได้ต้องการทุกคอลัมน์แต่อาจต้องการเหลือแค่บางคอลัมน์ กรณีแบบนี้อาจใส่คีย์เวิร์ด join_axes โดยใส่ค่าเป็นซีรีส์ของชื่อคอลัมน์ที่ต้องการ เช่น
c = pd.Series(['พลังโจมตี','พลังป้องกัน','ความไว'])
print(pd.concat([pokemon1,pokemon2,pokemon3],join_axes=[c]))

ได้
  พลังโจมตี พลังป้องกัน ความไว
อีวุย 55.0 NaN 55.0
เชาเวอร์ส 65.0 NaN 65.0
ธันเดอร์ส 65.0 NaN 130.0
บูสเตอร์ 130.0 NaN 65.0
เอย์ฟี NaN 60.0 110.0
แบล็กกี NaN 110.0 65.0
ลีเฟีย 110.0 130.0 NaN
เกลเซีย 60.0 110.0 NaN

ให้ระวังว่าค่าที่ต้องใส่ต้องเป็นลิสต์แม้ว่าจะมีอยู่แค่ตัวเดียวก็ตาม (ในที่นี้คือเป็น join_axes=[c] ไม่ใช่ join_axes=c) ที่จริงแล้วคีย์เวิร์ดนี้ทำไว้รองรับกรณีที่เป็นแบบคอลัมน์หลายแถว ซึ่งในกรณีแบบนั้นจะต้องใส่ซีรีส์ตามจำนวนเท่านั้น

ถ้าต้องการใช้คอลัมน์ของเดตาเฟรมสักอันก็อาจใช้ได้โดยใช้ เดตาเฟรม.columns เช่น
print(pd.concat([pokemon1,pokemon2,pokemon3],join_axes=[pokemon3.columns]))

ได้
  HP ชนิด พลังป้องกัน พลังโจมตี
อีวุย 55 ธรรมดา NaN 55.0
เชาเวอร์ส 130 น้ำ NaN 65.0
ธันเดอร์ส 65 ไฟฟ้า NaN 65.0
บูสเตอร์ 65 ไฟ NaN 130.0
เอย์ฟี 65 พลังจิต 60.0 NaN
แบล็กกี 95 มาร 110.0 NaN
ลีเฟีย 65 พืช 130.0 110.0
เกลเซีย 65 น้ำแข็ง 110.0 60.0

นอกจากนี้กรณีที่ต้องการเก็บเฉพาะคอลัมน์ที่ทุกเดตาเฟรมต่างก็มีเหมือนกันหมดก็อาจใส่คีย์เวิร์ด join เพิ่มลงไป โดยใส่เป็น join='inner'

ปกติแล้วถ้าไม่ใส่คีย์เวิร์ดนี้ลงไปจะหมายถึง join='outer' หมายถึงใช้ทุกคอลัมน์ที่มีจากทุกเดตาเฟรม แต่ถ้า inner ก็จะหมายถึงใช้เฉพาะคอลัมน์ที่มีในทุกอัน ตัวอย่างเช่น
print(pd.concat([pokemon1,pokemon2,pokemon3],join='inner'))

ได้
  HP ชนิด
อีวุย 55 ธรรมดา
เชาเวอร์ส 130 น้ำ
ธันเดอร์ส 65 ไฟฟ้า
บูสเตอร์ 65 ไฟ
เอย์ฟี 65 พลังจิต
แบล็กกี 95 มาร
ลีเฟีย 65 พืช
เกลเซีย 65 น้ำแข็ง



การรวมแล้วทำเป็นดัชนีซ้อน
ปกติเวลาที่ใช้ concat เพื่อต่อแถวเดตาเฟรมหากดัชนีมีตัวที่ซ้ำกันผลที่ได้ก็จะเป็นดัชนีที่ซ้ำกัน ซึ่งอาจแก้ไขได้ด้วยการใช้คีย์เวิร์ด ignore_index

แต่วิธีนั้นจะทำให้สูญเสียดัชนีเดิมหมด ที่จริงแล้วมีวิธีที่ดีกว่านั้นแม้ว่าจะทำให้ซับซ้อนขึ้นสักหน่อย นั่นคือการเพิ่มข้อมูลที่ว่าข้อมูลแถวนั้นมาจากเดตาเฟรมตัวไหนลงไปใส่เป็นดัชนีซ้อนลงไป

สามารถทำได้โดยใส่คีย์เวิร์ด keys แล้วตามด้วยลิสต์ของค่าดัชนีที่จะใช้แทนแต่ละตาราง

ตัวอย่าง ลองสร้างตารางข้อมูลโปเกมอนตัวหลักในแต่ละภาค
pokemon1 = pd.DataFrame([
    ['ฟุชิงิดาเนะ','พืช'],['ฮิโตคาเงะ','ไฟ'],['เซนิงาเมะ','น้ำ']],
    index=[1,4,7],columns=['สายพันธุ์','ชนิด'])
pokemon2 = pd.DataFrame([
    ['ชิโครีตา','พืช'],['ฮิโนอาราชิ','ไฟ'],['วานิโนโกะ','น้ำ']],
    index=[1,4,7],columns=['สายพันธุ์','ชนิด'])
pokemon3 = pd.DataFrame([
    ['คิโมริ','พืช'],['อาชาโม','ไฟ'],['มิซึโงโรว','น้ำ']],
    index=[1,4,7],columns=['สายพันธุ์','ชนิด'])
print(pokemon1)
print(pokemon2)
print(pokemon3)





ได้
  สายพันธุ์ ชนิด
1 ฟุชิงิดาเนะ พืช
4 ฮิโตคาเงะ ไฟ
7 เซนิงาเมะ น้ำ

  สายพันธุ์ ชนิด
1 ชิโครีตา พืช
4 ฮิโนอาราชิ ไฟ
7 วานิโนโกะ น้ำ

  สายพันธุ์ ชนิด
1 คิโมริ พืช
4 อาชาโม ไฟ
7 มิซึโงโรว น้ำ

นำมารวมกันให้เป็นดัชนีซ้อนกัน กำหนดดัชนีเป็นตัวเลขลำดับภาค
print(pd.concat([pokemon1,pokemon2,pokemon3],keys=[1,2,3]))

ได้
    สายพันธุ์ ชนิด
1 1 ฟุชิงิดาเนะ พืช
4 ฮิโตคาเงะ ไฟ
7 เซนิงาเมะ น้ำ
2 1 ชิโครีตา พืช
4 ฮิโนอาราชิ ไฟ
7 วานิโนโกะ น้ำ
3 1 คิโมริ พืช
4 อาชาโม ไฟ
7 มิซึโงโรว น้ำ

นอกจากนี้มีอีกวิธีในการรวมให้ได้ดัชนีซ้อนในลักษณะแบบนี้โดยไม่ใช้คีย์เวิร์ด keys นั่นคือแทนที่จะใส่ค่าในรูปลิสต์ก็ใส่ในรูปดิกชันนารีโดยที่มีคีย์เป็นค่าดัชนีที่ต้องการ

เช่น หากต้องการผลเหมือนตัวอย่างข้างต้นก็พิมพ์
print(pd.concat({1:pokemon1,2:pokemon2,3:pokemon3}))



การต่อขยายซีรีส์
หากมีซีรีส์อยู่อันหนึ่งแล้วต้องการเอาข้อมูลจากซีรีส์อีกอันมาใส่สามารถใช้เมธอด append ได้

เพียงแต่ว่าเมธอด appand นี้ว่ากันตามจริงแล้วก็เหมือนเป็นฟังก์ชัน np.concat ที่เขียนในรูปแบบเมธอดของเดตาเฟรมอันหนึ่ง เพราะการ "เอาอันหนึ่งมาต่อกับอีกอัน" กับ "เอาสองอันมาต่อกัน" ก็คือสิ่งเดียวกัน

ต่างกันก็ตรงที่ว่า append จะใช้อันหนึ่งเป็นฐานของเมธอด และอันที่เหลือเป็นอาร์กิวเมนต์

ตัวอย่างการใช้
pokemon1 = pd.Series(['อูปา','นูโอ'],index=[194,195])
pokemon2 = pd.Series(['ฮัสโบ','ฮาสึเบรโร','รูนปัปปะ'],index=[270,271,272])
pokemon3 = pd.Series(['บุยเซล','โฟลเซล'],index=[418,419])
print(pokemon1.append([pokemon2,pokemon3]))





ได้
194         อูปา
195         นูโอ
270        ฮัสโบ
271    ฮาสึเบรโร
272     รูนปัปปะ
418       บุยเซล
419       โฟลเซล
dtype: object

append ไม่ได้ทำการเปลี่ยนแปลงใดๆกับเดตาเฟรมที่เป็นฐาน แค่จะทำการคืนค่าเดตาเฟรมตัวใหม่ที่อยู่ในสภาพหลังต่อขยายมาแล้วเท่านั้น



การต่อขยายเดตาเฟรม
เช่นเดียวกับซีรีส์ เดตาเฟรมก็มีเมธอด append ไว้สำหรับเอาเดตาเฟรมตัวอื่นๆมาต่อให้กับเดตาเฟรมตัวหนึ่ง

ตัวอย่าง
pokemon1 = pd.DataFrame([
        ['ดัมเบอร์','เหล็ก/พลังจิต',0.6,95.2],
        ['เมทัง','เหล็ก/พลังจิต',0.6,95.2],
        ['เมทากรอส','เหล็ก/พลังจิต',1.6,550.0]],
    index=[374,375,376],
    columns=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'])
pokemon2 = pd.DataFrame([
        ['ทัตสึเบย์','มังกร',0.6,42.1],
        ['โคโมรู','มังกร',1.1,110.5],
        ['โบมันเดอร์','มังกร',1.5,102.6]],
    index=[371,372,373],
    columns=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'])
print(pokemon1)
print(pokemon2)
print(pokemon1.append(pokemon2))




ได้
  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
374 ดัมเบอร์ เหล็ก/พลังจิต 0.6 95.2
375 เมทัง เหล็ก/พลังจิต 0.6 95.2
376 เมทากรอส เหล็ก/พลังจิต 1.6 550.0

  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
371 ทัตสึเบย์ มังกร 0.6 42.1
372 โคโมรู มังกร 1.1 110.5
373 โบมันเดอร์ มังกร 1.5 102.6

  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
374 ดัมเบอร์ เหล็ก/พลังจิต 0.6 95.2
375 เมทัง เหล็ก/พลังจิต 0.6 95.2
376 เมทากรอส เหล็ก/พลังจิต 1.6 550.0
371 ทัตสึเบย์ มังกร 0.6 42.1
372 โคโมรู มังกร 1.1 110.5
373 โบมันเดอร์ มังกร 1.5 102.6

append นั้นในกรณีที่ใช้กับเดตาเฟรมนอกจากใช้ตัวที่ต่อเป็นเดตาเฟรมแล้วก็ยังอาจใช้ซีรีส์มาเป็นตัวต่อได้อีกด้วย ซึ่งถ้าเป็น concat จะใช้แบบนี้ไม่ได้ โดยที่ซีรีส์ที่จะใช้นั้นต้องมีจำนวนแถวเท่ากับจำนวนคอลัมน์ของเดตาเฟรมพอดี

ตัวอย่าง
pokemon3 = pd.Series(['เลิฟคัส','น้ำ',0.6,8.7],
                     index=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'],
                     name=370)
print(pokemon1.append(pokemon3))



ได้
  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
374 ดัมเบอร์ เหล็ก/พลังจิต 0.6 95.2
375 เมทัง เหล็ก/พลังจิต 0.6 95.2
376 เมทากรอส เหล็ก/พลังจิต 1.6 550.0
370 เลิฟคัส น้ำ 0.6 8.7



อ้างอิง


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


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

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

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

หมวดหมู่

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

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

目录

从日本来的名言
模块
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
机器学习
-- 神经网络
javascript
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



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

  查看日志

  推荐日志

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ