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

แผนภาพกระจายพร้อมเส้นแนวโน้มScatter Plot with Trend Line

ลากเส้นสรุปแนวโน้มกลางของกลุ่มจุด

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

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

การเพิ่มเส้นแนวโน้มลงบน scatter plot ช่วยให้ผู้อ่านเห็นทิศทางกลางได้ทันทีโดยไม่ต้องกะเอง แต่ก็เป็นการอ้างอะไรบางอย่างเกี่ยวกับรูปร่างของความสัมพันธ์ จึงต้องมาพร้อมกับตัวเลขบอกว่าเส้นนั้นอธิบายข้อมูลได้ดีแค่ไหน

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

เหมือนลากเส้นกลางถนนบนภาพถ่ายทางอากาศของรถที่วิ่งกระจาย เส้นช่วยให้เห็นทิศทางรวม แต่ถ้ารถกระจายมากเส้นก็ไม่ได้บอกอะไรมาก

ใช้ที่ไหนบ้าง — สื่อสารแนวโน้มให้ผู้อ่านทั่วไป ตรวจว่าความสัมพันธ์เป็นเส้นตรงไหม หรือเป็นภาพประกอบผลการวิเคราะห์ถดถอย

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

  1. ขั้นที่ 1วาดแผนภาพกระจายก่อน
  2. ขั้นที่ 2เลือกรูปแบบของเส้นที่เหมาะกับรูปร่างข้อมูล
  3. ขั้นที่ 3ลากเส้นและคำนวณค่าที่บอกความพอดี
  4. ขั้นที่ 4เขียนกำกับว่าเป็นเส้นแบบไหน

ตัวอย่างคลาสสิก · Datasaurus Dozen — สิบสามชุดข้อมูลที่สถิติเหมือนกันแต่ภาพต่างสุดขั้ว

Justin Matejka และ George Fitzmaurice ต่อยอดแนวคิดของ Anscombe ด้วยการสร้างชุดข้อมูล 13 ชุดที่มีค่าเฉลี่ย ส่วนเบี่ยงเบน และสหสัมพันธ์เท่ากันทุกตัว แต่ชุดหนึ่งเป็นรูปไดโนเสาร์ อีกชุดเป็นดาว เป็นการย้ำว่าเส้นแนวโน้มและตัวเลขสรุปไม่เคยแทนที่การดูข้อมูลจริงได้

Autodesk Research · Same Stats, Different Graphs

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

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

แต่การลากเส้นไม่ใช่การอธิบายเฉย ๆ มันคือการอ้างว่าความสัมพันธ์มีรูปร่างแบบนั้น ถ้าข้อมูลโค้งชัดเจนแต่เราลากเส้นตรง เส้นนั้นจะบิดเรื่องไปทั้งภาพ

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

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

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

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

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

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

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

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

  1. ตัดแถวที่ขาดค่าใดค่าหนึ่ง
  2. ดูรูปร่างจากแผนภาพกระจายก่อนเลือกรูปแบบของเส้น
  3. ตรวจค่าผิดปกติที่อาจดึงเส้นทั้งเส้น

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

สเกลของแกน — ถ้าใช้ log ต้องจำว่าเส้นตรงบนสเกล log หมายถึงความสัมพันธ์แบบเลขชี้กำลัง ไม่ใช่เชิงเส้น

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

ถ้าข้อมูลน้อยกว่า 20 จุด เส้นแนวโน้มจะแกว่งตามจุดไม่กี่จุดและไม่ควรแสดง

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

x,y,group

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

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

  • ทิศทางและความชันของแนวโน้มกลาง
  • ความพอดีของเส้นจากค่าที่กำกับ
  • จุดที่ห่างจากเส้นมากผิดปกติ

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

  • ★ ไม่บอกว่า x ทำให้ y เปลี่ยน แม้เส้นจะดูมั่นใจแค่ไหน
  • ไม่ควรใช้ทำนายค่าที่อยู่นอกช่วงของข้อมูลที่มี

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

  1. ดูกลุ่มจุดก่อนเพื่อประเมินว่าเส้นเหมาะกับรูปร่างไหม
  2. อ่านค่าความพอดีที่กำกับไว้
  3. ดูว่าจุดกระจายรอบเส้นสม่ำเสมอหรือเป็นรูปกรวย

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

  • ถ้า x เพิ่มหนึ่งหน่วย y เปลี่ยนประมาณเท่าไร
  • เส้นนี้อธิบายข้อมูลได้ดีแค่ไหน

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

  • ลากเส้นตรงกับข้อมูลที่โค้งชัดเจน
  • ไม่บอกค่าความพอดีของเส้น
  • เส้นยื่นออกไปนอกช่วงของข้อมูลที่มีจริง

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

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

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

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

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

ทุก ๆ <หน่วย> ของ <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), "แถว")

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

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

ข้อมูลตัวอย่าง · 120 แถว · seed 127● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
เลือกรูปแบบที่จะลากผ่านข้อมูล
กลุ่มเมืองกลุ่มชานเมืองกลุ่มต่างจังหวัด
20.030.040.050.060.070.080.0ตัวแปรที่อยากอธิบาย (y)0.0010.020.030.040.050.060.0ตัวแปรที่คิดว่ามาก่อน (x)x 20.0 · y 45.0 · กลุ่มเมืองx 13.1 · y 32.1 · กลุ่มชานเมืองx 20.8 · y 39.0 · กลุ่มต่างจังหวัดx 36.4 · y 59.6 · กลุ่มเมืองx 18.7 · y 29.9 · กลุ่มชานเมืองx 28.3 · y 34.9 · กลุ่มต่างจังหวัดx 46.7 · y 62.8 · กลุ่มเมืองx 24.8 · y 48.6 · กลุ่มชานเมืองx 45.9 · y 55.7 · กลุ่มต่างจังหวัดx 24.6 · y 40.1 · กลุ่มเมืองx 22.6 · y 37.5 · กลุ่มชานเมืองx 48.8 · y 54.1 · กลุ่มต่างจังหวัดx 13.2 · y 32.3 · กลุ่มเมืองx 24.9 · y 49.0 · กลุ่มชานเมืองx 57.8 · y 63.1 · กลุ่มต่างจังหวัดx 21.9 · y 39.4 · กลุ่มเมืองx 41.2 · y 65.8 · กลุ่มชานเมืองx 35.1 · y 42.0 · กลุ่มต่างจังหวัดx 10.8 · y 32.6 · กลุ่มเมืองx 13.5 · y 34.5 · กลุ่มชานเมืองx 43.7 · y 50.0 · กลุ่มต่างจังหวัดx 30.3 · y 49.1 · กลุ่มเมืองx 21.3 · y 35.1 · กลุ่มชานเมืองx 43.1 · y 48.7 · กลุ่มต่างจังหวัดx 8.02 · y 21.9 · กลุ่มเมืองx 24.5 · y 45.8 · กลุ่มชานเมืองx 52.6 · y 61.3 · กลุ่มต่างจังหวัดx 24.5 · y 47.8 · กลุ่มเมืองx 22.9 · y 40.9 · กลุ่มชานเมืองx 33.2 · y 41.5 · กลุ่มต่างจังหวัดx 45.5 · y 64.9 · กลุ่มเมืองx 45.7 · y 64.2 · กลุ่มชานเมืองx 30.7 · y 42.8 · กลุ่มต่างจังหวัดx 17.0 · y 34.3 · กลุ่มเมืองx 30.5 · y 44.8 · กลุ่มชานเมืองx 37.2 · y 48.3 · กลุ่มต่างจังหวัดx 23.6 · y 43.0 · กลุ่มเมืองx 36.5 · y 55.4 · กลุ่มชานเมืองx 47.9 · y 61.6 · กลุ่มต่างจังหวัดx 10.4 · y 33.7 · กลุ่มเมืองx 29.6 · y 43.3 · กลุ่มชานเมืองx 45.5 · y 54.9 · กลุ่มต่างจังหวัดx 49.9 · y 73.7 · กลุ่มเมืองx 42.9 · y 58.2 · กลุ่มชานเมืองx 32.6 · y 53.3 · กลุ่มต่างจังหวัดx 45.8 · y 75.0 · กลุ่มเมืองx 12.8 · y 27.8 · กลุ่มชานเมืองx 31.5 · y 43.7 · กลุ่มต่างจังหวัดx 42.9 · y 70.8 · กลุ่มเมืองx 31.2 · y 48.8 · กลุ่มชานเมืองx 25.2 · y 44.0 · กลุ่มต่างจังหวัดx 14.0 · y 32.9 · กลุ่มเมืองx 40.7 · y 55.3 · กลุ่มชานเมืองx 33.3 · y 44.5 · กลุ่มต่างจังหวัดx 21.8 · y 42.3 · กลุ่มเมืองx 29.1 · y 53.2 · กลุ่มชานเมืองx 23.0 · y 37.0 · กลุ่มต่างจังหวัดx 24.1 · y 35.9 · กลุ่มเมืองx 21.9 · y 42.3 · กลุ่มชานเมืองx 36.7 · y 49.9 · กลุ่มต่างจังหวัดx 30.8 · y 54.1 · กลุ่มเมืองx 27.6 · y 41.6 · กลุ่มชานเมืองx 49.4 · y 53.0 · กลุ่มต่างจังหวัดx 30.4 · y 50.9 · กลุ่มเมืองx 52.2 · y 77.7 · กลุ่มชานเมืองx 40.5 · y 53.2 · กลุ่มต่างจังหวัดx 34.6 · y 65.3 · กลุ่มเมืองx 22.8 · y 41.3 · กลุ่มชานเมืองx 25.8 · y 45.4 · กลุ่มต่างจังหวัดx 17.9 · y 42.1 · กลุ่มเมืองx 53.0 · y 68.9 · กลุ่มชานเมืองx 50.4 · y 58.7 · กลุ่มต่างจังหวัดx 37.1 · y 59.1 · กลุ่มเมืองx 27.5 · y 47.0 · กลุ่มชานเมืองx 34.8 · y 47.4 · กลุ่มต่างจังหวัดx 33.3 · y 55.9 · กลุ่มเมืองx 52.6 · y 60.6 · กลุ่มชานเมืองx 43.9 · y 48.3 · กลุ่มต่างจังหวัดx 45.2 · y 73.3 · กลุ่มเมืองx 13.6 · y 26.9 · กลุ่มชานเมืองx 50.0 · y 55.6 · กลุ่มต่างจังหวัดx 18.5 · y 37.2 · กลุ่มเมืองx 19.0 · y 40.9 · กลุ่มชานเมืองx 33.1 · y 45.4 · กลุ่มต่างจังหวัดx 46.1 · y 76.9 · กลุ่มเมืองx 33.8 · y 46.6 · กลุ่มชานเมืองx 57.7 · y 55.1 · กลุ่มต่างจังหวัดx 35.9 · y 55.2 · กลุ่มเมืองx 41.3 · y 54.4 · กลุ่มชานเมืองx 55.5 · y 55.1 · กลุ่มต่างจังหวัดx 38.8 · y 68.9 · กลุ่มเมืองx 22.4 · y 38.1 · กลุ่มชานเมืองx 23.4 · y 25.1 · กลุ่มต่างจังหวัดx 44.5 · y 61.6 · กลุ่มเมืองx 21.0 · y 41.0 · กลุ่มชานเมืองx 53.0 · y 54.3 · กลุ่มต่างจังหวัดx 37.7 · y 65.0 · กลุ่มเมืองx 27.1 · y 46.9 · กลุ่มชานเมืองx 39.0 · y 46.9 · กลุ่มต่างจังหวัดx 25.0 · y 45.3 · กลุ่มเมืองx 47.5 · y 63.8 · กลุ่มชานเมืองx 57.2 · y 66.6 · กลุ่มต่างจังหวัดx 46.9 · y 65.4 · กลุ่มเมืองx 35.9 · y 52.8 · กลุ่มชานเมืองx 21.6 · y 25.1 · กลุ่มต่างจังหวัดx 34.3 · y 55.2 · กลุ่มเมืองx 31.1 · y 46.0 · กลุ่มชานเมืองx 52.2 · y 57.2 · กลุ่มต่างจังหวัดx 47.3 · y 68.2 · กลุ่มเมืองx 25.7 · y 46.4 · กลุ่มชานเมืองx 55.9 · y 73.4 · กลุ่มต่างจังหวัดx 21.1 · y 43.3 · กลุ่มเมืองx 21.7 · y 31.9 · กลุ่มชานเมืองx 18.7 · y 30.0 · กลุ่มต่างจังหวัดx 39.8 · y 60.0 · กลุ่มเมืองx 24.9 · y 41.7 · กลุ่มชานเมืองx 34.1 · y 45.4 · กลุ่มต่างจังหวัดx 32.4 · y 60.8 · กลุ่มเมืองx 35.8 · y 56.4 · กลุ่มชานเมืองx 32.8 · y 45.2 · กลุ่มต่างจังหวัดเส้นตรง · R² 0.703

ค่า r บอกได้แค่ว่าสองสิ่งขยับไปด้วยกัน ไม่ได้บอกว่าอะไรเป็นเหตุของอะไร

สหสัมพันธ์ (r)0.838
จำนวนจุด120
0.703
ดูข้อมูลเป็นตาราง (40 แถว)
แผนภาพกระจาย 120 จุด สหสัมพันธ์ 0.84
xyกลุ่ม
20.045.0กลุ่มเมือง
13.132.1กลุ่มชานเมือง
20.839.0กลุ่มต่างจังหวัด
36.459.6กลุ่มเมือง
18.729.9กลุ่มชานเมือง
28.334.9กลุ่มต่างจังหวัด
46.762.8กลุ่มเมือง
24.848.6กลุ่มชานเมือง
45.955.7กลุ่มต่างจังหวัด
24.640.1กลุ่มเมือง
22.637.5กลุ่มชานเมือง
48.854.1กลุ่มต่างจังหวัด
13.232.3กลุ่มเมือง
24.949.0กลุ่มชานเมือง
57.863.1กลุ่มต่างจังหวัด
21.939.4กลุ่มเมือง
41.265.8กลุ่มชานเมือง
35.142.0กลุ่มต่างจังหวัด
10.832.6กลุ่มเมือง
13.534.5กลุ่มชานเมือง
43.750.0กลุ่มต่างจังหวัด
30.349.1กลุ่มเมือง
21.335.1กลุ่มชานเมือง
43.148.7กลุ่มต่างจังหวัด
8.0221.9กลุ่มเมือง
24.545.8กลุ่มชานเมือง
52.661.3กลุ่มต่างจังหวัด
24.547.8กลุ่มเมือง
22.940.9กลุ่มชานเมือง
33.241.5กลุ่มต่างจังหวัด
45.564.9กลุ่มเมือง
45.764.2กลุ่มชานเมือง
30.742.8กลุ่มต่างจังหวัด
17.034.3กลุ่มเมือง
30.544.8กลุ่มชานเมือง
37.248.3กลุ่มต่างจังหวัด
23.643.0กลุ่มเมือง
36.555.4กลุ่มชานเมือง
47.961.6กลุ่มต่างจังหวัด
10.433.7กลุ่มเมือง

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

  • รูปแบบของเส้น: สลับไปมาแล้วดูค่า R² ที่มุมขวาบน เส้นโค้งมักได้ R² สูงกว่าเสมอ ซึ่งไม่ได้แปลว่ามันถูกกว่า — ความยืดหยุ่นที่มากขึ้นย่อมพอดีกับข้อมูลมากขึ้นโดยอัตโนมัติ

ข้อควรระวัง

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

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

  • ข้อมูลน้อยกว่า 20 จุดใช้แทน: scatter-plot เปล่า ๆ
  • ความสัมพันธ์อ่อนมากใช้แทน: scatter-plot เปล่า ๆ พร้อมระบุค่า r
  • ข้อมูลมี noise สูงและต้องการเห็นแนวโน้มใช้แทน: binned-scatter

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

เทคนิคถัดไป · กราฟแท่ง