01ภาพรวมของกราฟนี้
Slope chart วางแกนตั้งสองแกนสำหรับสองจุดเวลา แล้วลากเส้นเชื่อมค่าของแต่ละรายการ ความชันของเส้นคือขนาดและทิศทางของการเปลี่ยนแปลง ทำให้ตอบคำถาม "ใครดีขึ้นใครแย่ลง" ได้ในพริบตาแม้มีหลายสิบรายการ
เปรียบเทียบให้เห็นภาพ
เหมือนภาพถ่ายสองใบของนักเรียนทั้งห้องเมื่อต้นปีและปลายปี แล้วลากเส้นเชื่อมคนเดิม เส้นที่พุ่งขึ้นคือคนที่พัฒนา
ใช้ที่ไหนบ้าง — เทียบคะแนนก่อนและหลังนโยบาย เทียบส่วนแบ่งตลาดสองปี หรือเทียบอันดับระหว่างสองรอบการสำรวจ
สร้างอย่างไร
- ขั้นที่ 1เลือกสองจุดเวลาที่มีความหมาย
- ขั้นที่ 2วาดแกนตั้งสองแกน
- ขั้นที่ 3ลากเส้นเชื่อมค่าของแต่ละรายการ
- ขั้นที่ 4เน้นเส้นที่เป็นประเด็นและทำเส้นอื่นเป็นสีเทา
ตัวอย่างคลาสสิก · Slopegraph ของ Edward Tufte ที่เทียบภาษีต่อ GDP ของแต่ละประเทศ
Tufte แสดงสัดส่วนภาษีต่อ GDP ของ 15 ประเทศในสองปี โดยไม่มีแท่ง ไม่มี grid และไม่มีสีสัน เหลือแค่ตัวเลข ชื่อประเทศ และเส้นเชื่อม เขาชี้ว่าภาพนี้แสดงข้อมูล 30 ค่า ลำดับสองชุด และการสลับอันดับทั้งหมดในพื้นที่เท่ากับย่อหน้าเดียว
02อธิบายภาษาคนเข้าใจง่าย ๆ
เมื่อมีข้อมูลแค่สองจุดเวลาแต่มีหลายสิบรายการ กราฟเส้นเต็มรูปแบบจะกลายเป็นก้อนเส้นพันกัน และกราฟแท่งกลุ่มก็จะมีแท่งเป็นร้อย
Slope chart ตัดทุกอย่างที่ไม่จำเป็นออก เหลือแค่แกนตั้งสองแกนและเส้นตรงเชื่อมระหว่างกัน ความชันของเส้นบอกทุกอย่างที่ต้องการรู้
ข้อดีที่ทรงพลังคือทิศทางอ่านได้ก่อนตัวเลข เส้นที่เอียงขึ้นคือดีขึ้น เส้นที่เอียงลงคือแย่ลง สมองประมวลผลทิศทางได้เร็วกว่าการเทียบความยาวมาก
อีกอย่างที่เห็นฟรีคือการสลับอันดับ ถ้าเส้นสองเส้นตัดกัน แปลว่ารายการหนึ่งแซงอีกรายการ ซึ่งเป็นข้อมูลที่กราฟแท่งบอกไม่ได้เลย
ข้อจำกัดคือรองรับได้แค่สองจุดเวลา และถ้ามีรายการมากเกิน 20-25 รายการ ป้ายชื่อจะชนกันจนอ่านไม่ออก ทางแก้คือเน้นเฉพาะเส้นที่เป็นประเด็นและทำเส้นที่เหลือเป็นฉากหลังสีเทา
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
category | รายการ | หมวดหมู่ | color | 5–25 รายการ | เกิน 25 รายการป้ายจะชนกัน |
before | ค่าที่จุดเวลาแรก | ตัวเลขต่อเนื่อง | y | ต่อเนื่อง | ต้องเป็นหน่วยเดียวกับ after |
after | ค่าที่จุดเวลาที่สอง | ตัวเลขต่อเนื่อง | y | ต่อเนื่อง | คู่กับ before |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- จัดข้อมูลเป็นแบบกว้าง
- ตรวจว่าทั้งสองค่าเป็นหน่วยเดียวกัน
- ตัดสินใจว่าเส้นไหนเป็นประเด็นที่จะเน้น
ค่าที่หายไป — รายการที่ขาดค่าใดค่าหนึ่งลากเส้นไม่ได้ ต้องแยกออกและระบุจำนวนที่ตัดไป
สเกลของแกน — ใช้สเกลเดียวกันทั้งสองแกนเสมอ ถ้าใช้คนละสเกล ความชันจะไม่มีความหมาย
กราฟจะเริ่มพังเมื่อไร
เกิน 30 รายการ เส้นจะพันกันและป้ายชนกัน — ใช้ dumbbell-chart หรือ scatter-plot แทน
หน้าตาไฟล์ที่ต้องเตรียม
category,before,after
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- รายการที่เพิ่มขึ้นและลดลง จากทิศของเส้น
- รายการที่เปลี่ยนมากที่สุด จากความชัน
- การสลับอันดับ จากจุดที่เส้นตัดกัน
- ภาพรวมว่าส่วนใหญ่ไปทางไหน
สิ่งที่กราฟนี้ตอบไม่ได้
- ไม่บอกว่าเกิดอะไรขึ้นระหว่างสองจุดเวลา
- ไม่รองรับมากกว่าสองจุดเวลา
อ่านตามลำดับนี้
- ดูภาพรวมว่าเส้นส่วนใหญ่เอียงทางไหน
- หาเส้นที่ชันที่สุดทั้งขึ้นและลง
- หาจุดที่เส้นตัดกันซึ่งคือการสลับอันดับ
คำถามที่ตอบได้
- ใครดีขึ้นและใครแย่ลง
- มีการสลับอันดับหรือไม่
- แนวโน้มโดยรวมของกลุ่มเป็นอย่างไร
สัญญาณว่ามีอะไรผิด
- สองแกนใช้สเกลต่างกัน ทำให้ความชันไม่มีความหมาย
- เส้นทุกเส้นเด่นเท่ากันจนหาประเด็นไม่เจอ
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- เน้นเส้นเดียวที่เป็นประเด็นด้วยสีเข้ม ที่เหลือเป็นสีเทา
- เขียนป้ายชื่อและค่าไว้ทั้งสองปลายเส้น
- เขียนชื่อจุดเวลาไว้เหนือแกนทั้งสอง
รูปแบบชื่อกราฟที่ควรใช้
<จำนวน> จาก <ทั้งหมด> รายการดีขึ้นระหว่าง <เวลา> ถึง <เวลา> โดย <รายการ> เปลี่ยนมากที่สุด
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), "แถว")07ลองเล่นเพื่อเข้าใจ
ดูข้อมูลเป็นตาราง (12 แถว)
| หมวด | ก่อน | หลัง | เปลี่ยน |
|---|---|---|---|
| กรุงเทพฯ | 42.0 | 56.0 | 14.0 |
| เชียงใหม่ | 45.0 | 53.0 | 8.00 |
| ขอนแก่น | 48.0 | 49.0 | 1.00 |
| ชลบุรี | 47.0 | 63.0 | 16.0 |
| ภูเก็ต | 61.0 | 55.0 | -6.00 |
| นครราชสีมา | 64.0 | 67.0 | 3.00 |
| สงขลา | 59.0 | 66.0 | 7.00 |
| อุดรธานี | 71.0 | 68.0 | -3.00 |
| สุราษฎร์ธานี | 68.0 | 73.0 | 5.00 |
| พิษณุโลก | 67.0 | 64.0 | -3.00 |
| ระยอง | 69.0 | 73.0 | 4.00 |
| นครปฐม | 74.0 | 81.0 | 7.00 |
ลองแล้วจะเห็นอะไร
- สังเกตอะไร: หาจุดที่เส้นสองเส้นตัดกัน นั่นคือรายการที่แซงกันระหว่างสองช่วงเวลา ซึ่งกราฟแท่งกลุ่มไม่มีทางแสดงได้
ข้อควรระวัง
- ใช้สเกลต่างกันระหว่างสองแกน ทำให้ความชันไม่มีความหมาย
- ใส่รายการมากเกินไปจนเส้นพันกันและป้ายชนกัน
- ให้ทุกเส้นเด่นเท่ากันทั้งที่มีเส้นเดียวที่เป็นประเด็น
- ใช้กับสองจุดเวลาที่ห่างกันมากโดยไม่บอกว่าระหว่างนั้นเกิดอะไรขึ้น
เมื่อไหร่ไม่ควรใช้กราฟนี้
- มีมากกว่าสองจุดเวลา→ใช้แทน: multi-line-chart หรือ bump-chart
- ต้องการเห็นขนาดของการเปลี่ยนแปลงเป็นความยาว→ใช้แทน: dumbbell-chart
- มีรายการมากกว่า 30 รายการ→ใช้แทน: scatter-plot ที่มีแกนก่อนและแกนหลัง