01ภาพรวมของกราฟนี้
การเพิ่มเส้นแนวโน้มลงบน scatter plot ช่วยให้ผู้อ่านเห็นทิศทางกลางได้ทันทีโดยไม่ต้องกะเอง แต่ก็เป็นการอ้างอะไรบางอย่างเกี่ยวกับรูปร่างของความสัมพันธ์ จึงต้องมาพร้อมกับตัวเลขบอกว่าเส้นนั้นอธิบายข้อมูลได้ดีแค่ไหน
เปรียบเทียบให้เห็นภาพ
เหมือนลากเส้นกลางถนนบนภาพถ่ายทางอากาศของรถที่วิ่งกระจาย เส้นช่วยให้เห็นทิศทางรวม แต่ถ้ารถกระจายมากเส้นก็ไม่ได้บอกอะไรมาก
ใช้ที่ไหนบ้าง — สื่อสารแนวโน้มให้ผู้อ่านทั่วไป ตรวจว่าความสัมพันธ์เป็นเส้นตรงไหม หรือเป็นภาพประกอบผลการวิเคราะห์ถดถอย
สร้างอย่างไร
- ขั้นที่ 1วาดแผนภาพกระจายก่อน
- ขั้นที่ 2เลือกรูปแบบของเส้นที่เหมาะกับรูปร่างข้อมูล
- ขั้นที่ 3ลากเส้นและคำนวณค่าที่บอกความพอดี
- ขั้นที่ 4เขียนกำกับว่าเป็นเส้นแบบไหน
ตัวอย่างคลาสสิก · Datasaurus Dozen — สิบสามชุดข้อมูลที่สถิติเหมือนกันแต่ภาพต่างสุดขั้ว
Justin Matejka และ George Fitzmaurice ต่อยอดแนวคิดของ Anscombe ด้วยการสร้างชุดข้อมูล 13 ชุดที่มีค่าเฉลี่ย ส่วนเบี่ยงเบน และสหสัมพันธ์เท่ากันทุกตัว แต่ชุดหนึ่งเป็นรูปไดโนเสาร์ อีกชุดเป็นดาว เป็นการย้ำว่าเส้นแนวโน้มและตัวเลขสรุปไม่เคยแทนที่การดูข้อมูลจริงได้
02อธิบายภาษาคนเข้าใจง่าย ๆ
เมื่อจุดกระจายพอสมควร ผู้อ่านแต่ละคนอาจกะทิศทางกลางไม่เหมือนกัน การลากเส้นแนวโน้มจึงเป็นการบอกว่า "ทิศทางกลางตามการคำนวณคือเส้นนี้"
แต่การลากเส้นไม่ใช่การอธิบายเฉย ๆ มันคือการอ้างว่าความสัมพันธ์มีรูปร่างแบบนั้น ถ้าข้อมูลโค้งชัดเจนแต่เราลากเส้นตรง เส้นนั้นจะบิดเรื่องไปทั้งภาพ
ตัวเลขที่ต้องมาคู่กันเสมอคือค่าที่บอกว่าเส้นอธิบายข้อมูลได้ดีแค่ไหน ถ้าค่านี้ต่ำมาก เส้นที่ดูมั่นใจนั้นแทบไม่ได้บอกอะไรเลย ซึ่งเป็นข้อมูลที่สำคัญพอ ๆ กับตัวเส้น
ข้อควรระวังคือเส้นแนวโน้มทำให้ภาพดูน่าเชื่อถือขึ้นมากในสายตาคนทั่วไป จึงมีแรงจูงใจให้ใส่แม้เมื่อไม่ควรใส่ กติกาง่าย ๆ คือถ้าเรารู้สึกว่าต้องใส่เส้นเพื่อให้คนเห็นแนวโน้ม แปลว่าแนวโน้มนั้นอาจอ่อนเกินกว่าจะสรุป
อย่าลืมว่าเส้นแนวโน้มไม่ได้เปลี่ยนความจริงข้อหนึ่งเลย คือมันยังบอกไม่ได้ว่าอะไรเป็นเหตุของอะไร การมีเส้นสวย ๆ ไม่ได้ทำให้ความสัมพันธ์กลายเป็นเหตุและผล
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
x | ตัวแปรอิสระ | ตัวเลขต่อเนื่อง | x | ต่อเนื่อง | ตัวแปรที่ใช้ทำนาย |
y | ตัวแปรตาม | ตัวเลขต่อเนื่อง | y | ต่อเนื่อง | ตัวแปรที่ถูกทำนาย |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- ตัดแถวที่ขาดค่าใดค่าหนึ่ง
- ดูรูปร่างจากแผนภาพกระจายก่อนเลือกรูปแบบของเส้น
- ตรวจค่าผิดปกติที่อาจดึงเส้นทั้งเส้น
ค่าที่หายไป — ตัดแถวที่ไม่ครบและรายงานจำนวน เพราะการประมาณเส้นใช้เฉพาะแถวที่ครบ
สเกลของแกน — ถ้าใช้ log ต้องจำว่าเส้นตรงบนสเกล log หมายถึงความสัมพันธ์แบบเลขชี้กำลัง ไม่ใช่เชิงเส้น
กราฟจะเริ่มพังเมื่อไร
ถ้าข้อมูลน้อยกว่า 20 จุด เส้นแนวโน้มจะแกว่งตามจุดไม่กี่จุดและไม่ควรแสดง
หน้าตาไฟล์ที่ต้องเตรียม
x,y,group
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- ทิศทางและความชันของแนวโน้มกลาง
- ความพอดีของเส้นจากค่าที่กำกับ
- จุดที่ห่างจากเส้นมากผิดปกติ
สิ่งที่กราฟนี้ตอบไม่ได้
- ★ ไม่บอกว่า x ทำให้ y เปลี่ยน แม้เส้นจะดูมั่นใจแค่ไหน
- ไม่ควรใช้ทำนายค่าที่อยู่นอกช่วงของข้อมูลที่มี
อ่านตามลำดับนี้
- ดูกลุ่มจุดก่อนเพื่อประเมินว่าเส้นเหมาะกับรูปร่างไหม
- อ่านค่าความพอดีที่กำกับไว้
- ดูว่าจุดกระจายรอบเส้นสม่ำเสมอหรือเป็นรูปกรวย
คำถามที่ตอบได้
- ถ้า x เพิ่มหนึ่งหน่วย y เปลี่ยนประมาณเท่าไร
- เส้นนี้อธิบายข้อมูลได้ดีแค่ไหน
สัญญาณว่ามีอะไรผิด
- ลากเส้นตรงกับข้อมูลที่โค้งชัดเจน
- ไม่บอกค่าความพอดีของเส้น
- เส้นยื่นออกไปนอกช่วงของข้อมูลที่มีจริง
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- เขียนว่าเป็นเส้นแบบไหนและค่าความพอดีเท่าไร
- ไม่ลากเส้นเลยช่วงของข้อมูลจริง
- ถ้ามีช่วงความไม่แน่นอนของเส้น ให้แสดงเป็นแถบด้วย
รูปแบบชื่อกราฟที่ควรใช้
ทุก ๆ <หน่วย> ของ <x> สัมพันธ์กับ <y> ที่เปลี่ยนไป <ค่า> (R² = <ค่า>)
06เอาไปใช้ต่อ
โค้ดทุกชิ้นอ่านไฟล์ scatter-with-trend-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง
# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด scatter-with-trend-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("scatter-with-trend-sample.csv")
df["x"] = pd.to_numeric(df["x"], errors="coerce")
df["y"] = pd.to_numeric(df["y"], errors="coerce")
DEGREE = 1 # ← ค่าจาก Playground
fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)
d = df.dropna(subset=["x", "y"])
ax.scatter(d["x"], d["y"], s=30, alpha=0.6, color=C1, edgecolor=SURFACE, linewidth=0.8)
coef = np.polyfit(d["x"], d["y"], DEGREE) # DEGREE=1 เส้นตรง, 2 เส้นโค้ง
grid = np.linspace(d["x"].min(), d["x"].max(), 100) # ไม่ลากเลยช่วงข้อมูลจริง
pred = np.polyval(coef, d["x"])
r2 = 1 - ((d["y"] - pred) ** 2).sum() / ((d["y"] - d["y"].mean()) ** 2).sum()
ax.plot(grid, np.polyval(coef, grid), color=INK, linewidth=2, linestyle="--" if DEGREE == 1 else "-")
ax.set_title(f"{'เส้นตรง' if DEGREE == 1 else 'เส้นโค้งกำลังสอง'} · R² = {r2:.3f}", loc="left", fontsize=10)
ax.spines[["top", "right"]].set_visible(False) # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("scatter-with-trend.png", dpi=160)
print("เขียน scatter-with-trend.png แล้ว ·", len(df), "แถว")07ลองเล่นเพื่อเข้าใจ
ค่า r บอกได้แค่ว่าสองสิ่งขยับไปด้วยกัน ไม่ได้บอกว่าอะไรเป็นเหตุของอะไร
ดูข้อมูลเป็นตาราง (40 แถว)
| x | y | กลุ่ม |
|---|---|---|
| 20.0 | 45.0 | กลุ่มเมือง |
| 13.1 | 32.1 | กลุ่มชานเมือง |
| 20.8 | 39.0 | กลุ่มต่างจังหวัด |
| 36.4 | 59.6 | กลุ่มเมือง |
| 18.7 | 29.9 | กลุ่มชานเมือง |
| 28.3 | 34.9 | กลุ่มต่างจังหวัด |
| 46.7 | 62.8 | กลุ่มเมือง |
| 24.8 | 48.6 | กลุ่มชานเมือง |
| 45.9 | 55.7 | กลุ่มต่างจังหวัด |
| 24.6 | 40.1 | กลุ่มเมือง |
| 22.6 | 37.5 | กลุ่มชานเมือง |
| 48.8 | 54.1 | กลุ่มต่างจังหวัด |
| 13.2 | 32.3 | กลุ่มเมือง |
| 24.9 | 49.0 | กลุ่มชานเมือง |
| 57.8 | 63.1 | กลุ่มต่างจังหวัด |
| 21.9 | 39.4 | กลุ่มเมือง |
| 41.2 | 65.8 | กลุ่มชานเมือง |
| 35.1 | 42.0 | กลุ่มต่างจังหวัด |
| 10.8 | 32.6 | กลุ่มเมือง |
| 13.5 | 34.5 | กลุ่มชานเมือง |
| 43.7 | 50.0 | กลุ่มต่างจังหวัด |
| 30.3 | 49.1 | กลุ่มเมือง |
| 21.3 | 35.1 | กลุ่มชานเมือง |
| 43.1 | 48.7 | กลุ่มต่างจังหวัด |
| 8.02 | 21.9 | กลุ่มเมือง |
| 24.5 | 45.8 | กลุ่มชานเมือง |
| 52.6 | 61.3 | กลุ่มต่างจังหวัด |
| 24.5 | 47.8 | กลุ่มเมือง |
| 22.9 | 40.9 | กลุ่มชานเมือง |
| 33.2 | 41.5 | กลุ่มต่างจังหวัด |
| 45.5 | 64.9 | กลุ่มเมือง |
| 45.7 | 64.2 | กลุ่มชานเมือง |
| 30.7 | 42.8 | กลุ่มต่างจังหวัด |
| 17.0 | 34.3 | กลุ่มเมือง |
| 30.5 | 44.8 | กลุ่มชานเมือง |
| 37.2 | 48.3 | กลุ่มต่างจังหวัด |
| 23.6 | 43.0 | กลุ่มเมือง |
| 36.5 | 55.4 | กลุ่มชานเมือง |
| 47.9 | 61.6 | กลุ่มต่างจังหวัด |
| 10.4 | 33.7 | กลุ่มเมือง |
ลองแล้วจะเห็นอะไร
- รูปแบบของเส้น: สลับไปมาแล้วดูค่า R² ที่มุมขวาบน เส้นโค้งมักได้ R² สูงกว่าเสมอ ซึ่งไม่ได้แปลว่ามันถูกกว่า — ความยืดหยุ่นที่มากขึ้นย่อมพอดีกับข้อมูลมากขึ้นโดยอัตโนมัติ
ข้อควรระวัง
- ลากเส้นตรงกับข้อมูลที่โค้งชัดเจน ทำให้สรุปทิศทางผิด
- ไม่แสดงค่าความพอดี ทำให้เส้นที่อ่อนมากดูน่าเชื่อถือเท่าเส้นที่แข็งแรง
- ลากเส้นเลยช่วงของข้อมูลที่มีจริง แล้วใช้ทำนายค่าในช่วงที่ไม่เคยวัด
- ใช้เส้นที่ยืดหยุ่นกว่าเพียงเพราะได้ค่าความพอดีสูงกว่า ซึ่งเกิดขึ้นเสมอโดยอัตโนมัติ
- ตีความความชันของเส้นว่าเป็นผลเชิงสาเหตุ
เมื่อไหร่ไม่ควรใช้กราฟนี้
- ข้อมูลน้อยกว่า 20 จุด→ใช้แทน: scatter-plot เปล่า ๆ
- ความสัมพันธ์อ่อนมาก→ใช้แทน: scatter-plot เปล่า ๆ พร้อมระบุค่า r
- ข้อมูลมี noise สูงและต้องการเห็นแนวโน้ม→ใช้แทน: binned-scatter