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



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

ใน 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

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
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月

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

ไทย

日本語

中文