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

กราฟไวโอลินViolin Plot

แสดงรูปร่างที่ box plot ซ่อนไว้

3
ผสานหลายแนวคิดควรรู้ก่อน · อ่าน box plot และเข้าใจแนวคิดความหนาแน่น
ลองเล่น

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

Violin plot รวมจุดแข็งของ box plot กับ density plot เข้าด้วยกัน ความกว้างของรูปบอกความหนาแน่นที่ระดับค่านั้น และมักมีกล่องเล็ก ๆ อยู่ข้างในเพื่อให้อ่านมัธยฐานได้ ผลคือเห็นทั้งรูปร่างและค่าสรุปในสัญลักษณ์เดียว

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

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

ใช้ที่ไหนบ้าง — เทียบผลการทดลองหลายกลุ่มในงานวิจัย ตรวจว่ากลุ่มไหนมีสองยอดซ่อนอยู่ หรือแทน box plot เมื่อรูปร่างสำคัญ

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

  1. ขั้นที่ 1คำนวณความหนาแน่นของแต่ละกลุ่ม
  2. ขั้นที่ 2สะท้อนเส้นความหนาแน่นสองข้างของแกนกลาง
  3. ขั้นที่ 3ใส่กล่องหรือเส้นมัธยฐานไว้ข้างใน
  4. ขั้นที่ 4ระบุ bandwidth ที่ใช้

ตัวอย่างคลาสสิก · Hintze และ Nelson เสนอ violin plot ในปี 1998

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

Hintze & Nelson · Violin Plots (The American Statistician)

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

Box plot กระชับมากแต่ซ่อนรูปร่างไว้หมด ปัญหานี้ไม่ใช่เรื่องทฤษฎี เพราะกลุ่มที่มีสองยอด เช่น ลูกค้าสองแบบปนกัน จะให้กล่องหน้าตาปกติที่ไม่มีอะไรเตือนเราเลย

Violin plot แก้ด้วยการเอาเส้นความหนาแน่นมาวาดแทนกล่อง แล้วสะท้อนให้สมมาตรสองข้าง ความกว้างของรูปตรงระดับค่าใดบอกว่ามีข้อมูลหนาแน่นแค่ไหนที่ระดับนั้น

เพราะเรายังอยากได้ตัวเลขสรุปด้วย จึงนิยมวาดกล่องเล็ก ๆ หรือเส้นมัธยฐานไว้ข้างในรูป ทำให้ได้ทั้งสองอย่างในสัญลักษณ์เดียว

ข้อควรระวังที่สืบทอดมาจาก density plot คือมันมีการตัดสินใจเรื่อง bandwidth ซ่อนอยู่ ความอ้วนผอมของไวโอลินเปลี่ยนได้ตามค่าที่เลือก จึงควรระบุไว้

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

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

รูปทรงของข้อมูลข้อมูลดิบรายแถว (ยังไม่สรุป)
จำนวนมิติที่เข้ารหัสได้3 มิติ
จำนวนแถวที่เหมาะ3–6 กลุ่ม × 50–1,000 จุดต่อกลุ่มขั้นต่ำ 60 แถว
เส้นฐานศูนย์เลือกได้อ่านจากตำแหน่งบนแกนค่าร่วม ไม่ใช่ความยาวจากศูนย์

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
groupกลุ่มที่เทียบหมวดหมู่x2–8 กลุ่มเกิน 8 กลุ่มรูปจะแคบจนอ่านรูปร่างไม่ได้
valueค่าที่วัดได้ตัวเลขต่อเนื่องyต่อเนื่องต้องเป็นข้อมูลดิบและมีอย่างน้อย 30 จุดต่อกลุ่ม

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

  1. ตรวจว่าทุกกลุ่มมีข้อมูลพอ
  2. เลือกและบันทึก bandwidth
  3. ตัดสินใจว่าจะใส่กล่องข้างในหรือไม่

ค่าที่หายไป — ตัดค่าว่างและแสดงจำนวนจุดของแต่ละกลุ่ม เพราะความกว้างของไวโอลินไม่ได้บอกขนาดกลุ่ม

สเกลของแกน — linear เป็นหลัก และทุกกลุ่มต้องใช้สเกลเดียวกัน

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

กลุ่มที่มีน้อยกว่า 30 จุด รูปร่างจะสะท้อน bandwidth มากกว่าสะท้อนข้อมูล

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

group,value

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

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

  • รูปร่างการกระจายของแต่ละกลุ่ม
  • กลุ่มที่มีสองยอด ซึ่ง box plot จะซ่อนไว้
  • ความเบ้จากความไม่สมมาตรของรูปตามแนวตั้ง
  • มัธยฐานจากเส้นข้างใน

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

  • ความกว้างไม่ได้บอกจำนวนข้อมูลของกลุ่ม เว้นแต่จะตั้งค่าให้ scale ตามจำนวน
  • รูปร่างขึ้นกับ bandwidth ที่เลือก จึงไม่ใช่ข้อเท็จจริงที่แน่นอน

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

  1. ดูว่ารูปอ้วนที่ระดับค่าไหน นั่นคือช่วงที่ข้อมูลหนาแน่น
  2. นับจำนวนส่วนที่อ้วนเพื่อหาจำนวนยอด
  3. ดูเส้นมัธยฐานข้างในเพื่ออ่านค่ากลาง

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

  • กลุ่มไหนมีข้อมูลสองกลุ่มปนกันอยู่
  • การกระจายของกลุ่มนี้เบ้ไปทางไหน
  • กลุ่มไหนที่ค่าเกาะกลุ่มแน่นที่สุด

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

  • ไวโอลินอ้วนมากในกลุ่มที่มีข้อมูลน้อย ซึ่งเป็นผลของ bandwidth ไม่ใช่ของข้อมูล
  • ไม่ระบุจำนวนข้อมูลต่อกลุ่ม

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

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

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

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

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

<กลุ่ม> มีการกระจายสองยอด ต่างจากกลุ่มอื่นที่มียอดเดียว

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

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

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

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

df = pd.read_csv("violin-plot-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "width"   # ← ค่าจาก Playground

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

sns.violinplot(data=df, x="group", y="value", ax=ax, inner="box",   # inner="box" ให้อ่านค่าสรุปได้
               color="#9ec5f4", linewidth=1.4, cut=0)                       # cut=0 ไม่ยื่นเลยข้อมูลจริง
counts = df.groupby("group").size()
ax.set_xticklabels([f"{k}\n(n={v})" for k, v in counts.items()])
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("violin-plot.png", dpi=160)
print("เขียน violin-plot.png แล้ว ·", len(df), "แถว")

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

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

ข้อมูลตัวอย่าง · 320 แถว · seed 101● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
รูปอ้วนตรงไหนแปลว่าข้อมูลหนาแน่นตรงนั้น
สาขา Aสาขา Bสาขา Cสาขา D
50.060.070.080.090.0สาขา Aสาขา Bสาขา Cสาขา Dสาขา A · มัธยฐาน 57.6 · IQR 54.8–59.7 · n=80สาขา B · มัธยฐาน 66.3 · IQR 63.4–70.2 · n=80สาขา C · มัธยฐาน 61.7 · IQR 60.1–63.7 · n=80สาขา D · มัธยฐาน 73.7 · IQR 69.0–77.0 · n=80
กลุ่มที่มัธยฐานสูงสุดสาขา D
กลุ่มที่กระจายมากสุดสาขา D
จำนวนกลุ่ม4
ดูข้อมูลเป็นตาราง (4 แถว)
violin plot 4 กลุ่ม
กลุ่มnต่ำสุดQ1มัธยฐานQ3สูงสุด
สาขา A8050.054.857.659.767.1
สาขา B8055.963.466.370.276.6
สาขา C8055.660.161.763.768.1
สาขา D8062.669.073.777.084.9

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

  • สังเกตอะไร: เทียบกับหน้า box-plot ที่ใช้ข้อมูลชุดเดียวกัน จะเห็นว่าไวโอลินเผยรูปร่างที่กล่องซ่อนไว้ทั้งหมด

ข้อควรระวัง

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

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

  • ข้อมูลน้อยกว่า 30 จุดต่อกลุ่มใช้แทน: strip-plot หรือ beeswarm-plot ที่แสดงทุกจุด
  • ต้องการความกระชับสูงสุดและรูปร่างไม่สำคัญใช้แทน: box-plot
  • ต้องการเห็นทั้งรูปร่าง สถิติ และจุดดิบใช้แทน: raincloud-plot

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

เทคนิคถัดไป · กราฟสันเขา