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

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

เห็นยอดรวมและส่วนประกอบในแท่งเดียว

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

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

Stacked bar chart วางหมวดย่อยซ้อนกันในแท่งเดียว ความสูงรวมคือยอดรวมของกลุ่ม ส่วนแต่ละช่วงสีคือส่วนประกอบ จุดแข็งคือตอบสองคำถามในภาพเดียว แต่แลกมาด้วยข้อจำกัดว่าเฉพาะชั้นล่างสุดเท่านั้นที่เริ่มจากเส้นฐานเดียวกัน จึงเทียบได้แม่นแค่ชั้นเดียว

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

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

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

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

  1. ขั้นที่ 1เลือกตัวแปรกลุ่มและตัวแปรหมวดย่อย
  2. ขั้นที่ 2กำหนดลำดับการซ้อนให้คงที่
  3. ขั้นที่ 3วางหมวดที่สำคัญที่สุดไว้ชั้นล่างสุด
  4. ขั้นที่ 4ใส่ legend และเว้นช่องว่างบาง ๆ ระหว่างชั้น

ตัวอย่างคลาสสิก · งบประมาณแผ่นดินที่แยกตามกระทรวง

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

Financial Times · Visual Vocabulary

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

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

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

ผลที่ตามมาคือ เราควรเอาหมวดที่สำคัญที่สุดหรือที่อยากให้คนเทียบมากที่สุดไว้ชั้นล่างสุดเสมอ นี่เป็นการตัดสินใจเชิงบรรณาธิการที่กระทบสิ่งที่คนอ่านเห็น ไม่ใช่แค่เรื่องความสวยงาม

จำนวนชั้นควรอยู่ที่ 3-5 ชั้น ถ้ามากกว่านี้ชั้นบาง ๆ จะบีบจนมองไม่เห็นและสีจะเยอะเกินกว่าที่คนตาบอดสีจะแยกได้ ทางแก้คือรวมหมวดเล็กเป็น "อื่น ๆ" ไว้ชั้นบนสุด

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

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

รูปทรงของข้อมูลตารางแบบยาว (หนึ่งแถวหนึ่งสังเกต)
จำนวนมิติที่เข้ารหัสได้3 มิติ
จำนวนแถวที่เหมาะ4–8 กลุ่ม × 3–4 ชั้นขั้นต่ำ 4 แถว
เส้นฐานศูนย์ต้องเริ่มที่ศูนย์ความสูงรวมต้องแปรตามยอดรวมจริง การตัดแกนทำให้ทั้งยอดรวมและสัดส่วนของชั้นล่างผิดพร้อมกัน

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
categoryกลุ่มหลักหมวดหมู่x2–10 กลุ่มแต่ละกลุ่มได้หนึ่งแท่ง
groupหมวดย่อยที่ซ้อนกันหมวดหมู่color2–5 ชั้นเกิน 5 ชั้นจะบางจนอ่านไม่ออก ให้รวมเป็น "อื่น ๆ"
valueปริมาณตัวเลขต่อเนื่องyบวกเท่านั้นค่าติดลบซ้อนกันไม่ได้ ถ้ามีต้องใช้ diverging-bar แทน

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

  1. ตรวจว่าค่าทุกตัวเป็นบวก
  2. กำหนดลำดับการซ้อนให้คงที่ทุกแท่ง โดยวางหมวดสำคัญไว้ล่างสุด
  3. รวมหมวดที่คิดเป็นน้อยกว่า 3% ของยอดรวมเป็น "อื่น ๆ"

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

สเกลของแกน — linear เริ่มที่ศูนย์เท่านั้น การซ้อนอาศัยสมบัติว่าผลรวมของความยาวเท่ากับความยาวรวม ซึ่ง log ทำลายทิ้ง

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

เกิน 6 ชั้น ชั้นบาง ๆ จะหายไปในภาพ — รวมเป็น "อื่น ๆ" หรือเปลี่ยนไปใช้ small-multiples

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

category,group,value

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

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

  • กลุ่มไหนมียอดรวมสูงสุด
  • หมวดย่อยไหนเป็นตัวขับยอดรวมของแต่ละกลุ่ม
  • สัดส่วนขององค์ประกอบเปลี่ยนไปอย่างไรข้ามกลุ่ม
  • หมวดที่มีอยู่ทุกกลุ่มกับหมวดที่โผล่เฉพาะบางกลุ่ม

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

  • เทียบชั้นกลางและชั้นบนข้ามแท่งได้ยากมาก เพราะไม่ได้เริ่มจากเส้นฐานเดียวกัน
  • ไม่บอกสัดส่วนโดยตรง ต้องคำนวณเอง — ถ้าต้องการสัดส่วนให้ใช้ percent-stacked-bar
  • ไม่รองรับค่าติดลบ

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

  1. ดูความสูงรวมของแต่ละแท่งก่อน
  2. ดูชั้นล่างสุดซึ่งเป็นชั้นเดียวที่เทียบข้ามแท่งได้แม่น
  3. ดูว่าชั้นไหนหนาขึ้นหรือบางลงเมื่อเปลี่ยนกลุ่ม
  4. ใช้ตารางข้อมูลยืนยันตัวเลขของชั้นบน ๆ

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

  • ยอดรวมของแต่ละกลุ่มเป็นเท่าไร
  • ชั้นล่างสุดของกลุ่มไหนใหญ่ที่สุด
  • อะไรทำให้กลุ่มนี้ยอดรวมสูงกว่ากลุ่มอื่น

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

  • ชั้นบาง ๆ จำนวนมากที่มองแทบไม่เห็น แปลว่ายังไม่ได้รวมเป็น "อื่น ๆ"
  • ลำดับการซ้อนเปลี่ยนไปในแต่ละแท่ง
  • มีค่าติดลบปนอยู่ ทำให้การซ้อนไม่มีความหมาย

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

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

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

  1. เขียนยอดรวมไว้เหนือแท่ง เพราะเป็นตัวเลขที่คนมองหาก่อน
  2. ติดป้ายชื่อหมวดลงในชั้นที่หนาพอ แทนการพึ่ง legend อย่างเดียว
  3. เว้นช่องว่าง 2px สีเดียวกับพื้นระหว่างชั้น เพื่อให้ขอบชั้นชัด

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

<กลุ่ม> มียอดรวมสูงสุดที่ <ค่า> โดยมาจาก <หมวดย่อย> เป็นหลัก

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

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

# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด 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("stacked-bar-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
VIEW = "stacked"   # ← ค่าจาก 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)
bottom = None
for i, col in enumerate(pivot.columns):
    ax.bar(pivot.index, pivot[col], bottom=bottom, label=col,
           color=[C1, C2, C3][i % 3], edgecolor=SURFACE, linewidth=1.6)   # เส้นขอบ = ช่องว่างระหว่างชั้น
    bottom = pivot[col] if bottom is None else bottom + pivot[col]
for i, total in enumerate(pivot.sum(axis=1)):
    ax.text(i, total * 1.02, f"{total:,.0f}", ha="center", fontsize=9)
ax.set_ylim(0, None); ax.legend(frameon=False, ncols=3)
ax.grid(axis="y", alpha=0.25); ax.set_axisbelow(True)

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

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

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

ข้อมูลตัวอย่าง · 18 แถว · seed 7● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
แท่งซ้อนตอบเรื่องยอดรวม
หน้าร้านเว็บไซต์แอป
0.00100200300400เหนืออีสานกลางตะวันออกตะวันตกใต้เหนือ · หน้าร้าน · 91.0เหนือ · เว็บไซต์ · 93.0เหนือ · แอป · 94.0อีสาน · หน้าร้าน · 120อีสาน · เว็บไซต์ · 84.0อีสาน · แอป · 154กลาง · หน้าร้าน · 88.0กลาง · เว็บไซต์ · 73.0กลาง · แอป · 130ตะวันออก · หน้าร้าน · 82.0ตะวันออก · เว็บไซต์ · 47.0ตะวันออก · แอป · 134ตะวันตก · หน้าร้าน · 92.0ตะวันตก · เว็บไซต์ · 70.0ตะวันตก · แอป · 136ใต้ · หน้าร้าน · 86.0ใต้ · เว็บไซต์ · 94.0ใต้ · แอป · 129
จำนวนหมวด6
จำนวนกลุ่มย่อย3
ยอดรวมสูงสุด358
ดูข้อมูลเป็นตาราง (6 แถว)
กราฟแท่งซ้อน 6 หมวด 3 กลุ่มย่อย
หมวดหน้าร้านเว็บไซต์แอปรวม
เหนือ91.093.094.0278
อีสาน12084.0154358
กลาง88.073.0130291
ตะวันออก82.047.0134263
ตะวันตก92.070.0136298
ใต้86.094.0129309

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

  • มุมมอง: ลองตามชั้นที่สองจากล่างข้ามทุกแท่งด้วยตา แล้วเปิดตารางเช็ค จะเห็นว่าสายตาประเมินชั้นที่ไม่ติดเส้นฐานได้แย่กว่าที่คิดมาก

ข้อควรระวัง

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

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

  • คำถามคือสัดส่วนภายในกลุ่มใช้แทน: percent-stacked-bar
  • ต้องเทียบหมวดย่อยข้ามกลุ่มอย่างแม่นยำใช้แทน: grouped-bar หรือ small-multiples
  • มีค่าติดลบใช้แทน: diverging-bar
  • ข้อมูลเป็นอนุกรมเวลาใช้แทน: stacked-area

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

เทคนิคถัดไป · กราฟแท่งซ้อน 100%