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

กราฟเส้นชั้นความหนาแน่น2D Density Contour

วาดเส้นรอบบริเวณที่ข้อมูลกระจุกเป็นชั้น ๆ

4
ขั้นสูงควรรู้ก่อน · อ่าน scatter plot และเข้าใจแนวคิดความหนาแน่น
ลองเล่น

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

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

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

เหมือนเส้นระดับความสูงบนแผนที่ภูเขา เส้นที่ชิดกันแปลว่าชัน เส้นที่วนเป็นวงปิดแปลว่าเป็นยอดเขา

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

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

  1. ขั้นที่ 1ประมาณความหนาแน่นสองมิติ
  2. ขั้นที่ 2เลือกจำนวนชั้นที่จะวาด
  3. ขั้นที่ 3วาดเส้นหรือระบายสีตามชั้น
  4. ขั้นที่ 4วางจุดข้อมูลจริงทับบาง ๆ

ตัวอย่างคลาสสิก · เส้นระดับความสูงบนแผนที่ภูมิประเทศ

แนวคิดเส้นชั้นมาจากแผนที่ภูมิประเทศที่ใช้กันมาตั้งแต่ศตวรรษที่ 18 หลักการเดียวกันคือวาดเส้นเชื่อมจุดที่มีค่าเท่ากัน การยืมภาพนี้มาใช้กับความหนาแน่นของข้อมูลได้ผลดีเพราะคนคุ้นเคยกับการอ่านแผนที่อยู่แล้ว

Wikipedia · Contour line

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

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

Contour plot ตอบด้วยการประมาณความหนาแน่นทั่วทั้งระนาบ แล้ววาดเส้นรอบบริเวณที่ความหนาแน่นเท่ากัน วิธีคิดเหมือนเส้นระดับบนแผนที่ทุกประการ

สิ่งที่อ่านได้คือจำนวนยอด ถ้ามีวงปิดสองกลุ่มแยกกัน แปลว่าข้อมูลมีสองกลุ่มย่อยที่ scatter plot อาจแสดงเป็นเมฆก้อนเดียว

ระยะห่างระหว่างเส้นก็มีความหมาย เส้นที่ชิดกันแปลว่าความหนาแน่นเปลี่ยนเร็ว ซึ่งมักเป็นขอบของกลุ่ม ส่วนเส้นที่ห่างกันแปลว่าบริเวณนั้นค่อย ๆ จางออกไป

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

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

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

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
xตัวแปรแกนนอนตัวเลขต่อเนื่องxต่อเนื่องข้อมูลดิบ
yตัวแปรแกนตั้งตัวเลขต่อเนื่องyต่อเนื่องข้อมูลดิบ

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

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

ค่าที่หายไป — ตัดแถวที่ไม่ครบและรายงานจำนวน เพราะการประมาณความหนาแน่นใช้ทุกจุดที่มี

สเกลของแกน — linear ทั้งสองแกน การใช้ log จะทำให้รูปร่างของชั้นเปลี่ยนไปมากและต้องอธิบาย

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

ต่ำกว่า 200 จุด เส้นชั้นจะสะท้อนพารามิเตอร์มากกว่าสะท้อนข้อมูล

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

x,y
23.61,65.65
58.57,67.07

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

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

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

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

  • ไม่บอกจำนวนจุดที่แน่นอนในแต่ละบริเวณ
  • เส้นเป็นการประมาณ ไม่ใช่ขอบเขตจริงของข้อมูล

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

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

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

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

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

  • เส้นยื่นเข้าไปในบริเวณที่ไม่มีจุดข้อมูลเลย
  • ไม่ระบุจำนวนชั้นหรือพารามิเตอร์ที่ใช้

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

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

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

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

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

ข้อมูลกระจุกเป็น <จำนวน> กลุ่ม โดยกลุ่มหลักอยู่ที่ <ตำแหน่ง>

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

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

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

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

df = pd.read_csv("contour-plot-sample.csv")
df["x"] = pd.to_numeric(df["x"], errors="coerce")
df["y"] = pd.to_numeric(df["y"], errors="coerce")
LEVELS = 5   # ← ค่าจาก Playground

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

d = df.dropna(subset=["x", "y"])
kde = gaussian_kde(np.vstack([d["x"], d["y"]]))
gx, gy = np.mgrid[d["x"].min():d["x"].max():80j, d["y"].min():d["y"].max():80j]
z = kde(np.vstack([gx.ravel(), gy.ravel()])).reshape(gx.shape)
cf = ax.contourf(gx, gy, z, levels=LEVELS, cmap="Blues", alpha=0.85)
ax.scatter(d["x"], d["y"], s=5, color=INK, alpha=0.2)      # จุดจริงทับไว้ให้ตรวจสอบได้
fig.colorbar(cf, ax=ax, label="ความหนาแน่น", shrink=0.7)

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

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

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

ข้อมูลตัวอย่าง · 800 แถว · seed 163● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
จำนวนเส้นชั้นที่วาด
30.040.050.060.070.080.020.040.060.080.0

เส้นชั้นเป็นค่าประมาณจากข้อมูล ไม่ใช่ขอบเขตจริง — จำนวนชั้นที่เลือกเปลี่ยนภาพที่เห็น

จำนวนชั้น5
ศูนย์กลาง45.6, 59.8
สหสัมพันธ์-0.357
ดูข้อมูลเป็นตาราง (5 แถว)
contour plot 5 ชั้น จาก 800 จุด
ชั้นรัศมี (เท่าของ SD)ครอบคลุมประมาณ
ชั้นที่ 13.1099%
ชั้นที่ 22.4895%
ชั้นที่ 31.8682%
ชั้นที่ 41.2454%
ชั้นที่ 50.6217%

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

  • จำนวนชั้น: เพิ่มจำนวนชั้นแล้วจะเห็นรายละเอียดมากขึ้น แต่ก็เห็นชั้นที่เกิดจากการประมาณมากกว่าจากข้อมูลจริงด้วย

ข้อควรระวัง

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

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

  • ข้อมูลน้อยกว่า 200 จุดใช้แทน: scatter-plot
  • ต้องการทราบจำนวนจุดที่แน่นอนใช้แทน: hexbin-plot ที่นับจริง
  • ผู้อ่านไม่คุ้นกับเส้นระดับใช้แทน: hexbin-plot ที่อ่านง่ายกว่า

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

เทคนิคถัดไป · เมทริกซ์สหสัมพันธ์