มีหลากหลายวิธีในการนำข้อมูลที่มีอยู่ในซีรีส์หรือเดตาเฟรมสองอันขึ้นไปมารวมเข้าด้วยกัน ในบทนี้จะพูดถึงวิธีการรวมโดยใช้ฟังก์ชัน 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 |
อ้างอิง