01ภาพรวมของกราฟนี้
Letter-value plot ขยาย box plot ด้วยการซ้อนกล่องหลายชั้น แต่ละชั้นแทนควอนไทล์ที่ลึกลงไปครึ่งหนึ่งเรื่อย ๆ เหตุผลคือเมื่อข้อมูลมีเป็นหมื่นแถว ควอนไทล์ที่ลึกยังประมาณได้แม่น การยุบทุกอย่างนอกหนวดให้เป็นจุดผิดปกติจึงทิ้งข้อมูลไปมาก
เปรียบเทียบให้เห็นภาพ
เหมือนซูมเข้าไปดูหางของสัตว์ทีละส่วน แทนที่จะบอกแค่ว่า "มีหาง" เราเห็นว่าหางเรียวลงอย่างไร
ใช้ที่ไหนบ้าง — ข้อมูลขนาดใหญ่ระดับหมื่นถึงล้านแถว การวิเคราะห์ค่าผิดปกติ หรือเมื่อ box plot แสดงจุดผิดปกติเป็นร้อยจุดจนอ่านไม่ได้
สร้างอย่างไร
- ขั้นที่ 1คำนวณควอนไทล์ที่ลึกลงไปเรื่อย ๆ
- ขั้นที่ 2วาดกล่างซ้อนกันโดยกล่องนอกแคบกว่า
- ขั้นที่ 3ใช้สีไล่เฉดตามความลึกของชั้น
- ขั้นที่ 4เหลือเฉพาะจุดที่ผิดปกติจริง ๆ
ตัวอย่างคลาสสิก · Hofmann, Wickham และ Kafadar เสนอ letter-value plot ในปี 2017
ทีมวิจัยชี้ว่า box plot แสดงจุดผิดปกติมากเกินจริงเมื่อข้อมูลใหญ่ เพราะเกณฑ์ 1.5 IQR ถูกปรับมาสำหรับข้อมูลขนาดเล็ก พวกเขาเสนอให้แสดงควอนไทล์ลึกเพิ่มตามจำนวนข้อมูลที่มี ซึ่งเป็นคำตอบที่มีฐานทางสถิติแทนการปรับเกณฑ์ตามใจ
02อธิบายภาษาคนเข้าใจง่าย ๆ
Box plot ถูกออกแบบในยุคที่ข้อมูลมีไม่กี่สิบแถว เกณฑ์ที่ว่าอะไรเกิน 1.5 เท่าของ IQR คือค่าผิดปกติจึงสมเหตุสมผลในบริบทนั้น
แต่เมื่อข้อมูลมีเป็นแสนแถว เกณฑ์เดิมจะทำให้มีจุดผิดปกติเป็นพัน ๆ จุด ซึ่งไม่ใช่ค่าผิดปกติจริง มันคือหางของการกระจายที่ปกติดี แต่ box plot ไม่มีที่ให้มัน
Letter-value plot แก้ด้วยการซ้อนกล่องเพิ่มเข้าไป กล่องในสุดคือครึ่งกลางเหมือนเดิม กล่องถัดออกมาคือ 75% ตรงกลาง แล้ว 87.5% ไล่ไปเรื่อย ๆ
เหตุผลทางสถิติคือเมื่อข้อมูลใหญ่ ควอนไทล์ที่ลึกลงไปก็ยังประมาณได้แม่นพอ ดังนั้นจึงคุ้มที่จะแสดง ต่างจากข้อมูลเล็กที่ควอนไทล์ลึก ๆ แกว่งจนไม่มีความหมาย
ผลที่ได้คือเห็นรูปร่างของหางว่าเรียวลงเร็วหรือช้า ซึ่งเป็นข้อมูลสำคัญมากในงานที่หางมีผล เช่น การเงินหรือการวัดเวลาตอบสนองของระบบ
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
group | กลุ่ม | หมวดหมู่ | x | 1–8 กลุ่ม | เหมือน box plot |
value | ค่าที่วัดได้ | ตัวเลขต่อเนื่อง | y | ต่อเนื่อง | ★ ต้องมีข้อมูลมาก อย่างน้อยหลักพันต่อกลุ่ม จึงจะคุ้มกับการใช้กราฟนี้ |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- ตรวจว่าข้อมูลมากพอที่ควอนไทล์ลึกจะมีความหมาย
- กำหนดจำนวนชั้นตามขนาดข้อมูล
- ตัดค่าว่างออก
ค่าที่หายไป — ตัดค่าว่างและรายงานจำนวน เพราะจำนวนชั้นที่เหมาะสมขึ้นกับขนาดข้อมูล
สเกลของแกน — linear หรือ log ก็ได้ และ log มักเหมาะเมื่อหางยาวมาก
กราฟจะเริ่มพังเมื่อไร
ต่ำกว่า 200 แถวต่อกลุ่ม ชั้นนอกจะมาจากข้อมูลไม่กี่จุดและไม่น่าเชื่อถือ — ใช้ box plot แทน
หน้าตาไฟล์ที่ต้องเตรียม
group,value
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- รูปร่างของหางว่าเรียวลงเร็วหรือช้า
- ความไม่สมมาตรระหว่างหางซ้ายกับหางขวา
- ค่าที่ผิดปกติจริง ๆ ซึ่งเหลืออยู่ไม่กี่จุด
สิ่งที่กราฟนี้ตอบไม่ได้
- ไม่บอกรูปร่างของช่วงกลางละเอียดเท่า violin plot
- ต้องอธิบายวิธีอ่านก่อนเสมอเพราะคนส่วนใหญ่ไม่รู้จัก
อ่านตามลำดับนี้
- ดูกล่องในสุดซึ่งคือครึ่งกลางเหมือน box plot
- ดูว่ากล่องชั้นนอกแผ่ออกไปเร็วแค่ไหน
- ดูจุดที่เหลือนอกกล่องทุกชั้น
คำถามที่ตอบได้
- หางของการกระจายหนาแค่ไหน
- กลุ่มไหนมีค่าสุดโต่งมากกว่า
สัญญาณว่ามีอะไรผิด
- ใช้กับข้อมูลน้อยจนชั้นนอกมาจากไม่กี่จุด
- ไม่อธิบายว่าแต่ละชั้นหมายถึงอะไร
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- อธิบายว่าแต่ละชั้นแทนควอนไทล์อะไร
- เขียนจำนวนข้อมูลของแต่ละกลุ่ม
- ทำเครื่องหมายจุดผิดปกติที่เหลือ
รูปแบบชื่อกราฟที่ควรใช้
<กลุ่ม> มีหางขวาหนากว่ากลุ่มอื่นชัดเจน จากการแผ่ของชั้นนอก
06เอาไปใช้ต่อ
โค้ดทุกชิ้นอ่านไฟล์ boxen-plot-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง
# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด boxen-plot-sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์
import matplotlib
matplotlib.use("Agg") # รันแบบไม่มีหน้าจอ ถ้ารันในเครื่องตัวเองลบบรรทัดนี้ได้
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
C1, C2, C3 = "#2a78d6", "#eb6834", "#1baf7a" # palette ของคลัง เปลี่ยนเป็นสีองค์กรได้
INK, SURFACE = "#0b0b0b", "#fcfcfb"
df = pd.read_csv("boxen-plot-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "tails" # ← ค่าจาก Playground
fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)
sns.boxenplot(data=df, x="group", y="value", ax=ax, color="#9ec5f4", linewidth=0.8)
counts = df.groupby("group").size()
ax.set_xticklabels([f"{k}\n(n={v})" for k, v in counts.items()])
ax.set_ylabel("ค่าที่วัดได้")
ax.set_title("แต่ละชั้นคือควอนไทล์ที่ลึกลงไปครึ่งหนึ่ง", loc="left", fontsize=10)
ax.spines[["top", "right"]].set_visible(False) # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("boxen-plot.png", dpi=160)
print("เขียน boxen-plot.png แล้ว ·", len(df), "แถว")07ลองเล่นเพื่อเข้าใจ
ดูข้อมูลเป็นตาราง (4 แถว)
| กลุ่ม | n | ต่ำสุด | Q1 | มัธยฐาน | Q3 | สูงสุด |
|---|---|---|---|---|---|---|
| สาขา A | 100 | 49.9 | 54.4 | 57.5 | 59.6 | 67.1 |
| สาขา B | 100 | 55.9 | 62.8 | 66.2 | 69.9 | 76.6 |
| สาขา C | 100 | 55.6 | 60.1 | 61.7 | 63.8 | 68.1 |
| สาขา D | 100 | 62.6 | 69.6 | 73.9 | 77.3 | 84.9 |
ลองแล้วจะเห็นอะไร
- สังเกตอะไร: เทียบกับหน้า box-plot ที่ใช้ข้อมูลเดียวกัน จะเห็นว่าสิ่งที่ box plot ยุบเป็นจุดผิดปกติ ที่นี่กลายเป็นชั้นที่มีรูปร่างและอ่านได้
ข้อควรระวัง
- ใช้กับข้อมูลน้อย ซึ่งชั้นนอกมาจากไม่กี่จุดและไม่น่าเชื่อถือ
- ไม่อธิบายว่าแต่ละชั้นแทนอะไร ทำให้ผู้อ่านตีความเป็น box plot ธรรมดา
- ใช้จำนวนชั้นมากเกินไปจนแยกความเข้มไม่ออก
เมื่อไหร่ไม่ควรใช้กราฟนี้
- ข้อมูลน้อยกว่า 200 แถวต่อกลุ่ม→ใช้แทน: box-plot หรือ strip-plot
- สนใจรูปร่างของช่วงกลาง→ใช้แทน: violin-plot
- ผู้อ่านไม่คุ้นกับสถิติ→ใช้แทน: box-plot