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

กราฟความชันSlope Chart

ใครขึ้นใครลง ตัดสินจากทิศของเส้นอย่างเดียว

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

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

Slope chart วางแกนตั้งสองแกนสำหรับสองจุดเวลา แล้วลากเส้นเชื่อมค่าของแต่ละรายการ ความชันของเส้นคือขนาดและทิศทางของการเปลี่ยนแปลง ทำให้ตอบคำถาม "ใครดีขึ้นใครแย่ลง" ได้ในพริบตาแม้มีหลายสิบรายการ

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

เหมือนภาพถ่ายสองใบของนักเรียนทั้งห้องเมื่อต้นปีและปลายปี แล้วลากเส้นเชื่อมคนเดิม เส้นที่พุ่งขึ้นคือคนที่พัฒนา

ใช้ที่ไหนบ้าง — เทียบคะแนนก่อนและหลังนโยบาย เทียบส่วนแบ่งตลาดสองปี หรือเทียบอันดับระหว่างสองรอบการสำรวจ

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

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

ตัวอย่างคลาสสิก · Slopegraph ของ Edward Tufte ที่เทียบภาษีต่อ GDP ของแต่ละประเทศ

Tufte แสดงสัดส่วนภาษีต่อ GDP ของ 15 ประเทศในสองปี โดยไม่มีแท่ง ไม่มี grid และไม่มีสีสัน เหลือแค่ตัวเลข ชื่อประเทศ และเส้นเชื่อม เขาชี้ว่าภาพนี้แสดงข้อมูล 30 ค่า ลำดับสองชุด และการสลับอันดับทั้งหมดในพื้นที่เท่ากับย่อหน้าเดียว

Edward Tufte · Slopegraphs

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

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

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

ข้อดีที่ทรงพลังคือทิศทางอ่านได้ก่อนตัวเลข เส้นที่เอียงขึ้นคือดีขึ้น เส้นที่เอียงลงคือแย่ลง สมองประมวลผลทิศทางได้เร็วกว่าการเทียบความยาวมาก

อีกอย่างที่เห็นฟรีคือการสลับอันดับ ถ้าเส้นสองเส้นตัดกัน แปลว่ารายการหนึ่งแซงอีกรายการ ซึ่งเป็นข้อมูลที่กราฟแท่งบอกไม่ได้เลย

ข้อจำกัดคือรองรับได้แค่สองจุดเวลา และถ้ามีรายการมากเกิน 20-25 รายการ ป้ายชื่อจะชนกันจนอ่านไม่ออก ทางแก้คือเน้นเฉพาะเส้นที่เป็นประเด็นและทำเส้นที่เหลือเป็นฉากหลังสีเทา

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

รูปทรงของข้อมูลตารางแบบกว้าง (หนึ่งแถวหนึ่ง entity)
จำนวนมิติที่เข้ารหัสได้3 มิติ
จำนวนแถวที่เหมาะ8–20 รายการขั้นต่ำ 4 แถว
เส้นฐานศูนย์เลือกได้อ่านจากความชันและตำแหน่งสัมพัทธ์ จึงย่อแกนได้ แต่ทั้งสองแกนต้องใช้สเกลเดียวกัน

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

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

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

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

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

สเกลของแกน — ใช้สเกลเดียวกันทั้งสองแกนเสมอ ถ้าใช้คนละสเกล ความชันจะไม่มีความหมาย

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

เกิน 30 รายการ เส้นจะพันกันและป้ายชนกัน — ใช้ dumbbell-chart หรือ scatter-plot แทน

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

category,before,after

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

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

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

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

  • ไม่บอกว่าเกิดอะไรขึ้นระหว่างสองจุดเวลา
  • ไม่รองรับมากกว่าสองจุดเวลา

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

  1. ดูภาพรวมว่าเส้นส่วนใหญ่เอียงทางไหน
  2. หาเส้นที่ชันที่สุดทั้งขึ้นและลง
  3. หาจุดที่เส้นตัดกันซึ่งคือการสลับอันดับ

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

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

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

  • สองแกนใช้สเกลต่างกัน ทำให้ความชันไม่มีความหมาย
  • เส้นทุกเส้นเด่นเท่ากันจนหาประเด็นไม่เจอ

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

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

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

  1. เน้นเส้นเดียวที่เป็นประเด็นด้วยสีเข้ม ที่เหลือเป็นสีเทา
  2. เขียนป้ายชื่อและค่าไว้ทั้งสองปลายเส้น
  3. เขียนชื่อจุดเวลาไว้เหนือแกนทั้งสอง

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

<จำนวน> จาก <ทั้งหมด> รายการดีขึ้นระหว่าง <เวลา> ถึง <เวลา> โดย <รายการ> เปลี่ยนมากที่สุด

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

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

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

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

df = pd.read_csv("slope-chart-sample.csv")
df["before"] = pd.to_numeric(df["before"], errors="coerce")
df["after"] = pd.to_numeric(df["after"], errors="coerce")
NOTE = "cross"   # ← ค่าจาก Playground

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

focus = (df["after"] - df["before"]).abs().idxmax()      # เส้นที่เปลี่ยนมากสุดเป็นประเด็น
for i, r in df.iterrows():
    color = C2 if i == focus else "#b5b2aa"                # เส้นอื่นเป็นสีเทาเป็นฉากหลัง
    ax.plot([0, 1], [r["before"], r["after"]], color=color, linewidth=2.4 if i == focus else 1.4)
    ax.text(-0.04, r["before"], f"{r['category']}", ha="right", va="center", fontsize=8)
    ax.text(1.04, r["after"], f"{r['after']:,.0f}", ha="left", va="center", fontsize=8)
ax.set_xticks([0, 1]); ax.set_xticklabels(["ก่อน", "หลัง"]); ax.set_xlim(-0.35, 1.35)
ax.spines[["top", "right", "bottom"]].set_visible(False)

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

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

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

ข้อมูลตัวอย่าง · 12 แถว · seed 23● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
เส้นที่ตัดกันคือการสลับอันดับ
ก่อนหลังกรุงเทพฯ56.0กรุงเทพฯ · 42.0 → 56.0เชียงใหม่53.0เชียงใหม่ · 45.0 → 53.0ขอนแก่น49.0ขอนแก่น · 48.0 → 49.0ชลบุรี63.0ชลบุรี · 47.0 → 63.0ภูเก็ต55.0ภูเก็ต · 61.0 → 55.0นครราชสีมา67.0นครราชสีมา · 64.0 → 67.0สงขลา66.0สงขลา · 59.0 → 66.0อุดรธานี68.0อุดรธานี · 71.0 → 68.0สุราษฎร์ธานี73.0สุราษฎร์ธานี · 68.0 → 73.0พิษณุโลก64.0พิษณุโลก · 67.0 → 64.0ระยอง73.0ระยอง · 69.0 → 73.0นครปฐม81.0นครปฐม · 74.0 → 81.0
เพิ่มขึ้น9 จาก 12
เปลี่ยนมากที่สุดชลบุรี · 16.0
สลับอันดับ3 หมวด
ดูข้อมูลเป็นตาราง (12 แถว)
slope chart 12 หมวด เพิ่มขึ้น 9 หมวด
หมวดก่อนหลังเปลี่ยน
กรุงเทพฯ42.056.014.0
เชียงใหม่45.053.08.00
ขอนแก่น48.049.01.00
ชลบุรี47.063.016.0
ภูเก็ต61.055.0-6.00
นครราชสีมา64.067.03.00
สงขลา59.066.07.00
อุดรธานี71.068.0-3.00
สุราษฎร์ธานี68.073.05.00
พิษณุโลก67.064.0-3.00
ระยอง69.073.04.00
นครปฐม74.081.07.00

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

  • สังเกตอะไร: หาจุดที่เส้นสองเส้นตัดกัน นั่นคือรายการที่แซงกันระหว่างสองช่วงเวลา ซึ่งกราฟแท่งกลุ่มไม่มีทางแสดงได้

ข้อควรระวัง

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

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

  • มีมากกว่าสองจุดเวลาใช้แทน: multi-line-chart หรือ bump-chart
  • ต้องการเห็นขนาดของการเปลี่ยนแปลงเป็นความยาวใช้แทน: dumbbell-chart
  • มีรายการมากกว่า 30 รายการใช้แทน: scatter-plot ที่มีแกนก่อนและแกนหลัง

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

เทคนิคถัดไป · กราฟฮอไรซัน