01ภาพรวมของกราฟนี้
Box plot ย่อการกระจายทั้งหมดเหลือห้าตัวเลข คือต่ำสุด ควอไทล์ที่หนึ่ง มัธยฐาน ควอไทล์ที่สาม และสูงสุด การย่อนี้ทำให้วางหลายกลุ่มเรียงกันได้โดยไม่รก แต่ก็ซ่อนรูปร่างไว้ การกระจายสองยอดกับยอดเดียวอาจให้กล่องหน้าตาเหมือนกันได้
เปรียบเทียบให้เห็นภาพ
เหมือนสรุปผลสอบทั้งห้องด้วยประโยคเดียวว่า "คนกลางได้ 65 ครึ่งกลางของห้องได้ระหว่าง 55 ถึง 78 และมีคนหนึ่งได้ 12" ซึ่งกระชับมากแต่ไม่บอกว่าคะแนนเกาะกลุ่มอย่างไร
ใช้ที่ไหนบ้าง — เทียบการกระจายของหลายสาขา หลายกลุ่มทดลอง หรือหลายช่วงเวลา และตรวจหาค่าผิดปกติก่อนวิเคราะห์
สร้างอย่างไร
- ขั้นที่ 1คำนวณควอไทล์ของแต่ละกลุ่ม
- ขั้นที่ 2วาดกล่องจาก Q1 ถึง Q3
- ขั้นที่ 3ลากหนวดถึงค่าที่ยังอยู่ในระยะ 1.5 เท่าของ IQR
- ขั้นที่ 4วาดจุดสำหรับค่าที่เกินหนวด
ตัวอย่างคลาสสิก · John Tukey สร้าง box plot ขึ้นในหนังสือ Exploratory Data Analysis ปี 1977
Tukey ออกแบบสัญลักษณ์นี้ให้วาดด้วยมือได้เร็วและอ่านได้ทันทีในยุคก่อนคอมพิวเตอร์กราฟิก เกณฑ์ 1.5 เท่าของ IQR สำหรับค่าผิดปกติที่เขาเลือกไว้ยังเป็นค่ามาตรฐานในซอฟต์แวร์แทบทุกตัวจนถึงวันนี้
02อธิบายภาษาคนเข้าใจง่าย ๆ
เมื่อต้องเทียบการกระจายของสิบกลุ่ม การวาดฮิสโทแกรมสิบอันจะกินพื้นที่มหาศาลและเทียบกันยาก box plot แก้ด้วยการย่อแต่ละกลุ่มให้เหลือสัญลักษณ์เดียวที่แคบ
กล่องคือช่วงที่ครึ่งกลางของข้อมูลอยู่ เส้นในกล่องคือค่ามัธยฐาน หนวดที่ยื่นออกไปคือช่วงของข้อมูลที่ยังถือว่าปกติ และจุดที่อยู่นอกหนวดคือค่าที่ผิดปกติตามเกณฑ์ที่กำหนด
ข้อดีคือความกระชับสุดขีด สิบกลุ่มวางเรียงกันได้สบายและเทียบมัธยฐานกับความกระจายได้ทันที นี่คือเหตุผลที่มันเป็นเครื่องมือมาตรฐานของการสำรวจข้อมูล
ข้อเสียที่ต้องรู้คือมันซ่อนรูปร่างไว้ทั้งหมด ข้อมูลที่มีสองยอดกับข้อมูลที่กระจายสม่ำเสมออาจให้กล่องที่หน้าตาเหมือนกันเป๊ะ ซึ่งเป็นข้อจำกัดที่ร้ายแรงถ้าเราไม่รู้
ทางแก้ที่ใช้กันมากขึ้นเรื่อย ๆ คือวาดจุดข้อมูลจริงทับลงไปบนกล่องเมื่อข้อมูลไม่เยอะเกินไป หรือเปลี่ยนไปใช้ violin plot ที่แสดงรูปร่างด้วย
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
group | กลุ่มที่เทียบ | หมวดหมู่ | x | 2–15 กลุ่ม | แต่ละกลุ่มต้องมีข้อมูลพอ อย่างน้อย 10 จุด |
value | ค่าที่วัดได้ | ตัวเลขต่อเนื่อง | y | ต่อเนื่อง | ต้องเป็นข้อมูลดิบรายแถว ห้ามสรุปมาก่อน |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- ตรวจว่าทุกกลุ่มมีข้อมูลพอ
- ระบุเกณฑ์ที่ใช้นิยามค่าผิดปกติ
- ตัดสินใจว่าจะแสดงจุดดิบทับหรือไม่
ค่าที่หายไป — ตัดค่าว่างออกและแสดงจำนวนจุดของแต่ละกลุ่มไว้ใต้ชื่อกลุ่ม เพราะกลุ่มที่มีข้อมูลน้อยต้องอ่านด้วยความระวัง
สเกลของแกน — linear หรือ log ก็ได้ แต่ควอไทล์บนสเกล log จะไม่สมมาตรเหมือนบน linear
กราฟจะเริ่มพังเมื่อไร
กลุ่มที่มีน้อยกว่า 10 จุด ควอไทล์จะไม่เสถียร — แสดงเป็น strip plot แทนสำหรับกลุ่มนั้น
หน้าตาไฟล์ที่ต้องเตรียม
group,value
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- กลุ่มไหนมีมัธยฐานสูงสุด
- กลุ่มไหนกระจายมากที่สุด จากความสูงของกล่อง
- ค่าผิดปกติอยู่ที่กลุ่มไหนบ้าง
- ความเบ้จากตำแหน่งของเส้นมัธยฐานในกล่อง
สิ่งที่กราฟนี้ตอบไม่ได้
- ★ ไม่บอกรูปร่างการกระจาย — สองยอดกับยอดเดียวอาจให้กล่องเหมือนกัน
- ไม่บอกจำนวนข้อมูลของแต่ละกลุ่ม ต้องเขียนกำกับเอง
- ไม่บอกว่าความต่างระหว่างกลุ่มมีนัยสำคัญหรือไม่
อ่านตามลำดับนี้
- ดูเส้นมัธยฐานเพื่อเทียบระดับกลางของแต่ละกลุ่ม
- ดูความสูงของกล่องเพื่อเทียบความกระจาย
- ดูตำแหน่งของเส้นมัธยฐานในกล่องเพื่ออ่านความเบ้
- ดูจุดนอกหนวดว่าอยู่กลุ่มไหน
คำถามที่ตอบได้
- กลุ่มไหนดีกว่าโดยรวม
- กลุ่มไหนที่ผลไม่สม่ำเสมอ
- มีค่าผิดปกติที่ต้องตรวจสอบไหม
สัญญาณว่ามีอะไรผิด
- กล่องแบนมากซึ่งมักแปลว่าข้อมูลกระจุกที่ค่าเดียว
- ไม่บอกจำนวนข้อมูลต่อกลุ่ม
- ไม่ระบุเกณฑ์ของค่าผิดปกติที่ใช้
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- เขียนจำนวนข้อมูลของแต่ละกลุ่มไว้ใต้ชื่อกลุ่ม
- วาดจุดข้อมูลจริงทับเมื่อข้อมูลไม่เกิน 200 จุดต่อกลุ่ม
- ระบุเกณฑ์ค่าผิดปกติที่ใช้ใน caption
รูปแบบชื่อกราฟที่ควรใช้
<กลุ่ม> มีมัธยฐานสูงสุดที่ <ค่า> แต่ <กลุ่ม> กระจายกว้างกว่าอย่างชัดเจน
06เอาไปใช้ต่อ
โค้ดทุกชิ้นอ่านไฟล์ box-plot-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง
# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด box-plot-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("box-plot-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "shape" # ← ค่าจาก Playground
fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)
groups = [g["value"].dropna().to_numpy() for _, g in df.groupby("group")]
labels = [f"{k}\n(n={len(g)})" for k, g in df.groupby("group")] # เขียน n กำกับเสมอ
bp = ax.boxplot(groups, labels=labels, patch_artist=True, widths=0.5)
for patch in bp["boxes"]:
patch.set_facecolor("#cde2fb"); patch.set_edgecolor(C1)
ax.set_ylabel("ค่าที่วัดได้"); ax.grid(axis="y", alpha=0.25); ax.set_axisbelow(True)
ax.spines[["top", "right"]].set_visible(False) # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("box-plot.png", dpi=160)
print("เขียน box-plot.png แล้ว ·", len(df), "แถว")07ลองเล่นเพื่อเข้าใจ
box plot ซ่อนรูปร่างไว้ — การกระจายสองยอดกับยอดเดียวอาจให้กล่องหน้าตาเหมือนกัน
ดูข้อมูลเป็นตาราง (4 แถว)
| กลุ่ม | n | ต่ำสุด | Q1 | มัธยฐาน | Q3 | สูงสุด |
|---|---|---|---|---|---|---|
| สาขา A | 80 | 50.0 | 54.8 | 57.6 | 59.7 | 67.1 |
| สาขา B | 80 | 55.9 | 63.4 | 66.3 | 70.2 | 76.6 |
| สาขา C | 80 | 55.6 | 60.1 | 61.7 | 63.7 | 68.1 |
| สาขา D | 80 | 62.6 | 69.0 | 73.7 | 77.0 | 84.9 |
ลองแล้วจะเห็นอะไร
- สังเกตอะไร: เปิดหน้า violin-plot คู่กัน จะเห็นว่าข้อมูลชุดเดียวกันมีรูปร่างที่กล่องไม่เคยบอกเลย
ข้อควรระวัง
- ★ ลืมว่ากล่องซ่อนรูปร่างไว้ แล้วสรุปว่าการกระจายของสองกลุ่มเหมือนกันเพราะกล่องเหมือนกัน
- ใช้กับกลุ่มที่มีข้อมูลน้อยกว่า 10 จุด ซึ่งควอไทล์ไม่เสถียร
- ไม่เขียนจำนวนข้อมูลของแต่ละกลุ่ม ทำให้เทียบกลุ่มใหญ่กับกลุ่มเล็กอย่างไม่ระวัง
- ไม่ระบุเกณฑ์ของค่าผิดปกติ ซึ่งแต่ละไลบรารีใช้ไม่เหมือนกัน
เมื่อไหร่ไม่ควรใช้กราฟนี้
- ต้องการเห็นรูปร่างการกระจาย→ใช้แทน: violin-plot หรือ raincloud-plot
- มีข้อมูลน้อยกว่า 10 จุดต่อกลุ่ม→ใช้แทน: strip-plot ที่แสดงทุกจุด
- มีกลุ่มเดียว→ใช้แทน: histogram หรือ density-plot
- ข้อมูลใหญ่มากและสนใจหาง→ใช้แทน: boxen-plot ที่แสดงควอนไทล์ลึกกว่า