Pythonic Way: แยกหลักเลขรางวัลที่ 1 ด้วย zip(data)

วิเคราะห์สลากกินแบ่งด้วย Zip และ Counter: เมื่อความเจ๋งของ Python ทำให้โค้ดสั้นกว่าที่คิด

หนึ่งในคำถามที่น่าสนใจสำหรับคนเริ่มเรียน Python คือ

หากเรามีข้อมูลรางวัลที่ 1 ย้อนหลังหลายงวด เราจะหาได้อย่างไรว่าแต่ละหลักมีเลขใดออกบ่อยที่สุด

ตัวอย่างข้อมูล

results = [
    "123456",
    "987654",
    "123789",
    "555555",
    "120456",
]

ข้อมูลแต่ละรายการคือเลขรางวัลที่ 1 จำนวน 6 หลัก

สิ่งที่เราอยากรู้คือ

  • หลักแสนเลขใดออกบ่อยที่สุด

  • หลักหมื่นเลขใดออกบ่อยที่สุด

  • หลักหน่วยเลขใดออกบ่อยที่สุด

หลายคนอาจเริ่มจากการเขียน Loop ซ้อนกันหลายชั้น แต่ Python มีวิธีที่สวยกว่านั้นมาก

ขั้นตอนที่ 1: เปลี่ยนแถวให้กลายเป็นคอลัมน์ด้วย zip()

ลองพิจารณาข้อมูลในรูปแบบตาราง

1 2 3 4 5 6
9 8 7 6 5 4
1 2 3 7 8 9
5 5 5 5 5 5
1 2 0 4 5 6

สิ่งที่เราต้องการจริง ๆ คือ

(1, 9, 1, 5, 1)  <- หลักแสน
(2, 8, 2, 5, 2)  <- หลักหมื่น
(3, 7, 3, 5, 0)  <- หลักพัน
(4, 6, 7, 5, 4)  <- หลักร้อย
(5, 5, 8, 5, 5)  <- หลักสิบ
(6, 4, 9, 5, 6)  <- หลักหน่วย

Python ทำสิ่งนี้ได้ด้วยคำสั่งเดียว

zip(*results)

ผลลัพธ์

[
    ('1', '9', '1', '5', '1'),
    ('2', '8', '2', '5', '2'),
    ('3', '7', '3', '5', '0'),
    ('4', '6', '7', '5', '4'),
    ('5', '5', '8', '5', '5'),
    ('6', '4', '9', '5', '6')
]

นี่คือการ "Transpose" ตาราง หรือสลับแถวให้กลายเป็นคอลัมน์

แล้ว * หน้า results คืออะไร

หลายคนรู้จักการใช้ * ในการแตก List

numbers = [1, 2, 3]

print(*numbers)

เทียบเท่ากับ

print(1, 2, 3)

หลักการเดียวกันถูกนำมาใช้กับ zip

zip(*results)

Python จะมองเป็น

zip(
    "123456",
    "987654",
    "123789",
    "555555",
    "120456"
)

จากนั้น zip จะจับสมาชิกตำแหน่งเดียวกันมารวมกัน

จึงได้แต่ละหลักออกมาเป็นกลุ่มอย่างสวยงาม

ขั้นตอนที่ 2: นับความถี่ด้วย Counter

เมื่อแต่ละหลักถูกแยกออกมาแล้ว การนับจำนวนครั้งที่พบเลขแต่ละตัวทำได้ง่ายมากด้วย Counter

from collections import Counter

for digits in zip(*results):
    print(Counter(digits))

ผลลัพธ์

Counter({'1': 3, '9': 1, '5': 1})
Counter({'2': 3, '8': 1, '5': 1})
Counter({'3': 2, '7': 1, '5': 1, '0': 1})
...

Counter จะทำหน้าที่นับจำนวนครั้งที่แต่ละค่าปรากฏโดยอัตโนมัติ

แสดงผลพร้อมชื่อหลัก

from collections import Counter

positions = [
    "แสน",
    "หมื่น",
    "พัน",
    "ร้อย",
    "สิบ",
    "หน่วย"
]

for name, digits in zip(positions, zip(*results)):
    print(f"หลัก{name}")
    print(Counter(digits).most_common())

ผลลัพธ์

หลักแสน
[('1', 3), ('9', 1), ('5', 1)]

หลักหมื่น
[('2', 3), ('8', 1), ('5', 1)]

...

เมธอด most_common() จะเรียงจากค่าที่พบมากที่สุดลงมา ทำให้สามารถดูแนวโน้มของแต่ละหลักได้ทันที

เวอร์ชัน Pythonic ที่สุด

เมื่อเข้าใจหลักการทั้งหมดแล้ว โค้ดสามารถย่อเหลือเพียงบรรทัดเดียว

from collections import Counter

freq = [Counter(col) for col in zip(*results)]

ตัวแปร freq จะเก็บ Counter ของทั้ง 6 หลักไว้พร้อมใช้งาน

สรุป

ตัวอย่างนี้แสดงให้เห็นความสวยงามของ Python ได้เป็นอย่างดี

  • * ใช้สำหรับแตกข้อมูลออกเป็น Argument

  • zip() ใช้รวมสมาชิกตำแหน่งเดียวกัน

  • zip(*data) ใช้สลับแถวและคอลัมน์ (Transpose)

  • Counter() ใช้นับความถี่ของข้อมูลโดยอัตโนมัติ

จากโจทย์วิเคราะห์สลากกินแบ่งที่ดูเหมือนต้องใช้ Loop ซับซ้อนหลายชั้น กลับสามารถเขียนได้สั้น กระชับ และอ่านง่ายด้วยการผสมผสานความสามารถของ Zip และ Counter เข้าด้วยกัน

นี่คือหนึ่งในเหตุผลที่ทำให้นักพัฒนาจำนวนมากรู้สึกว่า Python เป็นภาษาที่สามารถเปลี่ยนงานวิเคราะห์ข้อมูลที่ยุ่งยากให้กลายเป็นโค้ดที่สวยงามได้อย่างน่าทึ่ง

ความคิดเห็น

โพสต์ยอดนิยมจากบล็อกนี้

รับติดตั้งระบบป้ายโฆษณาดิจิตอล Digital Signage ตั้งเวลาทำงานและควบคุมผ่าน Internet ได้

แจกฟรี ระบบแสดงคิวงาน สำหรับร้านนวด สปา หรือ ร้านทำผม

บริการออกแบบ Character Agent สำหรับธุรกิจ

ป้ายดิจิตอลที่โต้ตอบได้