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



จัดการข้อมูลด้วย pandas เบื้องต้น บทที่ ๑๖: การแบ่งข้อมูลเป็นช่วงตามค่าตัวเลข
เขียนเมื่อ 2016/09/25 16:05
แก้ไขล่าสุด 2021/09/28 16:42
ใน pandas มีฟังก์ชันสำหรับแบ่งข้อมูลออกเป็นส่วนๆตามช่วงของค่าตัวเลขมากน้อย ได้แก่ pd.cut และ pd.qcut



การแบ่งข้อมูลเป็นกลุ่มๆตามช่วงของค่าแบ่งที่กำหนด
ถ้ามีชุดข้อมูลตัวเลขอยู่ชุดหนึ่ง อาจเป็นลิสต์, อาเรย์ หรือซีรีส์ก็ได้ แล้วต้องการทำการแบ่งกลุ่มตามค่ามากน้อยสามารถทำได้โดยใช้ฟังก์ชัน pd.cut

ตัวอย่าง มีข้อมูลน้ำหนักของโปเกมอนจำนวนหนึ่ง
import pandas as pd
saiphan = ['นาเอเทิล','ฮายาชิงาเมะ','โดไดโทส',
           'ฮิโกะซารุ'
,'โมวกะซารุ','โกวกะซารุ',
           'พจจามะ'
,'พตไทชิ','เอ็มเพิร์ต']
namnak = [10.2,97.2,310.0,6.2,22.0,55.0,5.2,23.0,84.5]
pokemon = pd.Series(namnak,index=saiphan,name='น้ำหนัก')
print(pokemon)



ได้
นาเอเทิล        10.2
ฮายาชิงาเมะ     97.2
โดไดโทส        310.0
ฮิโกะซารุ         6.2
โมวกะซารุ       22.0
โกวกะซารุ       55.0
พจจามะ           5.2
พตไทชิ          23.0
เอ็มเพิร์ต      84.5
Name: น้ำหนัก, dtype: float64

นำมาแบ่งกลุ่มตามช่วงน้ำหนัก โดยแบ่งที่ 50, 100 และ 300 ก็ให้ใส่ซีรีส์ที่ต้องการแบ่งเป็นอาร์กิวเมนต์ตัวแรก ส่วนตัวที่ ๒ เป็นลิสต์ของช่วงที่ต้องการ
print(pd.cut(pokemon,[0,50,100,300]))

ได้
นาเอเทิล         (0, 50]
ฮายาชิงาเมะ    (50, 100]
โดไดโทส              NaN
ฮิโกะซารุ         (0, 50]
โมวกะซารุ        (0, 50]
โกวกะซารุ      (50, 100]
พจจามะ           (0, 50]
พตไทชิ           (0, 50]
เอ็มเพิร์ต     (50, 100]
Name: น้ำหนัก, dtype: category
Categories (3, object): [(0, 50] < (50, 100] < (100, 300]]

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

จำนวนกลุ่มที่แบ่งจะเท่ากับจำนวนลิสต์ของค่าแบ่ง ลบด้วย 1

ส่วนค่าที่เกินกว่าขอบเขตของตัวเลขในลิสต์จะไม่สังกัดอยู่ในกลุ่มไหนเลย และได้ค่าเป็น NaN

นอกจากจะแบ่งโดยกำหนดค่าที่ต้องการแบ่งแล้ว ยังอาจแบ่งโดยใช้จำนวนช่วงที่ต้องการแบ่งได้ด้วย ในกรณีนี้จะเป็นการแบ่งให้ค่าแต่ละช่วงเท่ากันโดยนับจากขอบเขตต่ำสุดและสูงสุดของข้อมูล เช่น
klum = pd.cut(pokemon,4)
print(pd.concat([pokemon,klum],axis=1))

ได้
  น้ำหนัก น้ำหนัก
นาเอเทิล 10.2 (4.895, 81.4]
ฮายาชิงาเมะ 97.2 (81.4, 157.6]
โดไดโทส 310.0 (233.8, 310]
ฮิโกะซารุ 6.2 (4.895, 81.4]
โมวกะซารุ 22.0 (4.895, 81.4]
โกวกะซารุ 55.0 (4.895, 81.4]
พจจามะ 5.2 (4.895, 81.4]
พตไทชิ 23.0 (4.895, 81.4]
เอ็มเพิร์ต 84.5 (81.4, 157.6]

นอกจากจะแสดงชื่อกลุ่มเป็นค่าช่วงแล้วเรายังสามารถกำหนดชื่อกลุ่มตามที่ต้องการได้ด้วยการใส่คีย์เวิร์ด labels เป็นลิสต์ของชื่อทั้งหมด โดยจะต้องมีจำนวนเท่ากับจำนวนส่วนที่แบ่ง
klum = pd.cut(pokemon,[0,50,100,500],labels=['เบา','ปานกลาง','หนัก'])
klum.name = 'ถือว่า'
print(pd.concat([pokemon,klum],axis=1))

ได้
  น้ำหนัก ถือว่า
นาเอเทิล 10.2 เบา
ฮายาชิงาเมะ 97.2 ปานกลาง
โดไดโทส 310.0 หนัก
ฮิโกะซารุ 6.2 เบา
โมวกะซารุ 22.0 เบา
โกวกะซารุ 55.0 ปานกลาง
พจจามะ 5.2 เบา
พตไทชิ 23.0 เบา
เอ็มเพิร์ต 84.5 ปานกลาง

แต่ถ้าหากไม่ต้องการให้มีชื่ออะไรเลย แม้แต่ค่าช่วงก็ไม่ต้องการก็อาจใส่ labels=False แบบนี้จะได้ค่าเป็นเลขดัชนีกลุ่ม 0,1,2,...
klum = pd.cut(pokemon,[0,50,100,500],labels=False)
klum.name = 'กลุ่ม'
print(pd.concat([pokemon,klum],axis=1))

ได้
  น้ำหนัก กลุ่ม
นาเอเทิล 10.2 0
ฮายาชิงาเมะ 97.2 1
โดไดโทส 310.0 2
ฮิโกะซารุ 6.2 0
โมวกะซารุ 22.0 0
โกวกะซารุ 55.0 1
พจจามะ 5.2 0
พตไทชิ 23.0 0
เอ็มเพิร์ต 84.5 1

ปกติแล้วช่วงจะถูกแบ่งแบบ (a,b] นั่นคือแต่ละช่วงจะรวมถึงค่ามากสุด (b) แต่ไม่รวมถึงค่าน้อยสุด (a)

แต่สามารถเปลี่ยนให้กลายเป็น [a,b) แบบนี้ได้โดยใส่คีย์เวิร์ด right=0 (ถ้าไม่ใส่จะเป็นค่าตั้งต้น right=1)

ตัวอย่าง
klum1 = pd.cut(pokemon,[0,22,55,500],right=0)
klum1.name = 'right=0'
klum2 = pd.cut(pokemon,[0,22,55,500])
klum2.name = 'right=1'
print(pd.concat([pokemon,klum1,klum2],axis=1))

ได้
  น้ำหนัก right=0 right=1
นาเอเทิล 10.2 [0, 22) (0, 22]
ฮายาชิงาเมะ 97.2 [55, 500) (55, 500]
โดไดโทส 310.0 [55, 500) (55, 500]
ฮิโกะซารุ 6.2 [0, 22) (0, 22]
โมวกะซารุ 22.0 [22, 55) (0, 22]
โกวกะซารุ 55.0 [55, 500) (22, 55]
พจจามะ 5.2 [0, 22) (0, 22]
พตไทชิ 23.0 [22, 55) (22, 55]
เอ็มเพิร์ต 84.5 [55, 500) (55, 500]



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

pd.qcut จะคล้ายกับ pd.cut แต่จะแบ่งโดยใช้ค่าลำดับที่เป็นเกณฑ์ในการแบ่ง ค่าลำดับที่ในที่นี้คือค่าลำดับจากต่ำ โดยที่ตัวที่มีค่าน้อยสุดจะมีค่าลำดับเป็น 0 ตัวที่มากสุดจะเป็น 1 และตัวอื่นๆที่ค่าอยู่ระหว่างกลางก็จะมีค่าอยู่ระหว่าง 0 ถึง 1 ไล่กันไป

ค่าที่ต้องใส่ใน pd.qcut จะไม่ใช่ค่าแบ่งเหมือนอย่างของ pd.cut แต่จะใส่เป็นลิสต์ของค่าที่ไล่ตั้งแต่ 0 ถึง 1

ตัวอย่าง
klum = pd.qcut(pokemon,[0,0.25,0.5,0.75,1])
print(pd.concat([pokemon,klum],axis=1))

ได้
  น้ำหนัก น้ำหนัก
นาเอเทิล 10.2 [5.2, 10.2]
ฮายาชิงาเมะ 97.2 (84.5, 310]
โดไดโทส 310.0 (84.5, 310]
ฮิโกะซารุ 6.2 [5.2, 10.2]
โมวกะซารุ 22.0 (10.2, 23]
โกวกะซารุ 55.0 (23, 84.5]
พจจามะ 5.2 [5.2, 10.2]
พตไทชิ 23.0 (10.2, 23]
เอ็มเพิร์ต 84.5 (23, 84.5]

จะเห็นว่าช่วงที่ถูกแบ่งแต่ละช่วงจะแบ่งตามค่าของข้อมูลพอดี

จะกำหนดเป็นจำนวนช่วงที่จะแบ่งก็ได้เช่นกัน การใช้แบบนี้จะทำให้จำนวนในแต่ละกลุ่มเท่ากันพอดีหรือใกล้เคียงกันมากที่สุด
klum = pd.qcut(pokemon,3)
print(pd.concat([pokemon,klum],axis=1))

ได้
  น้ำหนัก น้ำหนัก
นาเอเทิล 10.2 [5.2, 18.0667]
ฮายาชิงาเมะ 97.2 (64.833, 310]
โดไดโทส 310.0 (64.833, 310]
ฮิโกะซารุ 6.2 [5.2, 18.0667]
โมวกะซารุ 22.0 (18.0667, 64.833]
โกวกะซารุ 55.0 (18.0667, 64.833]
พจจามะ 5.2 [5.2, 18.0667]
พตไทชิ 23.0 (18.0667, 64.833]
เอ็มเพิร์ต 84.5 (64.833, 310]

การใส่คีย์เวิร์ด labels เพื่อตั้งชื่อกลุ่มแทนค่าช่วงก็สามารถทำได้เช่นเดียวกัน



ใช้คู่กับ groupby ในการแบ่งกลุ่ม
ข้อมูลที่ได้จาก pd.cut และ pd.qcut สามารถนำมาใช้เป็นเกณฑ์การแบ่งกลุ่มใน groupby ได้

ตัวอย่าง
klum = pd.cut(pokemon,[0,50,100,500])
print(pokemon.groupby(klum).groups)
print(pokemon.groupby(klum).apply(dict))

ได้
{'(100, 500]': ['โดไดโทส'], '(50, 100]': ['ฮายาชิงาเมะ', 'โกวกะซารุ', 'เอ็มเพิร์ต'], '(0, 50]': ['นาเอเทิล', 'ฮิโกะซารุ', 'โมวกะซารุ', 'พจจามะ', 'พตไทชิ']}
น้ำหนัก
(0, 50]     นาเอเทิล        10.2
            พจจามะ           5.2
            พตไทชิ          23.0
            ฮิโกะซารุ         6.2
            โมวกะซารุ       22.0
(50, 100]   ฮายาชิงาเมะ     97.2
            เอ็มเพิร์ต      84.5
            โกวกะซารุ       55.0
(100, 500]  โดไดโทส        310.0
Name: น้ำหนัก, dtype: float64



อ้างอิง


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


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

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

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

หมวดหมู่

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

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

สารบัญ

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

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

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文