← ทำไมตาถึงอ่านกราฟนี้ได้เร็วกว่า
LEVEL 0 · ปูพื้นจากศูนย์
ช่องทางเข้ารหัสมีกี่แบบ
ถ้าขั้นเข้ารหัสคือขั้นเดียวที่คุณคุมได้ คำถามต่อไปคือ คุณมีอะไรให้เลือกใช้บ้าง
คำตอบคือน้อยกว่าที่คิดมาก — คุณสมบัติที่ตาถอดค่าเชิงปริมาณกลับมาได้จริง ๆ มีอยู่ราวเจ็ดอย่างเท่านั้น และแต่ละอย่างมีราคาต่างกัน
4.1 เจ็ดช่องทาง
แต่ละช่องทางข้างล่างตอบสามคำถามเหมือนกันหมด: (ก) มันคืออะไร (ข) ตาถอดได้แม่นแค่ไหน (ค) เหมาะกับข้อมูลชนิดไหน
| ช่องทาง | คืออะไร | ความแม่น | เหมาะกับ |
|---|---|---|---|
| ตำแหน่งบนแกนร่วม | จุดที่วางบนแกนเดียวกัน เทียบกันตรง ๆ | สูงสุด | ค่าเชิงปริมาณทุกชนิด |
| ความยาว | ความสูงหรือความยาวของแท่งจากฐานเดียวกัน | สูง | ค่าเชิงปริมาณ |
| ทิศทาง / ความชัน | ความเอียงของเส้น | กลาง | อัตราการเปลี่ยนแปลง |
| มุม | ขนาดมุมของชิ้นวงกลม | ต่ำ | สัดส่วนหยาบ ๆ เท่านั้น |
| พื้นที่ | ขนาดของวงกลมหรือสี่เหลี่ยม | ต่ำ | ค่าที่ต่างกันหลายเท่า |
| ความเข้ม | อ่อน-เข้มของสีเดียวกัน | ต่ำ | ค่าเชิงลำดับ |
| สี (hue) | สีที่ต่างชนิดกัน เช่น ฟ้ากับส้ม | ใช้บอกปริมาณไม่ได้ | การแบ่งกลุ่ม |
สองแถวสุดท้ายเป็นที่มาของความผิดพลาดที่พบบ่อยที่สุดในงานจริง — การเอาสีมาแบกตัวเลข ซึ่งเราจะเจาะลงไปทั้งบทที่ 8
4.2 ค่าชุดเดียวกัน ย้ายช่องทางแล้วเกิดอะไรขึ้น
คำว่า "แม่นกว่า" จะยังลอยอยู่จนกว่าจะเห็นตัวเลข ลองดูข้อมูลชุดเดียวกันบนสองช่องทาง
encode.py ไม่ได้กะเอา)หน่วย จ มีค่า 19 หน่วย ข มีค่า 18 ต่างกัน 1 จุด เท่ากันทั้งสองภาพ แต่
- บนแท่ง ความต่างนั้นกลายเป็นระยะที่กิน 4.00% ของช่วงทั้งหมดที่แท่งใช้ได้
- บนวงกลม ความต่างเดียวกันกลายเป็นมุม 3.6 องศา ซึ่งกินแค่ 1.00% ของวงกลม
แท่งขยายความต่างเดียวกันให้ใหญ่ขึ้น 4 เท่าก่อนส่งถึงตา โดยที่ตัวเลขต้นทางไม่ขยับเลยสักตัว นี่คือความหมายของคำว่า "ย้ายช่องทาง"
สังเกตว่าทั้งสองภาพเรียงลำดับเหมือนกันคือ ก ข ค ง จ ตามตารางต้นทาง จึงเทียบกันได้ตรง ๆ · ส่วนเรื่องที่ว่าของสองชิ้นอยู่ติดกันหรืออยู่คนละมุมมีผลต่อการเปรียบเทียบแค่ไหน เป็นคนละปัจจัยกับความแม่นของช่องทาง และรอไว้ที่บทที่ 11
และนี่คือคำตอบแรกของปริศนาข้อที่หนึ่งที่ปลูกไว้ในบทที่ 1 — กราฟวงกลมไม่ได้โดนด่าเพราะมันน่าเกลียด มันโดนด่าเพราะมันบีบความต่างให้เล็กลงสี่เท่าก่อนที่ตาจะได้เห็น
4.3 กับดักของพื้นที่
การเข้ารหัสด้วยพื้นที่มีกับดักที่แยกออกมาเป็นเรื่องของตัวเอง เพราะมันผิดได้ทั้งที่ตั้งใจดี
เวลาเราบอกว่า "ทำวงกลมให้ใหญ่ตามค่า" คนส่วนใหญ่ไปปรับ รัศมี ตามค่าตรง ๆ ซึ่งฟังดูสมเหตุสมผล แต่ตาไม่ได้อ่านรัศมี ตาอ่าน พื้นที่ และพื้นที่แปรตามรัศมียกกำลังสอง
ผลคือค่าเล็ก ๆ ถูกกดให้ดูเล็กกว่าความจริงมาก หน่วย ง ที่มีค่าจริง 16 ถูกตาอ่านเป็น 10.24 คือต่ำกว่าความเป็นจริง 36%
💡 วิธีแก้คือปรับรัศมีด้วยรากที่สองของค่า r = sqrt(v / v_max) * r_max ไม่ใช่ r = v / v_max * r_max · เครื่องมือทำกราฟหลายตัวยังตั้งค่าเริ่มต้นเป็นแบบหลังอยู่ ให้ตรวจก่อนใช้เสมอ
4.4 สคริปต์ที่คำนวณทุกตัวเลขในบทนี้
ไม่มีตัวเลขไหนในสองภาพข้างบนที่กะด้วยตา ทั้งหมดมาจากสคริปต์นี้ — เล่มนี้เรียกมันว่า encode.py และ caption ของ FIG 4.1 อ้างถึงไฟล์นี้ · รันได้ด้วย Python มาตรฐาน ไม่ต้องลงอะไรเพิ่ม
"""คำนวณเรขาคณิตของกราฟชุดเดียวกันที่เข้ารหัสหลายแบบ — Python มาตรฐานล้วน"""
import math
# ชุดข้อมูลสมมติ: สัดส่วนการใช้จ่ายของ 5 หน่วยงาน (รวม 100)
DATA = [("ก", 22), ("ข", 18), ("ค", 25), ("ง", 16), ("จ", 19)]
vals = [v for _, v in DATA]
total = sum(vals)
order = sorted(DATA, key=lambda kv: -kv[1])
gap_val = min(order[i][1] - order[i + 1][1] for i in range(len(order) - 1))
# --- 1. ความยาว (แท่ง) : ค่า -> พิกเซล เชิงเส้น ---
BAR_MAX_PX = 110
print("=== เข้ารหัสด้วยความยาว (แท่ง) ===")
for k, v in DATA:
print(f" {k}: {v:>3} -> ยาว {v / max(vals) * BAR_MAX_PX:7.2f} px")
gap_len = gap_val / max(vals) * BAR_MAX_PX
print(f" คู่ที่สูสีที่สุด (ต่าง {gap_val} จุด) ห่างกัน {gap_len:.2f} px "
f"= {gap_len / BAR_MAX_PX * 100:.2f}% ของช่วงที่แท่งใช้ได้")
# --- 2. มุม (วงกลม) : ค่า -> องศา ---
print("\n=== เข้ารหัสด้วยมุม (วงกลม) ===")
start, R = -90.0, 90.0 # เริ่มที่ 12 นาฬิกา
for k, v in DATA:
sweep = v / total * 360.0
end = start + sweep
x1, y1 = R * math.cos(math.radians(start)), R * math.sin(math.radians(start))
x2, y2 = R * math.cos(math.radians(end)), R * math.sin(math.radians(end))
print(f" {k}: {v:>3} -> {sweep:6.2f} องศา | "
f"M 0 0 L {x1:7.2f} {y1:7.2f} A {R} {R} 0 0 1 {x2:7.2f} {y2:7.2f} Z")
start = end
gap_deg = gap_val / total * 360.0
print(f" คู่ที่สูสีที่สุด (ต่าง {gap_val} จุด) ห่างกัน {gap_deg:.2f} องศา "
f"= {gap_deg / 360 * 100:.2f}% ของช่วงที่วงกลมใช้ได้")
# --- 3. พื้นที่ (ฟอง) : ถูก = รากที่สอง, ผิด = เชิงเส้น ---
print("\n=== เข้ารหัสด้วยพื้นที่ (ฟอง) ===")
R_MAX = 28.0
for k, v in DATA:
r_ok = math.sqrt(v / max(vals)) * R_MAX # พื้นที่แปรตามค่า (ถูก)
r_bad = v / max(vals) * R_MAX # รัศมีแปรตามค่า (ผิด)
area_bad = (r_bad / R_MAX) ** 2 * max(vals) # ค่าที่ตา "อ่านได้" จากรูปที่ผิด
print(f" {k}: {v:>3} -> r ถูก {r_ok:5.2f} | r ผิด {r_bad:5.2f} "
f"-> ตาอ่านได้ {area_bad:5.2f} (คลาดเคลื่อน {area_bad - v:+.2f})")
pct_bar, pct_pie = gap_len / BAR_MAX_PX * 100, gap_deg / 360 * 100
print(f"\n= แท่งขยายความต่างเดียวกันให้ใหญ่ขึ้น {pct_bar / pct_pie:.1f} เท่า")
# --- 4. คู่ที่ต่างกันมาก เพื่อให้เห็นความเพี้ยนของพื้นที่ชัด ๆ ---
print("\n=== คู่เทียบ 25 กับ 100 (ต่างกัน 4 เท่า) ===")
PAIR, R_BIG = [25, 100], 40.0
for v in PAIR:
r_ok = math.sqrt(v / max(PAIR)) * R_BIG
r_bad = v / max(PAIR) * R_BIG
read = (r_bad / R_BIG) ** 2 * max(PAIR)
print(f" ค่า {v:>4} -> r ถูก {r_ok:5.2f} | r ผิด {r_bad:5.2f} "
f"-> ตาอ่านได้ {read:6.2f} (เหลือ {read / v * 100:.0f}% ของค่าจริง)")ผลรันจริง
=== เข้ารหัสด้วยความยาว (แท่ง) ===
ก: 22 -> ยาว 96.80 px
ข: 18 -> ยาว 79.20 px
ค: 25 -> ยาว 110.00 px
ง: 16 -> ยาว 70.40 px
จ: 19 -> ยาว 83.60 px
คู่ที่สูสีที่สุด (ต่าง 1 จุด) ห่างกัน 4.40 px = 4.00% ของช่วงที่แท่งใช้ได้
=== เข้ารหัสด้วยมุม (วงกลม) ===
ก: 22 -> 79.20 องศา | M 0 0 L 0.00 -90.00 A 90.0 90.0 0 0 1 88.41 -16.86 Z
ข: 18 -> 64.80 องศา | M 0 0 L 88.41 -16.86 A 90.0 90.0 0 0 1 52.90 72.81 Z
ค: 25 -> 90.00 องศา | M 0 0 L 52.90 72.81 A 90.0 90.0 0 0 1 -72.81 52.90 Z
ง: 16 -> 57.60 องศา | M 0 0 L -72.81 52.90 A 90.0 90.0 0 0 1 -83.68 -33.13 Z
จ: 19 -> 68.40 องศา | M 0 0 L -83.68 -33.13 A 90.0 90.0 0 0 1 -0.00 -90.00 Z
คู่ที่สูสีที่สุด (ต่าง 1 จุด) ห่างกัน 3.60 องศา = 1.00% ของช่วงที่วงกลมใช้ได้
=== เข้ารหัสด้วยพื้นที่ (ฟอง) ===
ก: 22 -> r ถูก 26.27 | r ผิด 24.64 -> ตาอ่านได้ 19.36 (คลาดเคลื่อน -2.64)
ข: 18 -> r ถูก 23.76 | r ผิด 20.16 -> ตาอ่านได้ 12.96 (คลาดเคลื่อน -5.04)
ค: 25 -> r ถูก 28.00 | r ผิด 28.00 -> ตาอ่านได้ 25.00 (คลาดเคลื่อน +0.00)
ง: 16 -> r ถูก 22.40 | r ผิด 17.92 -> ตาอ่านได้ 10.24 (คลาดเคลื่อน -5.76)
จ: 19 -> r ถูก 24.41 | r ผิด 21.28 -> ตาอ่านได้ 14.44 (คลาดเคลื่อน -4.56)
= แท่งขยายความต่างเดียวกันให้ใหญ่ขึ้น 4.0 เท่า
=== คู่เทียบ 25 กับ 100 (ต่างกัน 4 เท่า) ===
ค่า 25 -> r ถูก 20.00 | r ผิด 10.00 -> ตาอ่านได้ 6.25 (เหลือ 25% ของค่าจริง)
ค่า 100 -> r ถูก 40.00 | r ผิด 40.00 -> ตาอ่านได้ 100.00 (เหลือ 100% ของค่าจริง)ตัวเลข 4.00% กับ 1.00% ไม่ได้ขึ้นกับขนาดภาพที่เราเลือก ลองเปลี่ยน BAR_MAX_PX เป็นค่าอะไรก็ได้แล้วรันใหม่ เปอร์เซ็นต์จะเท่าเดิม เพราะมันเป็นสัดส่วนของช่วงที่ช่องทางนั้นมีให้ใช้ ไม่ใช่ของจอ
4.5 ยังตอบไม่ได้ว่าแม่นกว่า "เท่าไหร่"
สิ่งที่บทนี้ทำได้คือบอกว่าช่องทางไหนบีบหรือขยายความต่าง ซึ่งเป็นเรขาคณิตล้วน ๆ คำนวณได้จากหน้ากระดาษ
แต่คำถามที่แท้จริงคือ คนจริงถอดค่าออกมาผิดพลาดเฉลี่ยเท่าไหร่ต่อช่องทาง ซึ่งเรขาคณิตตอบไม่ได้ ต้องวัดกับมนุษย์เท่านั้น และมีคนวัดไว้แล้วตั้งแต่ปี 1984
เปลี่ยนช่องทาง เท่ากับเปลี่ยนความแม่นที่คนอ่านจะได้ โดยที่ตัวเลขต้นทางไม่ขยับเลยสักตัว