บทนำ
เมื่อต้องการแยกสตริงเป็นกลุ่มของตัวอักษรที่ติดต่อกันและเหมือนกัน (เช่น "aaabbb" → ["aaa", "bbb"]) ใช้ itertools.groupby() ซึ่งเป็นเครื่องมือมาตรฐานสำหรับงานนี้
หลักการของ groupby()
groupby(iterable, key=None) ใหม่กลุ่ม (key, group) สำหรับแต่ละกลุ่มต่อเนื่องที่มีค่า key เท่ากัน
ตัวอย่างโค้ด
from itertools import groupby
my_string = 'pppyyytthhhhhhhoooooonnn'
print(f"สตริงต้นทาง: {my_string}")
# ใช้ groupby แยกกลุ่ม ต่อด้วย join รวมกลุ่มกลับเป็นสตริง
result = [''.join(group) for _, group in groupby(my_string)]
print("ผลลัพธ์:", result)
ผลลัพธ์
สตริงต้นทาง: pppyyytthhhhhhhoooooonnn
ผลลัพธ์: ['ppp', 'yyy', 'tt', 'hhhhhhh', 'oooooo', 'nnn']
การใช้งานขั้นสูง
1. นับความยาวแต่ละกลุ่ม (Run-Length Encoding)
rle = [(char, len(list(group))) for char, group in groupby(my_string)]
print(rle)
# [('p', 3), ('y', 3), ('t', 2), ('h', 7), ('o', 6), ('n', 3)]
2. กรองเฉพาะกลุ่มที่ยาวพอ
# เฉพาะกลุ่มที่มีความยาว >= 3
long_groups = [''.join(g) for k, g in groupby(my_string) if sum(1 for _ in g) >= 3]
# ต้องระวัง: group เป็น iterator ใช้ได้ครั้งเดียว
# วิธีที่ถูกต้อง:
long_groups = []
for k, g in groupby(my_string):
group_list = list(g)
if len(group_list) >= 3:
long_groups.append(''.join(group_list))
print(long_groups) # ['ppp', 'yyy', 'hhhhhhh', 'oooooo', 'nnn']
3. ใช้กับลิสต์ (ไม่ใช่แค่สตริง)
my_list = [1, 1, 1, 2, 2, 3, 3, 3, 3, 1]
result = [list(g) for _, g in groupby(my_list)]
print(result)
# [[1, 1, 1], [2, 2], [3, 3, 3, 3], [1]]
ข้อควรระวังสำคัญ
- groupby ต้องการข้อมูลที่จัดเรียงแล้ว (sorted) หากข้อมูลไม่ได้เรียง กลุ่มเดียวกันที่แยกกันจะไม่ถูกรวม
- กลุ่มที่ได้เป็น iterator ใช้ได้ครั้งเดียว ต้องแปลงเป็น list ก่อนใช้ซ้ำ
- ฟังก์ชัน
keyสามารถกำหนดเองได้ เช่นkey=str.lowerสำหรับ case-insensitive
สรุป
itertools.groupby เป็นวิธีมาตรฐานและมีประสิทธิภาพสำหรับการแยกกลุ่มข้อมูลติดต่อกัน ใช้ร่วมกับ ''.join(group) เพื่อรวมกลุ่มกลับเป็นสตริง ได้ผลลัพธ์เป็นลิสต์ของสตริงย่อยแต่ละกลุ่ม