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



การใช้ collections.Counter ใน python
เขียนเมื่อ 2020/04/13 22:04
แก้ไขล่าสุด 2021/09/28 16:42



มอดูล collections ของไพธอนประกอบไปด้วยออบเจ็กต์จิปาถะที่อาจสะดวกที่จะใช้ในงานบางอย่าง เช่น collections.OrderedDict ที่ได้เขียนไปใน https://phyblas.hinaboshi.com/20190706

ในบทความนี้จะแนะนำออบเจ็กต์อีกตัวที่อาจมีโอกาสได้ใช้บ่อย คือ collections.Counter



Counter เป็นออบเจ็กต์ที่ใช้งานสะดวกในเวลาที่เราต้องการจะนับจำนวนของอะไรบางอย่างว่ามีอะไรอยู่เท่าไหร่

เช่นมีลิสต์ที่มีตัวเลขอยู่จำนวนหนึ่ง ต้องการจะนับดูว่ามีตัวเลขอะไรอยู่เท่าไหร่ ถ้าใช้ดิกชันนารีทั่วไปอาจเขียนได้แบบนี้
lislek = [12,10,11,12,14,11,11,13,11,10,13,12]
naplek = {}
for lek in lislek:
    if(lek in naplek):
        naplek[lek] += 1
    else:
        naplek[lek] = 1

print(naplek) # ได้ {12: 3, 10: 2, 11: 4, 14: 1, 13: 2}

แต่ถ้าใช้ Counter ก็จะทำได้โดยง่าย โดยแค่ใส่ลิสต์นั้นลงไปก็จะทำการนับให้เลย
from collections import Counter

lislek = [22,20,21,22,24,21,21,23,21,20,23,22]
naplek = Counter(lislek)
print(naplek) # ได้ Counter({21: 4, 22: 3, 20: 2, 23: 2, 24: 1})

ผลที่ได้จะได้เป็นออบเจ็กต์ Counter ที่เรียงลำดับตามจำนวนจากมากไปน้อยให้เลย

ออบเจ็กต์ Counter นี้โดยพื้นฐานแล้วมีส่วนคล้ายกับออบเจ็กต์ชนิดดิกชันนารี คือสามารถดูค่าได้โดยใส่คีย์ เพียงแต่ถ้าใส่คีย์ที่ไม่มีจะได้ค่า 0 และมีเมธอด .values() .keys() .items() และสามารถแปลงเป็นดิกชันนารีได้โดยง่าย
print(naplek[22]) # ได้ 3
print(naplek[26]) # ได้ 0
print(naplek.keys()) # ได้ dict_keys([22, 20, 21, 24, 23])
print(naplek.values()) # ได้ dict_values([3, 2, 4, 1, 2])
print(naplek.items()) # ได้ dict_items([(22, 3), (20, 2), (21, 4), (24, 1), (23, 2)])
print(dict(naplek)) # ได้ {22: 3, 20: 2, 21: 4, 24: 1, 23: 2}

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

Counter นอกจากจะใช้กับลิสต์แล้วก็ยังใช้กับสายอักขระได้ด้วย โดยถ้าป้อนสายอักขระลงไปมันก็จะช่วยนับจำนวนให้ว่ามีอักษรตัวไหนอยู่กี่ตัว
napakson = Counter('จอดรถตรงตรอกยอมทนอดนอนอดกรนรอยลภมรดมดอกหอมบนขอนตรงคลองมอญ')
print(napakson) # ได้ Counter({'อ': 12, 'ร': 7, 'น': 6, 'ด': 5, 'ม': 5, 'ต': 3, 'ง': 3, 'ก': 3, 'ย': 2, 'ล': 2, 'จ': 1, 'ถ': 1, 'ท': 1, 'ภ': 1, 'ห': 1, 'บ': 1, 'ข': 1, 'ค': 1, 'ญ': 1})

Counter ที่สร้างมาแล้วสามารถเพิ่มข้อมูลลงไปได้ เช่นโดยใช้ +=

เช่นอาจลองสร้าง Counter ว่างเปล่าแล้วค่อยๆใส่สมาชิกเพิ่มเข้าไป โดยไล่นับเพิ่มไปเรื่อยๆเช่น
lislek = [2,1,4,5,2,6,2,6,3,2,4,1,2,2,3,2,1,4,2,4]
naplek = Counter()
for lek in lislek:
    naplek[lek] += 1

print(naplek)

หรืออาจใช้เมธอด .update() เพื่อทำการเพิ่มข้อมูลใส่เข้าไปเรื่อยๆเช่น
napakson = Counter()
napakson.update('กนกพล')
print(napakson) # ได้ Counter({'ก': 2, 'น': 1, 'พ': 1, 'ล': 1})
napakson.update('คนตลก')
print(napakson) # ได้ Counter({'ก': 3, 'น': 2, 'ล': 2, 'พ': 1, 'ค': 1, 'ต': 1})
napakson.update('ตกคลอง')
print(napakson) # ได้ Counter({'ก': 4, 'ล': 3, 'น': 2, 'ค': 2, 'ต': 2, 'พ': 1, 'อ': 1, 'ง': 1})

นอกจากนี้ยังสามารถสร้างโดยแทนเลขจำนวนข้อมูลเข้าไป เช่นเดียวกับเวลาสร้างดิกชันนารีก็ได้
nap = Counter(a=7,b=2)
print(nap) # ได้ Counter({'a': 7, 'b': 2})

หรือจะสร้างโดยแปลงจากดิกชันนารีก็ได้
dic = {11: 2, 13: 4}
nap = Counter(dic)
print(nap) # ได้ Counter({13: 4, 11: 2})

กรณีที่สร้างด้วยวิธีนี้ ค่าอาจจะไม่ใช่ตัวเลขก็ได้ ไม่ทำให้เกิดข้อผิดพลาดอะไร แต่ถ้าตัวที่ไม่ใช่ตัวเลขถูกใส่ข้อมูลเพิ่มเช่นใช้เมธอด .update() ในสภาพแบบนั้นก็จะเกิดข้อผิดพลาด
nap = Counter({"ก": "ข", "ค": "ง"})
print(nap) # ได้ Counter({'ค': 'ง', 'ก': 'ข'})
nap.update("ก") # ได้ TypeError: can only concatenate str (not "int") to str

ถ้าใช้เมธอด .most_common() จะได้ลิสต์ของแต่ละคู่โดยเรียงจากมากไปน้อยตามจำนวนลำดับที่กำหนด แต่ถ้าไม่ได้ใส่เลขลำดับจะแสดงทั้งหมด
nap = Counter('สมพรสวดวนจนอรชรสองคนฉงนฉงวยงวยงงคอตกยอมนอนลงบนบก')
print(nap.most_common(3)) # ได้ [('น', 7), ('ง', 7), ('อ', 5)]
print(nap.most_common()) # ได้ [('น', 7), ('ง', 7), ('อ', 5), ('ว', 4), ('ส', 3), ('ร', 3), ('ย', 3), ('ม', 2), ('ค', 2), ('ฉ', 2), ('ก', 2), ('บ', 2), ('พ', 1), ('ด', 1), ('จ', 1), ('ช', 1), ('ต', 1), ('ล', 1)]

ถ้าใช้เมธอด .elements() จะได้อิเทอเรเตอร์ที่ไล่สมาชิกที่มีในนั้นทีละตัว
nap = Counter('ดวงกมลชงนมผงรอชมภมรบนดอน')
print(nap) # ได้ Counter({'ม': 4, 'ง': 3, 'น': 3, 'ด': 2, 'ช': 2, 'ร': 2, 'อ': 2, 'ว': 1, 'ก': 1, 'ล': 1, 'ผ': 1, 'ภ': 1, 'บ': 1})
el = nap.elements()
print(el) # ได้ <itertools.chain object at 0x10bbfae50>
print(list(el)) # ได้ ['ด', 'ด', 'ว', 'ง', 'ง', 'ง', 'ก', 'ม', 'ม', 'ม', 'ม', 'ล', 'ช', 'ช', 'น', 'น', 'น', 'ผ', 'ร', 'ร', 'อ', 'อ', 'ภ', 'บ']

กรณีที่สร้างโดยกำหนดจำนวนเข้าไปโดยตรงก็จะออกมาเรียงตามลำดับที่ใส่โดยตัวเดียวกันอยู่ติดกัน
nap = Counter(na=3,ka=4,ma=2)
print(list(nap.elements())) # ได้ ['na', 'na', 'na', 'ka', 'ka', 'ka', 'ka', 'ma', 'ma']

Counter สามารถนำมาบวกลบกันได้ โดยการบวกจะเป็นการเอาสมาชิกที่มีมารวมกัน
a = Counter('ปลวกจกหนอนลงคอ')
b = Counter('คงลอยคอลอยวน')
print(a) # ได้ Counter({'ล': 2, 'ก': 2, 'น': 2, 'อ': 2, 'ป': 1, 'ว': 1, 'จ': 1, 'ห': 1, 'ง': 1, 'ค': 1})
print(b) # ได้ Counter({'อ': 3, 'ค': 2, 'ล': 2, 'ย': 2, 'ง': 1, 'ว': 1, 'น': 1})
print(a+b) # ได้ Counter({'อ': 5, 'ล': 4, 'น': 3, 'ค': 3, 'ว': 2, 'ก': 2, 'ง': 2, 'ย': 2, 'ป': 1, 'จ': 1, 'ห': 1})

ส่วนการลบจะเอามาหักลบกัน เพียงแต่ถ้าติดลบหรือเป็น 0 จะหายไปเลย ไม่แสดง
a = Counter('พลพรรคครบสองคน')
b = Counter('ลงคอรอครอบครอง')
print(a) # ได้ Counter({'ร': 3, 'ค': 3, 'พ': 2, 'ล': 1, 'บ': 1, 'ส': 1, 'อ': 1, 'ง': 1, 'น': 1})
print(b) # ได้ Counter({'อ': 4, 'ค': 3, 'ร': 3, 'ง': 2, 'ล': 1, 'บ': 1})
print(a-b) # ได้ Counter({'พ': 2, 'ส': 1, 'น': 1})
print(b-a) # ได้ Counter({'อ': 3, 'ง': 1})

นอกจากนี้ยังมีคุณสมบัติแปลกๆเช่นถ้ามีจำนวนทั้งบวกและลบอยู่ในนี้ หากเจอเครื่องหมายบวกจะกำจัดตัวที่เป็นลบ หากเจอเครื่องหมายลบจะเหลือแต่ตัวที่เป็นลบ และค่าจะกลายเป็นบวก
nap = Counter(ha=3,na=-2,ya=-4)
print(+nap) # ได้ Counter({'ha': 3})
print(-nap) # ได้ Counter({'ya': 4, 'na': 2})

สามารถใช้ตัวดำเนินการ & และ | ซึ่งจะให้ผลคล้ายกับการอินเตอร์เซ็กและยูเนียนกันของเซ็ต
a = Counter('สองสมรยอมลงคลองลอยคอ')
b = Counter('รอชวนภมรดมดอมดอกขจร')
print(a) # ได้ Counter({'อ': 5, 'ง': 3, 'ล': 3, 'ส': 2, 'ม': 2, 'ย': 2, 'ค': 2, 'ร': 1})
print(b) # ได้ Counter({'ร': 3, 'อ': 3, 'ม': 3, 'ด': 3, 'ช': 1, 'ว': 1, 'น': 1, 'ภ': 1, 'ก': 1, 'ข': 1, 'จ': 1})
print(a|b) # ได้ Counter({'อ': 5, 'ง': 3, 'ม': 3, 'ร': 3, 'ล': 3, 'ด': 3, 'ส': 2, 'ย': 2, 'ค': 2, 'ช': 1, 'ว': 1, 'น': 1, 'ภ': 1, 'ก': 1, 'ข': 1, 'จ': 1})
print(b&a) # ได้ Counter({'อ': 3, 'ม': 2, 'ร': 1})

เมธอด .subtract() จะให้ผลคล้ายกับการเอามาลบกัน แต่จะเป็นการเปลี่ยนแปลง Counter ตัวทางซ้าย (ตัวที่ใช้เมธอด) แทนที่จะคืนค่า และจำนวนที่เป็น 0 และติดลบจะยังคงเหลืออยู่
a = Counter('ฝนตกตลอด')
b = Counter('ตกตรงตรอก')
print(a) # ได้ Counter({'ต': 2, 'ฝ': 1, 'น': 1, 'ก': 1, 'ล': 1, 'อ': 1, 'ด': 1})
print(b) # ได้ Counter({'ต': 3, 'ก': 2, 'ร': 2, 'ง': 1, 'อ': 1})
a.subtract(b)
print(a) # ได้ Counter({'ฝ': 1, 'น': 1, 'ล': 1, 'ด': 1, 'อ': 0, 'ต': -1, 'ก': -1, 'ง': -1, 'ร': -2})
a.subtract(b)
print(a) # ได้ Counter({'ฝ': 1, 'น': 1, 'ล': 1, 'ด': 1, 'อ': -1, 'ง': -2, 'ก': -3, 'ต': -4, 'ร': -4})



สุดท้ายขอยกตัวอย่างการนำมาประยุกต์ใช้งาน เช่นนับจำนวนเฉพาะที่เป็นองค์ประกอบในตัวเลข
def yaek(n):
    nap = Counter()
    i = 2
    while(i*2<=n):
        if(n%i==0):
            nap[i] += 1
            n = n//i
        elif(i==2):
            i += 1
        else:
            i += 2
    if(n!=1):
        nap[n] += 1
    return nap

print(yaek(104)) # ได้ Counter({2: 3, 13: 1})
print(yaek(150)) # ได้ Counter({5: 2, 2: 1, 3: 1})
print(yaek(508)) # ได้ Counter({2: 2, 127: 1})
print(yaek(4500)) # ได้ Counter({5: 3, 2: 2, 3: 2})
print(yaek(16129)) # ได้ Counter({127: 2})
print(yaek(19600)) # ได้ Counter({2: 4, 5: 2, 7: 2})
print(yaek(51000)) # ได้ Counter({2: 3, 5: 3, 3: 1, 17: 1})




อ้างอิง


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

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

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

หมวดหมู่

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

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

สารบัญ

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

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

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文