01ภาพรวมของกราฟนี้
Density plot ใช้การประมาณความหนาแน่นแบบเคอร์เนลเพื่อวาดเส้นเรียบแทนแท่ง ข้อดีคือรูปร่างไม่กระโดดตามตำแหน่งขอบ bin และซ้อนหลายกลุ่มได้สวยกว่า ข้อเสียคือย้ายการตัดสินใจจาก "จำนวน bin" ไปเป็น "ความกว้างของเคอร์เนล" ซึ่งซ่อนกว่าและคนมักไม่ปรับ
เปรียบเทียบให้เห็นภาพ
เหมือนเปลี่ยนจากการนับคนเป็นกลุ่ม ๆ มาเป็นการวัดความหนาแน่นของฝูงชนอย่างต่อเนื่อง ภาพที่ได้ลื่นกว่า แต่เราต้องเลือกว่าจะวัดในรัศมีกว้างแค่ไหน
ใช้ที่ไหนบ้าง — เทียบการกระจายของหลายกลุ่มบนภาพเดียว นำเสนอรูปร่างการกระจายในรายงาน หรือใช้เป็นชั้นเสริมบนกราฟอื่น
สร้างอย่างไร
- ขั้นที่ 1เลือกฟังก์ชันเคอร์เนล ปกติใช้แบบเกาส์เซียน
- ขั้นที่ 2เลือกความกว้างของเคอร์เนล
- ขั้นที่ 3คำนวณความหนาแน่นที่จุดต่าง ๆ
- ขั้นที่ 4วาดเส้นพร้อมขีดแสดงข้อมูลจริงที่ฐาน
ตัวอย่างคลาสสิก · ปัญหา bandwidth ที่ Silverman วางรากฐานไว้
Bernard Silverman แสดงให้เห็นว่าชุดข้อมูลเดียวกันสามารถให้เส้นความหนาแน่นที่มีหนึ่ง สอง หรือห้ายอดได้ ขึ้นกับ bandwidth ที่เลือกล้วน ๆ และเสนอสูตรประมาณค่าที่ยังใช้เป็นค่าเริ่มต้นในไลบรารีส่วนใหญ่จนถึงวันนี้
Silverman · Density Estimation for Statistics and Data Analysis ↗
02อธิบายภาษาคนเข้าใจง่าย ๆ
ปัญหาหนึ่งของฮิสโทแกรมที่คนมักไม่รู้คือ รูปร่างที่เห็นไม่ได้ขึ้นกับจำนวน bin อย่างเดียว แต่ขึ้นกับว่าขอบของ bin ตกตรงไหนด้วย ขยับขอบไปนิดเดียวยอดอาจย้ายที่
Density plot แก้ด้วยการเลิกใช้ถัง แล้วเปลี่ยนเป็นวางระฆังเล็ก ๆ ไว้บนทุกจุดข้อมูล แล้วบวกระฆังทั้งหมดเข้าด้วยกันเป็นเส้นเรียบเส้นเดียว
แต่การตัดสินใจไม่ได้หายไป มันแค่ย้ายที่ ตอนนี้เราต้องเลือกว่าระฆังแต่ละใบกว้างแค่ไหน ซึ่งเรียกว่า bandwidth ถ้าแคบเกินไปเส้นจะหยักตาม noise ถ้ากว้างเกินไปยอดหลายยอดจะถูกรีดจนเหลือยอดเดียว
อันตรายที่แท้จริงคือไลบรารีส่วนใหญ่เลือก bandwidth ให้อัตโนมัติ คนใช้จึงไม่รู้ว่ามีการตัดสินใจเกิดขึ้น ต่างจากฮิสโทแกรมที่ทุกคนรู้ว่าต้องเลือกจำนวน bin
ข้อควรระวังอีกข้อคือเส้นจะยื่นเลยค่าต่ำสุดและสูงสุดจริงของข้อมูลเสมอ ซึ่งอาจสื่อว่ามีค่าที่เป็นไปไม่ได้ เช่น อายุติดลบ ต้องตัดปลายหรือเตือนผู้อ่าน
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
value | ค่าที่ต้องการดูการกระจาย | ตัวเลขต่อเนื่อง | x | ต่อเนื่อง | ต้องเป็นข้อมูลดิบ และควรมีอย่างน้อย 50 แถวจึงจะเชื่อถือได้ |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- กรองค่าหายออก
- ตรวจว่าตัวแปรมีขอบเขตทางกายภาพหรือไม่ เช่น ห้ามติดลบ
- ตัดสินใจ bandwidth อย่างมีสติแทนที่จะใช้ค่า default
ค่าที่หายไป — ตัดค่าว่างออกและรายงานจำนวน เพราะการประมาณความหนาแน่นใช้ทุกจุดที่มี
สเกลของแกน — แกน x ตามค่าจริง แกน y เป็นความหนาแน่นซึ่งพื้นที่ใต้เส้นรวมเป็นหนึ่ง ไม่ใช่จำนวนนับ
กราฟจะเริ่มพังเมื่อไร
ต่ำกว่า 50 แถว เส้นจะสะท้อนการเลือก bandwidth มากกว่าสะท้อนข้อมูล
หน้าตาไฟล์ที่ต้องเตรียม
value
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- รูปร่างการกระจายแบบเรียบ
- จำนวนยอดที่แท้จริงของการกระจาย
- ความเบ้และความกว้างของหาง
สิ่งที่กราฟนี้ตอบไม่ได้
- อ่านจำนวนนับไม่ได้ เพราะแกนตั้งเป็นความหนาแน่นไม่ใช่ความถี่
- เส้นที่ยื่นเลยขอบข้อมูลจริงเป็นผลของการประมาณ ไม่ใช่ข้อมูลที่มีอยู่
อ่านตามลำดับนี้
- ดูจำนวนและตำแหน่งของยอด
- ดูความกว้างของเส้นซึ่งบอกความกระจาย
- ดูขีดที่ฐานเพื่อรู้ว่าข้อมูลจริงอยู่ตรงไหน
คำถามที่ตอบได้
- การกระจายมีกี่ยอด
- ข้อมูลเบ้ไปทางไหน
- สองกลุ่มนี้มีรูปร่างการกระจายต่างกันไหม
สัญญาณว่ามีอะไรผิด
- เส้นยื่นเข้าไปในช่วงค่าที่เป็นไปไม่ได้ เช่น อายุติดลบ
- ไม่บอกค่า bandwidth ที่ใช้
- มีจุดข้อมูลน้อยแต่วาดเส้นเรียบที่ดูน่าเชื่อถือ
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- วาดขีดของข้อมูลจริงไว้ที่ฐานเสมอ เพื่อให้เห็นว่าเส้นมาจากข้อมูลจริงแค่ไหน
- เขียนค่า bandwidth และจำนวนข้อมูลไว้ใน caption
- ลากเส้นมัธยฐานเพื่อให้มีจุดอ้างอิง
รูปแบบชื่อกราฟที่ควรใช้
การกระจายของ <ตัวแปร> มี <จำนวน> ยอด โดยยอดหลักอยู่ที่ <ค่า>
06เอาไปใช้ต่อ
โค้ดทุกชิ้นอ่านไฟล์ density-plot-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง
# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด density-plot-sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์
import matplotlib
matplotlib.use("Agg") # รันแบบไม่มีหน้าจอ ถ้ารันในเครื่องตัวเองลบบรรทัดนี้ได้
import matplotlib.pyplot as plt
import pandas as pd
from scipy.stats import gaussian_kde # pip install scipy
import numpy as np
C1, C2, C3 = "#2a78d6", "#eb6834", "#1baf7a" # palette ของคลัง เปลี่ยนเป็นสีองค์กรได้
INK, SURFACE = "#0b0b0b", "#fcfcfb"
df = pd.read_csv("density-plot-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
BANDWIDTH = 1 # ← ค่าจาก Playground
fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)
values = df["value"].dropna().to_numpy()
kde = gaussian_kde(values, bw_method=BANDWIDTH) # bw_method คือการตัดสินใจที่ต้องประกาศ
grid = np.linspace(values.min(), values.max(), 200)
ax.fill_between(grid, kde(grid), color="#9ec5f4", alpha=0.7)
ax.plot(grid, kde(grid), color=C1, linewidth=2)
ax.plot(values, np.zeros_like(values), "|", color=INK, alpha=0.35, markersize=8) # ขีดข้อมูลจริง
ax.set_xlabel("ค่าที่วัดได้"); ax.set_ylabel("ความหนาแน่น (ไม่ใช่จำนวนนับ)")
ax.set_title(f"bandwidth = {BANDWIDTH} · n = {len(values)}", loc="left")
ax.spines[["top", "right"]].set_visible(False) # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("density-plot.png", dpi=160)
print("เขียน density-plot.png แล้ว ·", len(df), "แถว")07ลองเล่นเพื่อเข้าใจ
เส้นที่ยื่นเลยค่าต่ำสุด/สูงสุดจริงเป็นผลของการประมาณ ไม่ใช่ข้อมูลที่มีอยู่
ดูข้อมูลเป็นตาราง (12 แถว)
| ค่า x | ความหนาแน่น |
|---|---|
| 44.15 | 0.0001 |
| 47.14 | 0.0022 |
| 50.12 | 0.0111 |
| 53.11 | 0.0240 |
| 56.09 | 0.0369 |
| 59.08 | 0.0527 |
| 62.06 | 0.0666 |
| 65.05 | 0.0659 |
| 68.03 | 0.0491 |
| 71.02 | 0.0195 |
| 74.00 | 0.0058 |
| 76.99 | 0.0011 |
ลองแล้วจะเห็นอะไร
- ตัวคูณ bandwidth: ลากไปต่ำสุดจะเห็นเส้นหยักตาม noise ลากไปสูงสุดจะเหลือเนินเดียว — ข้อมูลไม่เปลี่ยนเลย เปลี่ยนแค่การตัดสินใจของคนทำกราฟ
ข้อควรระวัง
- ใช้ bandwidth ค่า default โดยไม่รู้ว่ามีการตัดสินใจเกิดขึ้น ซึ่งอันตรายกว่าฮิสโทแกรมเพราะมองไม่เห็น
- ปล่อยให้เส้นยื่นเข้าไปในช่วงค่าที่เป็นไปไม่ได้
- ใช้กับข้อมูลน้อย ๆ ซึ่งได้เส้นเรียบที่ดูน่าเชื่อถือเกินความจริง
- ซ้อนเกิน 3 กลุ่มจนเส้นบังกัน
- ตีความแกนตั้งว่าเป็นจำนวนคน ทั้งที่เป็นความหนาแน่น
เมื่อไหร่ไม่ควรใช้กราฟนี้
- ข้อมูลน้อยกว่า 50 แถว→ใช้แทน: strip-plot หรือ histogram ที่แสดงข้อมูลจริงตรงไปตรงมา
- ผู้อ่านต้องการจำนวนนับ→ใช้แทน: histogram
- ต้องเทียบมากกว่า 3 กลุ่ม→ใช้แทน: ridgeline-plot หรือ violin-plot