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

กราฟแท่งกลุ่มGrouped Bar Chart

เทียบหมวดย่อยภายในแต่ละกลุ่มแบบเคียงข้างกัน

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

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

Grouped bar chart แบ่งข้อมูลด้วยสองตัวแปรหมวดหมู่พร้อมกัน ตัวหนึ่งกำหนดกลุ่มบนแกน อีกตัวกำหนดสีของแท่งที่วางเรียงกันในกลุ่ม ทำให้เทียบ "ภายในกลุ่มเดียวกัน" ได้แม่นที่สุด เพราะแท่งเริ่มจากเส้นฐานเดียวกันและอยู่ติดกัน

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

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

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

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

  1. ขั้นที่ 1เลือกตัวแปรหมวดสองตัว
  2. ขั้นที่ 2ตัดสินใจว่าตัวไหนอยู่แกน ตัวไหนเป็นสี
  3. ขั้นที่ 3วาดแท่งเรียงกันภายในกลุ่ม
  4. ขั้นที่ 4ใส่ legend และป้ายกำกับ

ตัวอย่างคลาสสิก · ผลสำรวจที่แยกตามเพศในแต่ละช่วงอายุ

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

Pew Research Center · วิธีนำเสนอข้อมูลสำรวจ

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

บางครั้งข้อมูลมีสองมิติที่เป็นหมวดหมู่ เช่น ยอดขายที่แยกทั้งตามภูมิภาคและตามช่องทาง ถ้าวาดกราฟแท่งธรรมดาเราต้องเลือกดูแค่มิติเดียว แต่ grouped bar ให้เห็นทั้งสองพร้อมกัน

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

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

ข้อจำกัดที่หนีไม่พ้นคือจำนวนแท่ง ถ้ามี 6 ภูมิภาค × 5 ช่องทาง จะได้ 30 แท่งเบียดกันจนบางและอ่านไม่ออก ในทางปฏิบัติควรคุมไว้ที่ประมาณ 4 กลุ่ม × 3-4 หมวดย่อย ถ้าเกินนี้ให้แยกเป็นกราฟย่อยหลายอันแทน

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

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

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

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
categoryกลุ่มหลักหมวดหมู่x2–6 กลุ่มตัวแปรที่จะวางเป็นกลุ่มบนแกน
groupหมวดย่อยในกลุ่มหมวดหมู่color2–4 หมวดเกิน 4 หมวดแท่งจะบางจนอ่านยาก และต้อง direct label ทุกแท่ง
valueปริมาณตัวเลขต่อเนื่องyต่อเนื่องต้องเป็นหน่วยเดียวกันทุกแท่ง

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

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

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

สเกลของแกน — linear เริ่มที่ศูนย์เท่านั้น ห้ามใช้ log

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

เกิน 24 แท่ง (เช่น 6×4) แท่งจะบางจนเทียบไม่ได้ — แยกเป็น small-multiples แทน

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

category,group,value
เหนือ,หน้าร้าน,91
เหนือ,เว็บไซต์,93

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

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

  • ภายในแต่ละกลุ่ม หมวดย่อยไหนมากที่สุด
  • รูปแบบภายในกลุ่มเหมือนกันทุกกลุ่มหรือกลับด้านในบางกลุ่ม
  • หมวดย่อยไหนเด่นสม่ำเสมอทุกกลุ่ม
  • กลุ่มที่มีรูปแบบผิดจากกลุ่มอื่น ซึ่งมักเป็นจุดที่น่าสนใจที่สุด

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

  • ไม่บอกยอดรวมของแต่ละกลุ่ม ต้องใช้ stacked-bar
  • เทียบหมวดย่อยเดียวกันข้ามกลุ่มที่อยู่ไกลกันได้ยาก เพราะไม่ได้อยู่ติดกัน
  • ไม่บอกสัดส่วนภายในกลุ่ม ต้องใช้ percent-stacked-bar

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

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

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

  • ในภูมิภาคนี้ ช่องทางไหนขายดีที่สุด
  • ช่องทางนี้เด่นทุกภูมิภาคหรือเด่นเฉพาะบางที่
  • มีภูมิภาคไหนที่รูปแบบต่างจากที่อื่นไหม

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

  • แท่งบางจนแยกกลุ่มไม่ออก แปลว่าหมวดเยอะเกินไป
  • ลำดับของหมวดย่อยไม่เหมือนกันในแต่ละกลุ่ม ทำให้เทียบไม่ได้
  • ไม่มี legend หรือ legend อยู่ไกลจากกราฟ

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

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

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

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

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

<หมวดย่อย> นำในเกือบทุก <กลุ่ม> ยกเว้น <ข้อยกเว้น>

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

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

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

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

df = pd.read_csv("grouped-bar-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "on"   # ← ค่าจาก 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")
n = len(pivot.columns)
xs = np.arange(len(pivot.index))
width = 0.8 / n
for i, col in enumerate(pivot.columns):
    ax.bar(xs + i * width - 0.4 + width / 2, pivot[col], width * 0.92,
           label=col, color=[C1, C2, C3][i % 3])
ax.set_xticks(xs); ax.set_xticklabels(pivot.index)
ax.set_ylim(0, None)
ax.legend(frameon=False, ncols=n)   # legend จำเป็นเสมอเมื่อมีตั้งแต่ 2 ชุดขึ้นไป
ax.grid(axis="y", alpha=0.25); ax.set_axisbelow(True)

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

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

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

ข้อมูลตัวอย่าง · 18 แถว · seed 7● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
ตารางช่วยตรวจว่าที่อ่านจากภาพตรงกับตัวเลขจริงไหม
หน้าร้านเว็บไซต์แอป
0.0050.0100150200เหนืออีสานกลางตะวันออกตะวันตกใต้เหนือ · หน้าร้าน · 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. ใส่หมวดย่อยเกิน 4 หมวด ทำให้แท่งบางและสีแยกยากสำหรับคนตาบอดสี
  2. สลับลำดับหมวดย่อยในแต่ละกลุ่ม ทำให้เทียบข้ามกลุ่มไม่ได้
  3. ใช้กราฟนี้ตอบคำถามเรื่องยอดรวม ซึ่งต้องใช้แท่งซ้อนแทน
  4. เลือกตัวแปรผิดข้าง — เอาสิ่งที่อยากให้เทียบไปไว้คนละกลุ่มจนอยู่ห่างกัน
  5. ไม่มี legend หรือใช้สีอย่างเดียวโดยไม่มีป้ายกำกับ

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

  • คำถามคือยอดรวมของแต่ละกลุ่มใช้แทน: stacked-bar
  • คำถามคือสัดส่วนภายในกลุ่มใช้แทน: percent-stacked-bar
  • มีมากกว่า 4 หมวดย่อยใช้แทน: small-multiples หรือ heatmap-matrix
  • ตัวแปรที่สองคือเวลาใช้แทน: multi-line-chart ที่แสดงแนวโน้มได้ดีกว่า

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

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