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

กราฟกระแสน้ำStreamgraph

เห็นการขึ้นลงของหลายหมวดพร้อมกันแบบภาพรวม

4
ขั้นสูงควรรู้ก่อน · อ่านกราฟพื้นที่ซ้อนเป็น และเข้าใจว่าทำไมเส้นฐานสำคัญ
ลองเล่น

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

Streamgraph คือพื้นที่ซ้อนที่ย้ายเส้นฐานมาไว้กึ่งกลางแล้วให้แถบขยายออกสองทาง ผลคือไม่มีชั้นไหนวางบนเส้นตรงเลย แลกกับภาพรวมที่ลื่นตาและเหมาะกับการเล่าเรื่องมากกว่าการอ่านค่า

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

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

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

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

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

ตัวอย่างคลาสสิก · The Ebb and Flow of Movies ของ New York Times ปี 2008

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

The New York Times · The Ebb and Flow of Movies

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

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

ผลที่ได้คือภาพที่อ่านง่ายในแง่ "ภาพรวม" เราเห็นทันทีว่าช่วงไหนที่กระแสรวมกว้างขึ้น และหมวดไหนที่กำลังเบียดหมวดอื่นออกไป

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

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

สรุปคือใช้เมื่อเป้าหมายคือความรู้สึกและภาพรวม ไม่ใช่การอ่านค่า ถ้าผู้อ่านต้องตอบว่าหมวดนี้มีเท่าไร ต้องเปลี่ยนไปใช้พื้นที่ซ้อนธรรมดาหรือ small multiples

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

รูปทรงของข้อมูลตารางแบบยาว (หนึ่งแถวหนึ่งสังเกต)
จำนวนมิติที่เข้ารหัสได้3 มิติ
จำนวนแถวที่เหมาะ4–7 หมวด × 30–120 จุดเวลาขั้นต่ำ 30 แถว
เส้นฐานศูนย์ไม่เกี่ยวข้องเส้นฐานถูกย้ายมาไว้กึ่งกลางและเลื่อนตามยอดรวม แกนตั้งจึงไม่มีจุดอ้างอิงคงที่

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
dateเวลาเวลาx20–200 จุดยิ่งจุดเยอะขอบยิ่งลื่น
seriesหมวดหมวดหมู่color3–8 หมวดรองรับหมวดได้มากกว่าพื้นที่ซ้อนเพราะไม่ต้องอ่านค่า
valueปริมาณตัวเลขต่อเนื่องyบวกเท่านั้นต้องบวกกันได้จริง

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

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

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

สเกลของแกน — linear เท่านั้น และมักซ่อนแกนตั้งเพราะเส้นฐานเลื่อน

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

ถ้าจุดเวลาน้อยกว่า 20 ขอบจะเหลี่ยมและเสียข้อดีเรื่องความลื่น

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

date,series,value

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

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

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

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

  • ★ อ่านค่าของหมวดใดหมวดหนึ่งไม่ได้ เพราะไม่มีชั้นไหนวางบนเส้นตรง
  • แกนตั้งไม่มีความหมาย เพราะเส้นฐานเลื่อนตามยอดรวม

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

  1. ดูความกว้างรวมของกระแสก่อน
  2. ดูว่าแถบสีไหนหนาขึ้นตามเวลา
  3. อย่าพยายามอ่านค่าจากแกนตั้ง

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

  • ภาพรวมโตขึ้นไหม
  • หมวดไหนกำลังมาแรง
  • มีหมวดไหนที่โผล่ขึ้นมาแล้วหายไป

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

  • มีแกนตั้งพร้อมตัวเลข ซึ่งชวนให้อ่านค่าที่อ่านไม่ได้
  • ใช้กับหมวดที่ต้องอ่านค่าจริง

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

ผู้ชมที่เหมาะผู้ชมทั่วไปที่ต้องการภาพรวมและความรู้สึก
เวลาที่ผู้ชมต้องใช้ต้องอธิบาย
ขนาดเล็กสุดที่ยังอ่านได้480×260 px
สื่อสารกับคนทั่วไป
สไลด์ผู้บริหาร
สิ่งพิมพ์
รายงานเชิงวิเคราะห์นักวิเคราะห์ต้องอ่านค่า ซึ่งกราฟนี้ให้ไม่ได้
dashboard ที่อัปเดตตลอดไม่เหมาะกับการเหลือบดูเพื่อตัดสินใจ

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

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

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

<หมวด> ขยายส่วนแบ่งอย่างต่อเนื่องตั้งแต่ <เวลา> จนเบียด <หมวดอื่น>

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

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

# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด streamgraph-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("streamgraph-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "shape"   # ← ค่าจาก Playground

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

df["date"] = pd.to_datetime(df["date"])
pivot = df.pivot_table(index="date", columns="series", values="value", aggfunc="sum").fillna(0)
ax.stackplot(pivot.index, *[pivot[c] for c in pivot.columns], labels=list(pivot.columns),
             baseline="sym", colors=[C1, C2, C3][: len(pivot.columns)])   # sym = จัดกึ่งกลาง
ax.set_yticks([])          # แกนตั้งไม่มีความหมายเมื่อเส้นฐานเลื่อน
ax.legend(frameon=False, ncols=3, loc="upper left")
fig.autofmt_xdate()

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

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

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

ข้อมูลตัวอย่าง · 26 แถว · seed 29● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
ลองตอบว่าหมวดหนึ่งมีค่าเท่าไร
หน้าร้านเว็บไซต์แอป
ก.พ. 68เม.ย. 68มิ.ย. 68หน้าร้าน · ล่าสุด 58.3เว็บไซต์ · ล่าสุด 83.5แอป · ล่าสุด 84.4
เส้นที่นำอยู่ล่าสุดแอป
จำนวนอนุกรม3
ยอดรวมล่าสุด226
ดูข้อมูลเป็นตาราง (26 แถว)
streamgraph 3 อนุกรม 26 จุดเวลา
วันที่หน้าร้านเว็บไซต์แอป
1/1/256870.753.431.7
8/1/256871.356.131.8
15/1/256868.658.631.6
22/1/256869.357.835.5
29/1/256864.760.941.4
5/2/256864.965.040.1
12/2/256862.267.240.9
19/2/256864.467.544.3
26/2/256863.065.448.8
5/3/256862.465.752.9
12/3/256862.368.551.8
19/3/256863.372.251.1
26/3/256863.372.450.1
2/4/256860.270.852.2
9/4/256856.173.056.5
16/4/256859.677.261.7
23/4/256860.178.066.8
30/4/256858.780.267.6
7/5/256859.681.770.1
14/5/256861.880.973.8
21/5/256860.878.473.7
28/5/256860.180.175.3
4/6/256857.982.677.7
11/6/256856.083.778.3
18/6/256857.086.879.8
25/6/256858.383.584.4

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

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

ข้อควรระวัง

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

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

  • ต้องอ่านค่าของแต่ละหมวดใช้แทน: stacked-area หรือ small-multiples
  • สนใจสัดส่วนมากกว่าปริมาณใช้แทน: percent-stacked-bar
  • มีหมวดน้อยกว่า 3 หมวดใช้แทน: multi-line-chart

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

เทคนิคถัดไป · กราฟขั้นบันได