เทคนิคที่ 005 · PYTHON / JAVASCRIPT

กราฟแท่งซ้อน 100%100% Stacked Bar

เทียบสัดส่วนข้ามกลุ่มเมื่อยอดรวมไม่ใช่ประเด็น

2
ต่อยอดโดยตรงควรรู้ก่อน · อ่านกราฟแท่งซ้อนเป็น
ลองเล่น

01ภาพรวมของกราฟนี้

100% stacked bar ยืดทุกแท่งให้สูงเท่ากัน แล้วแบ่งเป็นสัดส่วนของแต่ละหมวดย่อย ผลคือเทียบ "องค์ประกอบ" ข้ามกลุ่มได้ง่ายมาก เพราะทุกแท่งมีมาตรวัดเดียวกัน แต่ต้องยอมรับว่าข้อมูลขนาดของกลุ่มหายไปจากภาพทั้งหมด

เปรียบเทียบให้เห็นภาพ

เหมือนถ่ายรูปพายของแต่ละบ้านแล้วย่อ-ขยายให้ทุกถาดใหญ่เท่ากัน เราจึงเทียบได้ว่าบ้านไหนแบ่งชิ้นให้ใครมากกว่ากัน แต่จะไม่รู้เลยว่าพายของบ้านไหนใหญ่กว่ากันจริง ๆ

ใช้ที่ไหนบ้าง — เทียบสัดส่วนช่องทางการขายระหว่างภูมิภาคที่มีขนาดต่างกันมาก เทียบส่วนผสมของประชากรระหว่างจังหวัด หรือดูว่าสัดส่วนผู้ใช้แต่ละแพลตฟอร์มเปลี่ยนไปอย่างไรตามปี

สร้างอย่างไร

  1. ขั้นที่ 1คำนวณสัดส่วนของแต่ละหมวดย่อยภายในกลุ่ม
  2. ขั้นที่ 2ยืดทุกแท่งให้เป็น 100%
  3. ขั้นที่ 3คงลำดับการซ้อนให้เหมือนกัน
  4. ขั้นที่ 4เขียนขนาดของกลุ่มกำกับไว้เพราะภาพซ่อนไว้

ตัวอย่างคลาสสิก · องค์ประกอบพลังงานของแต่ละประเทศ

ประเทศใหญ่กับประเทศเล็กมีการใช้พลังงานรวมต่างกันหลายร้อยเท่า การเทียบ "สัดส่วนพลังงานหมุนเวียน" จึงต้องยืดทุกแท่งให้เท่ากันก่อน มิฉะนั้นประเทศเล็กจะกลายเป็นเส้นบาง ๆ ที่อ่านอะไรไม่ได้เลย งานของ Our World in Data จึงคู่กราฟนี้กับตัวเลขปริมาณจริงเสมอ

Our World in Data · Energy mix

02อธิบายภาษาคนเข้าใจง่าย ๆ

ปัญหาของแท่งซ้อนธรรมดาคือ ถ้ากลุ่มหนึ่งใหญ่กว่าอีกกลุ่มมาก แท่งของกลุ่มใหญ่จะสูงลิ่วจนเราเทียบองค์ประกอบภายในไม่ได้เลย เช่น เทียบสัดส่วนช่องทางขายของกรุงเทพกับจังหวัดเล็ก ๆ

ทางแก้คือเลิกสนใจขนาด แล้วเปลี่ยนทุกแท่งให้สูงเท่ากันที่ 100% แต่ละช่วงสีจึงกลายเป็นเปอร์เซ็นต์ของกลุ่มตัวเอง ทีนี้เราเทียบได้ตรง ๆ ว่าสัดส่วนของช่องทางแอปในกรุงเทพสูงกว่าหรือต่ำกว่าจังหวัดอื่น

ราคาที่ต้องจ่ายคือข้อมูลขนาดหายไปหมด จังหวัดที่มียอดขาย 10 ล้านกับจังหวัดที่มียอด 10,000 บาท จะมีแท่งสูงเท่ากันเป๊ะ ซึ่งอาจนำไปสู่ข้อสรุปที่ผิดอย่างร้ายแรงถ้าผู้อ่านไม่รู้ข้อนี้

เพราะเหตุนี้ กราฟชนิดนี้ต้องมีการเขียนขนาดของแต่ละกลุ่มกำกับไว้เสมอ ไม่ว่าจะเป็นตัวเลขข้างชื่อกลุ่ม หรือความกว้างของแท่งที่แปรตามขนาด ซึ่งจะกลายเป็น mosaic plot

ข้อดีที่หลายคนมองข้ามคือ ชั้นล่างสุดและชั้นบนสุดอ่านได้แม่นทั้งคู่ เพราะทั้งคู่ติดขอบที่ตรงกันทุกแท่ง ดังนั้นควรวางหมวดที่สำคัญที่สุดสองหมวดไว้ล่างสุดและบนสุด ส่วนหมวดที่ไม่สำคัญไว้ตรงกลาง

03ข้อมูลที่ต้องใช้

รูปทรงของข้อมูลตารางแบบยาว (หนึ่งแถวหนึ่งสังเกต)
จำนวนมิติที่เข้ารหัสได้3 มิติ
จำนวนแถวที่เหมาะ3–10 กลุ่ม × 3–5 หมวดขั้นต่ำ 4 แถว
เส้นฐานศูนย์ต้องเริ่มที่ศูนย์สัดส่วนต้องเริ่มที่ 0% และจบที่ 100% ทุกแท่ง

คอลัมน์ที่ต้องเตรียม

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
categoryกลุ่มที่จะเทียบสัดส่วนหมวดหมู่x2–12 กลุ่มขนาดของกลุ่มจะหายไปจากภาพ ต้องเขียนกำกับ
groupหมวดย่อยหมวดหมู่color2–5 หมวดวางหมวดสำคัญไว้ล่างสุดและบนสุด เพราะสองตำแหน่งนี้อ่านแม่นที่สุด
valueปริมาณดิบตัวเลขต่อเนื่องyบวกเท่านั้นใส่ค่าดิบ ไม่ต้องคำนวณเปอร์เซ็นต์มาก่อน ให้กราฟคำนวณให้

ต้องเตรียมข้อมูลอย่างไรก่อนวาด

  1. ตรวจว่าทุกกลุ่มมีข้อมูลครบทุกหมวดย่อย
  2. บันทึกยอดรวมของแต่ละกลุ่มไว้เขียนกำกับ
  3. วางหมวดที่สำคัญที่สุดไว้ล่างสุด

ค่าที่หายไป — ถ้าบางกลุ่มเก็บข้อมูลไม่ครบ สัดส่วนที่คำนวณได้จะผิดทั้งกลุ่ม ต้องตัดกลุ่มนั้นออกและระบุ ไม่ใช่เติมศูนย์

สเกลของแกน — แกนเป็น 0–100% เสมอ ไม่มีทางเลือกอื่น

กราฟจะเริ่มพังเมื่อไร

เกิน 6 หมวดย่อย ช่วงกลางจะบางจนอ่านไม่ออก และเกิน 12 กลุ่มแท่งจะบางเกินไป

หน้าตาไฟล์ที่ต้องเตรียม

category,group,value

04อ่านแล้วเห็นอะไร

สิ่งที่เห็นได้จากภาพ

  • กลุ่มไหนมีสัดส่วนของหมวดหนึ่งสูงผิดจากกลุ่มอื่น
  • องค์ประกอบโดยรวมเหมือนกันทุกกลุ่มหรือต่างกันชัดเจน
  • แนวโน้มของสัดส่วนเมื่อเรียงกลุ่มตามลำดับที่มีความหมาย เช่น ปี
  • หมวดที่ครองสัดส่วนใหญ่ในทุกกลุ่ม

สิ่งที่กราฟนี้ตอบไม่ได้

  • ★ ไม่บอกขนาดของกลุ่มเลย — สองแท่งที่หน้าตาเหมือนกันอาจมาจากข้อมูล 10 แถวกับ 10,000 แถว
  • ไม่บอกว่าสัดส่วนที่เปลี่ยนมาจากตัวเศษเพิ่มหรือตัวส่วนลด
  • เทียบหมวดที่อยู่ตรงกลางข้ามกลุ่มได้ยาก

อ่านตามลำดับนี้

  1. ดูชั้นล่างสุดก่อน เพราะเริ่มที่ 0% ทุกแท่งจึงเทียบได้แม่น
  2. ดูชั้นบนสุดต่อ เพราะจบที่ 100% ทุกแท่งเช่นกัน
  3. ตรวจขนาดของกลุ่มจากตัวเลขที่เขียนกำกับ
  4. ระวังไม่สรุปเรื่องปริมาณจากภาพนี้

คำถามที่ตอบได้

  • กลุ่มไหนพึ่งพาช่องทางนี้มากที่สุดเมื่อคิดเป็นสัดส่วน
  • องค์ประกอบเปลี่ยนไปอย่างไรเมื่อเวลาผ่านไป
  • มีกลุ่มไหนที่โครงสร้างต่างจากกลุ่มอื่นอย่างชัดเจน

สัญญาณว่ามีอะไรผิด

  • ไม่มีตัวเลขขนาดของกลุ่มกำกับไว้เลย
  • กลุ่มที่มีข้อมูลน้อยมากถูกวางเคียงกับกลุ่มใหญ่โดยไม่มีคำเตือน
  • ช่วงกลางบางจนมองไม่เห็นแต่ยังใส่ไว้

05เหมาะกับการนำเสนอแบบไหน

ผู้ชมที่เหมาะผู้อ่านที่เข้าใจว่ากำลังดูสัดส่วน ไม่ใช่ปริมาณ
เวลาที่ผู้ชมต้องใช้ไม่กี่วินาที
ขนาดเล็กสุดที่ยังอ่านได้400×280 px
รายงานเชิงวิเคราะห์
สไลด์ผู้บริหาร
สิ่งพิมพ์
dashboard ที่อัปเดตตลอด
สื่อสารกับคนทั่วไปผู้อ่านทั่วไปมักเข้าใจผิดว่าแท่งสูงเท่ากันแปลว่าขนาดเท่ากัน
มือถือช่วงกลางบางเกินกว่าจะกดหรืออ่านบนจอเล็ก

ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย

  1. เขียนยอดรวม n ของแต่ละกลุ่มไว้ข้างชื่อกลุ่มเสมอ — ข้อนี้เป็นข้อบังคับไม่ใช่ทางเลือก
  2. ติดเปอร์เซ็นต์ในช่วงที่กว้างพอ
  3. ถ้าเรียงกลุ่มตามเวลา ให้ลากเส้นเชื่อมขอบของชั้นเดียวกันเพื่อให้เห็นแนวโน้ม

รูปแบบชื่อกราฟที่ควรใช้

สัดส่วนของ <หมวด> ใน <กลุ่ม> สูงถึง <เปอร์เซ็นต์> เทียบกับ <เปอร์เซ็นต์> ใน <กลุ่มอื่น>

06เอาไปใช้ต่อ

โค้ดทุกชิ้นอ่านไฟล์ percent-stacked-bar-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง

# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด percent-stacked-bar-sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์
import matplotlib
matplotlib.use("Agg")   # รันแบบไม่มีหน้าจอ ถ้ารันในเครื่องตัวเองลบบรรทัดนี้ได้
import matplotlib.pyplot as plt
import pandas as pd

C1, C2, C3 = "#2a78d6", "#eb6834", "#1baf7a"   # palette ของคลัง เปลี่ยนเป็นสีองค์กรได้
INK, SURFACE = "#0b0b0b", "#fcfcfb"

df = pd.read_csv("percent-stacked-bar-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
VIEW = "percent"   # ← ค่าจาก Playground

fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)

pivot = df.pivot_table(index="category", columns="group", values="value", aggfunc="sum").fillna(0)
totals = pivot.sum(axis=1)
share = pivot.div(totals, axis=0) * 100
bottom = None
for i, col in enumerate(share.columns):
    ax.bar(share.index, share[col], bottom=bottom, label=col,
           color=[C1, C2, C3][i % 3], edgecolor=SURFACE, linewidth=1.6)
    bottom = share[col] if bottom is None else bottom + share[col]
# เขียนขนาดของกลุ่มกำกับเสมอ เพราะภาพซ่อนข้อมูลนี้ไว้
ax.set_xticks(range(len(totals)))
ax.set_xticklabels([f"{k}\n(n={v:,.0f})" for k, v in totals.items()], fontsize=9)
ax.set_ylim(0, 100); ax.set_ylabel("สัดส่วน (%)"); ax.legend(frameon=False, ncols=3)

ax.spines[["top", "right"]].set_visible(False)   # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("percent-stacked-bar.png", dpi=160)
print("เขียน percent-stacked-bar.png แล้ว ·", len(df), "แถว")

ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*" · ทดสอบกับเวอร์ชัน 3.10

07ลองเล่นเพื่อเข้าใจ

ข้อมูลตัวอย่าง · 18 แถว · seed 7● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
ทุกแท่งสูงเท่ากันที่ 100%
หน้าร้านเว็บไซต์แอป
0%20%40%60%80%100%เหนืออีสานกลางตะวันออกตะวันตกใต้เหนือ · หน้าร้าน · 91.0 (32.7%)เหนือ · เว็บไซต์ · 93.0 (33.5%)เหนือ · แอป · 94.0 (33.8%)อีสาน · หน้าร้าน · 120 (33.5%)อีสาน · เว็บไซต์ · 84.0 (23.5%)อีสาน · แอป · 154 (43.0%)กลาง · หน้าร้าน · 88.0 (30.2%)กลาง · เว็บไซต์ · 73.0 (25.1%)กลาง · แอป · 130 (44.7%)ตะวันออก · หน้าร้าน · 82.0 (31.2%)ตะวันออก · เว็บไซต์ · 47.0 (17.9%)ตะวันออก · แอป · 134 (51.0%)ตะวันตก · หน้าร้าน · 92.0 (30.9%)ตะวันตก · เว็บไซต์ · 70.0 (23.5%)ตะวันตก · แอป · 136 (45.6%)ใต้ · หน้าร้าน · 86.0 (27.8%)ใต้ · เว็บไซต์ · 94.0 (30.4%)ใต้ · แอป · 129 (41.7%)
จำนวนหมวด6
จำนวนกลุ่มย่อย3
สัดส่วนสูงสุดของกลุ่มเดียว51.0%
ดูข้อมูลเป็นตาราง (6 แถว)
กราฟแท่งซ้อน 100% 6 หมวด 3 กลุ่มย่อย
หมวดหน้าร้านเว็บไซต์แอปรวม
เหนือ91.093.094.0278
อีสาน12084.0154358
กลาง88.073.0130291
ตะวันออก82.047.0134263
ตะวันตก92.070.0136298
ใต้86.094.0129309

ลองแล้วจะเห็นอะไร

  • มุมมอง: สังเกตว่าทุกแท่งสูงเท่ากันหมด ขนาดจริงของแต่ละภูมิภาคหายไปจากภาพโดยสิ้นเชิง — เปิดตารางเพื่อดูว่าซ่อนอะไรไว้

ข้อควรระวัง

  1. ★ ไม่เขียนขนาดของแต่ละกลุ่มกำกับ ทำให้คนอ่านคิดว่าทุกกลุ่มใหญ่เท่ากัน
  2. ใช้กับกลุ่มที่มีข้อมูลน้อยมาก เช่น 8 คน ซึ่งสัดส่วนจะแกว่งแรงจนไม่มีความหมาย
  3. สรุปว่าปริมาณเพิ่มขึ้นทั้งที่กราฟบอกได้แค่ว่าสัดส่วนเพิ่มขึ้น ซึ่งอาจเกิดจากตัวอื่นลดลงก็ได้
  4. วางหมวดที่สำคัญที่สุดไว้ตรงกลาง ซึ่งเป็นตำแหน่งที่อ่านยากที่สุด
  5. ใส่หมวดย่อยเกิน 5 หมวดจนช่วงกลางบางเกินกว่าจะเห็น

เมื่อไหร่ไม่ควรใช้กราฟนี้

  • ขนาดของกลุ่มเป็นประเด็นสำคัญใช้แทน: stacked-bar หรือ mosaic-plot ที่ความกว้างบอกขนาด
  • มีเพียงสองหมวดย่อยใช้แทน: bar-chart ของสัดส่วนหมวดเดียว ซึ่งอ่านง่ายกว่ามาก
  • เป็นสเกลความเห็นที่มีลำดับ เช่น พอใจมาก–ไม่พอใจใช้แทน: sentiment-diverging ที่จัดกึ่งกลางที่ความเห็นกลาง

กราฟที่เกี่ยวข้อง

เทคนิคถัดไป · กราฟแท่งสองทาง