บทความนี้เป็นการประยุกต์ใช้โครงสร้างข้อมูลประเภทลิสต์ (list) ของภาษาไพธอนเพื่อเก็บการนับความถี่ของตัวเลขที่สุ่ม ซึ่งมีประโยชน์ต่อการนำไปใช้ทางสถิติต่อไป ซึ่งบทความนี้อาศัยความรู้เรื่องของการสุ่มตัวเลข และการใช้ตัวแปรประเภทลิสต์ โดยทดสอบการทำงานกับ Micropython บนไมโครคอนโทรลเลอร์ esp8266 และ esp32
ปัญหา
จะสร้างตารางความถี่ของตัวเลขที่สุ่มค่าใชนช่วง 0 ถึง 20 จำนวน 100 ชุดได้อย่างไร?
แนวคิดในการแก้ไขปัญหา
อุปกรณ์ประกอบการทดลองในครั้งนี้ทางทีมงานเราใช้บอร์ด dCore-miniML (ดังภาพที่ 1) ซึ่งเป็นบอร์ดขยายของ ESP32-CAM และบอร์ด ESP8266 ที่ต่อเชื่อมกับ ET-BASE NodeMCU เพื่อทดสอบการทำงาน
ก่อนอื่นต้องดูคุณสมบัติของคลาสลิสต์ที่ Micropython ของ esp32 รองรับเป็นดังภาพที่ 2 และของ esp8266 เป็นดังภาพที่ 3 จะพบว่ารองรับการทำงานที่เหมือนกันทำให้การเขียนโปรแกรมมีความสะดวกยิ่งขึ้น
การสร้างวัตถุประเภทลิสต์สามารถสร้างได้หลายวิธี แต่ที่ทีมงานเรานิยมใช้เป็นดังนี้
วัตถุ = []
จากภาพที่ 2 และ 3 สามารถสรุปหน้าที่ของคำสั่งได้ดังนี้
- append( ข้อมูล ) เป็นการเพิ่มข้อมูลเข้าลิสต์
- clear( ) เป็นการล้างค่าที่เก็บทั้งหมด
- copy( ) เป็นการคัดลอกลิสต์ไปเป็นข้อมูลอีกชุดหนึ่ง
- count( ) นับจำนวนสมาชิกในลิสต์
- extend( ลิสต์ ) นำลิสต์ที่กำหนดมาต่อท้าย
- index( ข้อมูล ) หาลำดับของ ‘ข้อมูล’ ในลิสต์
- insert( ลำดับ, ข้อมูล ) แทรก ‘ข้อมูล’ เข้าไปยัง ‘ลำดับ’ ที่ระบุ
- pop( ) นำข้อมูลที่ท้ายลิสต์ออก และคืนค่าที่นำออกกลับมา
- remove( ข้อมูล ) ลบลิสต์ที่ต้องการออก ซึ่งถ้าพบข้อมูลหลายชุดจะลบลำดับแรกที่พบออก
- reverse() ทำการกลับลำดับข้อมูลภายในลิสต์
- sort( reverse=False ) ทำการเรียงข้อมูลในลิสต์ ถ้าต้องการเรียงจากมากไปหาน้อยให้ระบุ reverse=True
ตัวอย่างโปรแกรม
ทดลองเขียนแบบที่ 1
ตัวอย่างแรกเป็นการใช้วัตถุประเภทลิสต์ 2 ชุด ชื่อ data และ freq สำหรับเก็บข้อมูลและจำนวนของข้อมูล โดยการสุ่มนั้นใช้ getrandbits(5)%21 หมายถึงสุ่มค่าขนาด 5 บิต คือ 0 ถึง 31 แต่ต้องการแค่ 0 ถึง 20 จึงทำการหารแบบ % ด้วย 21 เพื่อให้ได้ค่าในช่วง 0 ถึง 20 หลังจากนั้นทำการตรวจสอบค่าที่สุ่มได้กับที่มีมาก่อน ถ้าพบ ให้เพิ่มค่าความถี่ขึ้น 1 ถ้าไม่พบจะเพิ่มรายการข้อมูลเข้าไปใน data พร้อมเพิ่มความถี่ 1 ใน freq เมื่อสุ่มจนครบ 100 ครั้งจึงเข้าสู่ส่วนสุดท้ายของโปรแกรม คือ การแสดงข้อมูลที่เก็บไว้ และตัวอย่างผลลัพธ์การทำงานเป็นดังภาพที่ 4
# ต้องการสุ่มค่า 100 ชุด โดยใช้ตัวเลข 0-20 และนับความถี่
import random
data = []
freq = []
for i in range(100):
item = random.getrandbits(5)%21
if (len(data) == 0):
data.append(item) # เก็บตัวเลขที่สุ่มได้
freq.append(1) # เก็บค่าความถี่
else:
if (item in data):
idx = data.index(item)
freq[idx] += 1
else:
data.append(item) # เก็บตัวเลขที่สุ่มได้
freq.append(1) # เก็บค่าความถี่
print("-------------------------")
print("\tData\tFreq.")
print("-------------------------")
sum = 0
for i in range(len(data)):
print("{}\t{}\t{}".format(i+1,data[i],freq[i]))
sum += freq[i]
print("-------------------------")
print("Freq. sum = {}".format(sum))
print("-------------------------")
ทดลองเขียนแบบที่ 2
จากตัวอย่างที่ 1 จะพบว่าการเรียงของข้อมูลในลิสต์นั้นดูไม่สวยงามจึงทำการปรับปรุงให้ตารางความถี่นั้นจัดเรียงลำดับให้ดูดีจากค่าน้อยไปหามาก โดยใช้หลักการสร้างตัวเลขสุ่มเก็บใน data จำนวน 100 ชุด หลังจากนั้นทำการจัดเรียงข้อมูลทำให้ข้อมูลสุ่มนั้นเรียงลำดับจากน้อยไปหามาก
เมื่อได้ข้อมูลที่เรียงจากน้อยไปหามากจึงดำเนินการสร้างตารางสุ่มเก็บใน freqTable ด้วยการพิจารณาค่าของ data ทีละตัว ถ้าเป็นค่าใหม่จะเพิ่ม data นั้นเข้า freqTable พร้อมกำหนดค่าความถี่เป็น 1 ด้วย [ค่า, 1] เข้าไปในลิสต์ แต่ถ้าพบว่าซ้ำกับตัวก่อนหน้าที่เก็บใน freqTable จะทำการเพิ่มค่าความถี่ของข้อมูลนั้น สุดท้ายทำการแสดงข้อมูลออกมาดังตัวอย่างในภาพที่ 5
# ต้องการสุ่มค่า 100 ชุด โดยใช้ตัวเลข 0-20 และนับความถี่
import random
data = []
freqTable = []
# random 100 items
for i in range(100):
data.append(random.getrandbits(5)%21)
# processing
data.sort()
freqTableIdx = 0
for i in range(100):
if (i == 0): # first time
freqTable.append([data[i],1])
else:
if (freqTable[freqTableIdx][0] == data[i]):
freqTable[freqTableIdx][1] += 1
else:
freqTable.append([data[i],1])
freqTableIdx += 1
print("-------------------------")
print("\tData\tFreq.")
print("-------------------------")
sum = 0
for i in range(len(freqTable)):
print("{}\t{}\t{}".format(i+1, freqTable[i][0],freqTable[i][1]))
sum += freqTable[i][1]
print("Freq. sum = {}".format(sum))
print("-------------------------")
ทดลองเขียนแบบที่ 3
เมื่อนำโปรแกรมการเขียนแบบที่ 2 มาเพิ่มส่วนของการแสดงผลกราฟบนจอ TFT ดังภาพที่ 1 จะได้โค้ดของโปรแกรมเป็นดังนี้
# ต้องการสุ่มค่า 100 ชุด โดยใช้ตัวเลข 0-20 และนับความถี่
import random
from st7735 import TFT
from sysfont import sysfont
from machine import SPI,Pin
import machine as mc
import time
import math
data = []
freqTable = []
# random 100 items
for i in range(100):
data.append(random.getrandbits(5)%21)
# processing
data.sort()
freqTableIdx = 0
for i in range(100):
if (i == 0): # first time
freqTable.append([data[i],1])
else:
if (freqTable[freqTableIdx][0] == data[i]):
freqTable[freqTableIdx][1] += 1
else:
freqTable.append([data[i],1])
freqTableIdx += 1
print("-------------------------")
print("\tData\tFreq.")
print("-------------------------")
sum = 0
for i in range(len(freqTable)):
print("{}\t{}\t{}".format(i+1, freqTable[i][0],freqTable[i][1]))
sum += freqTable[i][1]
print("Freq. sum = {}".format(sum))
print("-------------------------")
mc.freq(240000000)
spi = SPI(2, baudrate=27000000,
sck=Pin(14), mosi=Pin(12),
polarity=0, phase=0)
# dc, rst, cs
tft=TFT(spi,15,13,2)
tft.init_7735(tft.GREENTAB80x160)
tft.fill(tft.BLACK)
tft.text((10,0),"Freq. Graph",tft.YELLOW, sysfont)
for i in range(len(freqTable)):
tft.fillrect((20+i*6,78-freqTable[i][1]*3),(3,freqTable[i][1]*3),tft.WHITE)
ทดลองแบบที่ 4
ในแบบนี้เพิ่มเรื่องของการหาช่วงค่า min และ max เพื่อนำมาใช้คำนวณหาสัดส่วนของการปรับความสูงของกราฟ โดยใช้สมการดังนี้
scale = math.fabs(maxY-minY)
หลังจากนั้นคำนวณค่าความสูงของกราฟซึ่งเป็นค่าความถี่ด้วยสมการดังนี้
data = int(ความสูงของกราฟ*(ความถี่ของข้อมูล/scale))
นอกจากนี้ได้ปรับให้มีการสุ่ทค่าสีให้กับกราฟแต่ละแท่ง ซึ่งโค้ดโปรแกรมทั้งหมดเมื่อรวมกันเป็นดังนี้ และตัวอย่างของผลลัพธ์เป็นดังภาพที่ 6
import random
from st7735 import TFT
from sysfont import sysfont
from machine import SPI,Pin
import machine as mc
import time
import math
maxData = const(10000)
data = []
freqTable = []
graphHeight = const(30)
minY = maxData
maxY = 1
mc.freq(240000000)
spi = SPI(2, baudrate=30000000,
sck=Pin(14), mosi=Pin(12),
polarity=0, phase=0)
# dc, rst, cs
tft=TFT(spi,15,13,2)
tft.init_7735(tft.GREENTAB80x160)
tft.fill(tft.BLACK)
tft.text((10,36),"(C)2020-21",tft.YELLOW,sysfont)
tft.text((92,36),"JarutEx",tft.WHITE,sysfont)
tft.text((93,36),"JarutEx",tft.WHITE,sysfont)
time.sleep_ms(1000)
tft.fill(tft.BLACK)
tft.text((10,0), "Freq. Graph",tft.YELLOW,sysfont)
# random 100 items
for i in range(maxData):
data.append(random.getrandbits(5)%21)
# processing
data.sort()
freqTableIdx = 0
for i in range(maxData):
if (i == 0): # first time
freqTable.append([data[i],1])
else:
if (freqTable[freqTableIdx][0] == data[i]):
freqTable[freqTableIdx][1] += 1
else:
freqTable.append([data[i],1])
freqTableIdx += 1
print("-------------------------")
print("\tData\tFreq.")
print("-------------------------")
sum = 0
for i in range(len(freqTable)):
print("{}\t{}\t{}".format(i+1, freqTable[i][0],freqTable[i][1]))
if minY > freqTable[i][1]:
minY = freqTable[i][1]
if maxY < freqTable[i][1]:
maxY = freqTable[i][1]
sum += freqTable[i][1]
print("Freq. sum = {}".format(sum))
print("-------------------------")
tft.text((10,10),"min={} max={}".format(minY,maxY),tft.WHITE,sysfont)
scale = math.fabs(maxY-minY) # หาความยาวของช่วงค่า
#print("Scale = {} [{}-{}]".format(scale,minY,maxY))
for i in range(len(freqTable)):
data = int(graphHeight*(freqTable[i][1]/scale))
gColor = tft.color(random.getrandbits(8),random.getrandbits(8),random.getrandbits(8))
tft.fillrect((20+i*6,78-data),(3,data),gColor)
สรุป
การแก้ปัญหาการสร้างตารางความถี่ของข้อมูลด้วยโครงสร้างข้อมูลแบบลิสค์หรือใช้คลาสลิสต์นั้นสามารถเขียนได้หลากหลายวิธีขึ้นอยู่กับวิธีการคิดและทักษะของผู้เขียนโปรแกรม ยิ่งสามารถเขียนได้หลากหลายวิธีเท่าไรนั่นหมายความว่าผู้เขียนโปรแกรมมีทักษะมากขึ้นเท่านั้น แต่สิ่งหนึ่งที่จะไม่ได้จากการนับความถี่ คือ ข้อมูลยี้เกิดขึ้นเป็นลำดับที่เท่าไร ซึ่งคุณลักษณะของเวลาที่ได้ข้อมูลอาจมีความจำเป็นสำหรับการใช้านข้อมูลในบางกรณี ดังนั้น การเก็บข้อมูลตามช่วงเวลาจึงมีความจำเป็น และการสร้างตารางความถี่ของข้อมูลควรมาจากการนำข้อมูลดิบเหล่านั้นมาประมวลผล สุดท้ายนี้ขอให้สนุกกับการเขียนโปรแกรมครับ
ท่านใดต้องการพูดคุยสามารถคอมเมนท์ได้เลยครับ
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-08-21, 2021-11-25