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

กราฟจุดกระจายStrip Plot

ไม่สรุปอะไรเลย แสดงทุกจุดที่มีจริง

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

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

Strip plot ไม่สรุปอะไรเลย มันวาดทุกจุดข้อมูลตามค่าจริงและเบียดตำแหน่งเล็กน้อยไม่ให้ทับกันสนิท เมื่อข้อมูลมีไม่มาก นี่คือวิธีที่ซื่อสัตย์ที่สุด เพราะค่าสรุปอย่างมัธยฐานจากข้อมูล 8 จุดอาจให้ภาพที่มั่นใจเกินจริง

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

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

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

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

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

ตัวอย่างคลาสสิก · ขบวนการแสดงจุดข้อมูลจริงในวารสารชีววิทยา

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

PLOS Biology · Beyond bar and line graphs

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

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

Strip plot เลือกทางตรงข้ามคือไม่สรุปเลย วาดทุกจุดที่มีจริงลงไป ผู้อ่านจึงเห็นด้วยตาตัวเองว่ามีข้อมูลกี่จุดและมันเกาะกลุ่มกันอย่างไร

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

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

เมื่อข้อมูลเริ่มเยอะเกิน 200-300 จุดต่อกลุ่ม การเบียดจะไม่พอ ต้องเปลี่ยนไปใช้ beeswarm ที่จัดจุดอย่างเป็นระบบ หรือกลับไปใช้ box plot เพราะตอนนั้นการสรุปเริ่มเชื่อถือได้แล้ว

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

รูปทรงของข้อมูลข้อมูลดิบรายแถว (ยังไม่สรุป)
จำนวนมิติที่เข้ารหัสได้2 มิติ
จำนวนแถวที่เหมาะ10–100 จุดต่อกลุ่มขั้นต่ำ 5 แถว
เส้นฐานศูนย์เลือกได้อ่านจากตำแหน่งของจุดบนแกนร่วม

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

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

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

  1. ตัดค่าว่างออก
  2. กำหนดขนาดของ jitter ให้น้อยที่สุดเท่าที่ยังเห็นจุด
  3. ตัดสินใจว่าจะเพิ่มเส้นมัธยฐานหรือไม่

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

สเกลของแกน — linear เป็นหลัก ใช้ log ได้ถ้าค่ากินหลายเท่า

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

เกิน 300 จุดต่อกลุ่ม จุดจะทับกันจนนับไม่ได้ — ใช้ beeswarm-plot หรือ box-plot

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

group,value

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

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

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

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

  • ไม่ให้ค่าสรุปโดยตรง ต้องเพิ่มเส้นมัธยฐานเอง
  • เมื่อจุดเยอะจะทับกันจนประเมินความหนาแน่นผิด

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

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

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

  • กลุ่มนี้มีข้อมูลกี่จุดจริง ๆ
  • จุดเกาะกลุ่มกันหรือกระจาย
  • มีจุดที่โดดออกไปไหม

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

  • ใช้ jitter มากจนดูเหมือนตำแหน่งแนวนอนมีความหมาย
  • จุดทับกันจนดูเหมือนข้อมูลน้อยกว่าความจริง

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

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

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

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

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

ข้อมูลทั้ง <จำนวน> จุดของ <กลุ่ม> เกาะอยู่ระหว่าง <ช่วง>

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

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

# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด strip-plot-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("strip-plot-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "count"   # ← ค่าจาก Playground

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

groups = list(df["group"].unique())
rng = np.random.default_rng(42)          # ตรึง seed ให้ jitter เหมือนเดิมทุกครั้ง
for i, g in enumerate(groups):
    v = df[df["group"] == g]["value"]
    jitter = rng.uniform(-0.12, 0.12, len(v))    # jitter ไม่มีความหมาย มีไว้กันจุดทับ
    ax.scatter(np.full(len(v), i) + jitter, v, s=34, alpha=0.6, color=C1)
    ax.plot([i - 0.2, i + 0.2], [v.median()] * 2, color=INK, linewidth=2)
ax.set_xticks(range(len(groups)))
ax.set_xticklabels([f"{g}\n(n={(df['group'] == g).sum()})" for g in groups])

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

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

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

ข้อมูลตัวอย่าง · 80 แถว · seed 101● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
จุดที่เห็นคือข้อมูลจริงทุกจุด
สาขา Aสาขา Bสาขา Cสาขา D
50.060.070.080.090.0สาขา Aสาขา Bสาขา Cสาขา Dสาขา A · มัธยฐาน 58.0 · IQR 55.7–60.2 · n=20สาขา B · มัธยฐาน 64.3 · IQR 62.6–67.1 · n=20สาขา C · มัธยฐาน 61.4 · IQR 60.2–63.9 · n=20สาขา D · มัธยฐาน 74.5 · IQR 72.4–78.1 · n=20
กลุ่มที่มัธยฐานสูงสุดสาขา D
กลุ่มที่กระจายมากสุดสาขา D
จำนวนกลุ่ม4
ดูข้อมูลเป็นตาราง (4 แถว)
strip plot 4 กลุ่ม
กลุ่มnต่ำสุดQ1มัธยฐานQ3สูงสุด
สาขา A2050.055.758.060.263.4
สาขา B2055.962.664.367.172.3
สาขา C2055.660.261.463.967.2
สาขา D2066.272.474.578.184.9

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

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

ข้อควรระวัง

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

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

  • มีข้อมูลมากกว่า 300 จุดต่อกลุ่มใช้แทน: beeswarm-plot หรือ box-plot
  • ต้องการค่าสรุปเป็นหลักใช้แทน: box-plot
  • ต้องการเห็นรูปร่างการกระจายใช้แทน: violin-plot หรือ histogram

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

เทคนิคถัดไป · กราฟฝูงผึ้ง