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



จัดการข้อมูลด้วย pandas เบื้องต้น บทที่ ๖: การจัดการกับข้อมูลที่ซ้ำซ้อน
เขียนเมื่อ 2016/09/25 14:02
แก้ไขล่าสุด 2021/09/28 16:42
บางครั้งข้อมูลที่เราพิจารณาก็อาจมีบางส่วนที่ซ้ำซ้อน ซึ่งบางครั้งเราอาจลบทิ้งหรือแก้ไขให้ไม่ซ้ำ

ใน pandas มีเมธอดสำหรับจัดการกับข้อมูลที่ซ้ำซ้อนโดยเฉพาะอยู่หลายอันซึ่งสะดวกที่จะใช้งานได้เป็นอย่างดี



ดัชนีซ้ำ
ก่อนอื่นขอกล่าวถึงกรณีที่ค่าในดัชนีมีการซ้ำเกิดขึ้นก่อน

แม้จะยังไม่เคยกล่าวถึงในบทก่อนๆ แต่จริงๆแล้วดัชนีของซีรีส์และเดตาเฟรมสามารถกำหนดให้ซ้ำกันได้

เช่นลองดูซีรีส์ที่ตั้งดัชนีไว้ซ้ำกัน
import pandas as pd
pokemon = pd.Series(['ฟุชิงิดาเนะ','ชิโครีตา','คิโมริ','นาเอเทิล'],index=[1,2,3,1])
print(pokemon)



ได้
1    ฟุชิงิดาเนะ
2       ชิโครีตา
3         คิโมริ
1       นาเอเทิล
dtype: object

พอเป็นแบบนี้เวลาที่อ้างอิงถึงโดยใช้ดัชนีที่ซ้ำก็จะได้ผลออกมาเป็นซีรีส์เหมือนเดิม เช่น
print(pokemon[1])

ได้
1    ฟุชิงิดาเนะ
1       นาเอเทิล
dtype: object

แล้วพอใส่ [1] ต่อท้ายไปอีกก็ยังจะได้ตัวเดิม จะใส่กี่ตัวก็ยังจะได้ผลเหมือนเดิมต่อไปเรื่อยๆ ไม่สิ้นสุด เช่น
print(pokemon[1][1][1][1][1][1][1][1][1][1][1][1][1][1])

ดังนั้นการจะเข้าถึงสมาชิกข้างในตัวใดตัวหนึ่งจึงไม่มีทางทำได้ด้วยการใส่แค่ดัชนีเฉยๆ อาจต้องใช้วิธีอื่นเช่น .iloc เพื่อจะพิจารณาจากลำดับที่
print(pokemon.iloc[0]) # ได้ ฟุชิงิดาเนะ
print(pokemon.iloc[3]) # ได้ นาเอเทิล

ในขณะที่ถ้าใส่ดัชนีที่ไม่ซ้ำก็จะได้ผลออกมาเป็นสมาชิกข้างในตัวนั้นทันทีโดยไม่ใช่ซีรีส์ เช่น pokemon[2] ได้ ชิโครีตา

กรณีของเดตาเฟรมก็ให้ผลในทำนองเดียวกัน แต่เดตาเฟรมนอกจากดัชนีแล้วยังมีคอลัมน์ด้วย ซึ่งคอลัมน์เองก็สามารถมีชื่อซ้ำกันได้เช่นกัน เช่น
pokemon = pd.DataFrame([
        [4,'ฮิโตคาเงะ','Hitokage'],
        [155,'ฮิโนอาราชิ','Hinoarashi'],
        [255,'อาชาโม','Achamo'],
        [390,'ฮิโกะซารุ','Hikozaru']],
    columns=['หมายเลข','สายพันธุ์','สายพันธุ์'],
    index=[1,2,3,1])
print(pokemon)



ได้
  หมายเลข สายพันธุ์ สายพันธุ์
1 4 ฮิโตคาเงะ Hitokage
2 155 ฮิโนอาราชิ Hinoarashi
3 255 อาชาโม Achamo
1 390 ฮิโกะซารุ Hikozaru

แบบนี้หากอ้างอิงถึงคอลัมน์ที่ชื่อซ้ำก็จะได้ผลเป็นเดตาเฟรมเหมือนเดิม ในขณะที่ปกติแล้วถ้าคอลัมน์ไม่ซ้ำควรจะได้เป็นซีรีส์
print(pokemon['สายพันธุ์'])

ได้
  สายพันธุ์ สายพันธุ์
1 ฮิโตคาเงะ Hitokage
2 ฮิโนอาราชิ Hinoarashi
3 อาชาโม Achamo
1 ฮิโกะซารุ Hikozaru

เพื่อที่จะดูว่าดัชนีหรือคอลัมน์มีการซ้ำหรือเปล่าอาจดูที่แอตทริบิวต์ is_unique ที่ตัว index และ columns
print(pokemon.index.is_unique) # ได้ False
print(pokemon.columns.is_unique) # ได้ False

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



การกำจัดข้อมูลที่ซ้ำซ้อน
มีเมธอดที่สะดวกสำหรับใช้ตรวจดูว่าข้อมูลซ้ำกันหรือเปล่าก็คือ duplicated

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

ตัวอย่าง
pokemon = pd.Series(['เซนิงาเมะ','วานิโนโกะ','วานิโนโกะ','มิซึโงโรว','พจจามะ','เซนิงาเมะ','มิจุมารุ'])
print(pokemon.duplicated())



ได้
0    False
1    False
2     True
3    False
4    False
5     True
6    False
dtype: bool

จะเห็นว่าเป็น True เฉพาะตัวหลังที่ซ้ำกับตัวก่อนหน้าเท่านั้น ตัวแรกจะยังเป็น False อยู่

และถ้าต้องการกำจัดตัวซ้ำ ก็แค่ใช้ซีรีส์บูลที่ได้มานี้เป็นดัชนี
print(pokemon[pokemon.duplicated()==0])

ได้
0     เซนิงาเมะ
1    วานิโนโกะ
3    มิซึโงโรว
4       พจจามะ
6     มิจุมารุ
dtype: object

กรณีที่ใช้กับเดตาเฟรมจะได้ True เมื่อสมาชิกในทุกคอลัมน์เหมือนกันหมด
pokemon = pd.DataFrame([
        ['ฟูจัง','ฟุชิงิโซว',30],
        ['ฟูจัง','ฟุชิงิโซว',30],
        ['ฟูจัง','ฟูดิน',38],
        ['ดีจัง','ดิกดา',10],
        ['กาจัง','การ์ดี',35],
        ['กาจัง','การะการะ',44]],
    columns=['ชื่อ','สายพันธุ์','เลเวล'])
print(pokemon)
print('-----------------')
print(pokemon.duplicated())



ได้
  ชื่อ สายพันธุ์ เลเวล
0 ฟูจัง ฟุชิงิโซว 30
1 ฟูจัง ฟุชิงิโซว 30
2 ฟูจัง ฟูดิน 38
3 ดีจัง ดิกดา 10
4 กาจัง การ์ดี 35
5 กาจัง การะการะ 44
-----------------
0    False
1     True
2    False
3    False
4    False
5    False
dtype: bool

จะเห็นว่านอกจากแถวที่ซ้ำกันทุกตัวแล้ว แถวอื่นได้ False หมด

หากต้องการให้พิจารณาแค่บางแถวก็อาจใส่คีย์เวิร์ด subset โดยใส่รายชื่อคอลัมน์ที่ต้องการให้พิจารณา เช่น
print(pokemon.duplicated(subset='ชื่อ'))

ได้
0    False
1     True
2     True
3    False
4    False
5     True
dtype: bool

ปกติแล้วจะให้ค่า True กับตัวที่ซ้ำกับตัวก่อนหน้าเท่านั้น ในขณะที่ตัวหน้ายังเป็น False แต่สามารถทำให้กลับกันได้โดยเพิ่มคีย์เวิร์ด keep

หากต้องการไล่จากท้าย แล้วให้ตัวท้ายเป็น False ตัวก่อนเป็น True ก็ใส่ keep='last' เช่น
print(pokemon.duplicated(subset='ชื่อ',keep='last'))

ได้
0     True
1     True
2    False
3    False
4     True
5    False
dtype: bool

หรือถ้าจะให้เป็น True หมดไม่ว่าจะตัวแรกหรือตัวหลังก็ใส่ keep=0
print(pokemon.duplicated(subset='ชื่อ',keep=0))

ได้
0     True
1     True
2     True
3    False
4     True
5     True
dtype: bool

เมื่อได้ซีรีส์บูลมาจากเมธอด duplicated แล้วก็เอาซีรีส์นี้มาใช้เพื่อเป็นดัชนีเพื่อคัดกรองเอาตัวที่มีค่าซ้ำออกได้

เมธอด duplicated สามารถใช้เพื่อตรวจดูการซ้ำกันของค่าดัชนีได้เช่นกัน เพื่อคัดกรองแถวที่มีดัชนีซ้ำกันทิ้งไป

ตัวอย่าง ท่าทั้งหมดที่เรียนรู้ได้ที่เลเวลต่างๆของพิคาชู จากเกมโปเกมอนภาคแรก สร้างข้อมูลขึ้นมาให้มีดัชนีซ้ำกันจากนั้นก็ทำการคัดออกให้เหลือแค่ตัวหลังเท่านั้น
tha = pd.DataFrame([
        ['เสียงร้อง',np.NaN,'ธรรมดา',1],
        ['ช็อตไฟฟ้า',40,'ไฟฟ้า',1],
        ['คลื่นไฟฟ้า',np.NaN,'ไฟฟ้า',9],
        ['จู่โจมสายฟ้าแลบ',40,'ไฟฟ้า',16],
        ['สปีดสตาร์',60,'ธรรมดา',26],
        ['เคลื่อนไหวเร็ว',np.NaN,'พลังจิต',33],
        ['ฟ้าผ่า',120,'ไฟฟ้า',43]],
    columns=['ชื่อ','พลังโจมตี','ชนิด','เลเวลที่ได้'],
    index=[1,2,3,4,1,4,2])
print(tha)
print(tha.index.duplicated(keep='last'))
print(tha[tha.index.duplicated(keep='last')==0])

ได้
  ชื่อ พลังโจมตี ชนิด เลเวลที่ได้
1 เสียงร้อง NaN ธรรมดา 1
2 ช็อตไฟฟ้า 40.0 ไฟฟ้า 1
3 คลื่นไฟฟ้า NaN ไฟฟ้า 9
4 จู่โจมสายฟ้าแลบ 40.0 ไฟฟ้า 16
1 สปีดสตาร์ 60.0 ธรรมดา 26
4 เคลื่อนไหวเร็ว NaN พลังจิต 33
2 ฟ้าผ่า 120.0 ไฟฟ้า 43
[ True  True False  True False False False]
  ชื่อ พลังโจมตี ชนิด เลเวลที่ได้
3 คลื่นไฟฟ้า NaN ไฟฟ้า 9
1 สปีดสตาร์ 60.0 ธรรมดา 26
4 เคลื่อนไหวเร็ว NaN พลังจิต 33
2 ฟ้าผ่า 120.0 ไฟฟ้า 43

เมธอด duplicated นั้นแค่ให้ผลเป็นซีรีส์บูลแล้วเราก็ต้องเอามันมาใช้เป็นดัชนีอีกทีเพื่อคัดกรองแถวที่ต้องการ

แต่มีเมธอดที่สะดวกกว่านั้น คือ drop_duplicates ซึ่งจะทำการลบแถวที่ซ้ำกันออกไปเลย

คีย์เวิร์ดใน drop_duplicates นั้นจะเหมือนกับ duplicated คือใช้ keep กับ subset ได้

เพียงแต่ว่า drop_duplicates จะใช้กับคอลัมน์ธรรมดาเท่านั้น ไม่สามารถใช้กับคอลัมน์ของดัชนี ดังนั้นในกรณีนั้นยังคงต้องใช้ duplicated

ตัวอย่างการใช้ ลองดูท่าของโปเกมอนพาราส จากโปเกมอนภาคแรก
tha = pd.DataFrame([
        [40,'ธรรมดา',1],
        [np.NaN,'พืช',13],
        [20,'แมลง',20],
        [np.NaN,'พืช',27],
        [70,'ธรรมดา',34],
        [np.NaN,'ธรรมดา',41]],
    columns=['พลังโจมตี','ชนิด','เลเวลที่ได้'],
    index=['ข่วน','ผงชา','ดูดเลือด','สปอร์เห็ด','ข่วนยับ','เติบโต'])
print(tha)
print('~~~~~~~~~~~~~~')
print(tha.drop_duplicates(subset='ชนิด',keep='last'))

ได้
  พลังโจมตี ชนิด เลเวลที่ได้
ข่วน 40.0 ธรรมดา 1
ผงชา NaN พืช 13
ดูดเลือด 20.0 แมลง 20
สปอร์เห็ด NaN พืช 27
ข่วนยับ 70.0 ธรรมดา 34
เติบโต NaN ธรรมดา 41
~~~~~~~~~~~~~~
  พลังโจมตี ชนิด เลเวลที่ได้
ดูดเลือด 20.0 แมลง 20
สปอร์เห็ด NaN พืช 27
เติบโต NaN ธรรมดา 41

แบบนี้จะมีค่าเท่ากับ
print(tha[tha.duplicated(subset='ชนิด',keep='last')==0])

ปกติ drop_duplicates จะสร้างข้อมูลใหม่โดยไม่ทับตัวเก่า แต่ก็สามารถใส่คีย์เวิร์ด inplace=True เพื่อให้ตัวเก่าถูกทับไปเลย เช่น
tha.drop_duplicates(subset='ชนิด',keep='last',inplace=True)

แบบนี้จะมีค่าเท่ากับเขียน
tha = tha.drop_duplicates(subset='ชนิด',keep='last')



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

ตัวอย่าง
pokemon = pd.Series(['ฟุชิงิบานะ','เมกาเนียม','จูไคน์','เมกาเนียม','จูไคน์','ฟุชิงิบานะ','โดไดโทส','ฟุชิงิบานะ'])
print(pokemon.unique())
# หรือ print(pokemon.drop_duplicates().values)



ได้
['ฟุชิงิบานะ' 'เมกาเนียม' 'จูไคน์' 'โดไดโทส']



อ้างอิง


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


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

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

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

หมวดหมู่

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

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

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

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文