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 ได้เป็นอย่างดี
*ใช้สำหรับแตกข้อมูลออกเป็น Argumentzip()ใช้รวมสมาชิกตำแหน่งเดียวกันzip(*data)ใช้สลับแถวและคอลัมน์ (Transpose)Counter()ใช้นับความถี่ของข้อมูลโดยอัตโนมัติ
จากโจทย์วิเคราะห์สลากกินแบ่งที่ดูเหมือนต้องใช้ Loop ซับซ้อนหลายชั้น กลับสามารถเขียนได้สั้น กระชับ และอ่านง่ายด้วยการผสมผสานความสามารถของ Zip และ Counter เข้าด้วยกัน
นี่คือหนึ่งในเหตุผลที่ทำให้นักพัฒนาจำนวนมากรู้สึกว่า Python เป็นภาษาที่สามารถเปลี่ยนงานวิเคราะห์ข้อมูลที่ยุ่งยากให้กลายเป็นโค้ดที่สวยงามได้อย่างน่าทึ่ง
ความคิดเห็น
แสดงความคิดเห็น