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

กราฟเครือข่ายForce-Directed Graph

เห็นว่าใครเชื่อมกับใครเมื่อความสัมพันธ์ไม่ใช่ต้นไม้

4
ขั้นสูงควรรู้ก่อน · เข้าใจว่าข้อมูลเครือข่ายต่างจากข้อมูลตาราง
ลองเล่น

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

Force-directed graph วางโหนดด้วยการจำลองแรงทางฟิสิกส์ ทำให้กลุ่มที่เชื่อมกันหนาแน่นรวมตัวกันโดยอัตโนมัติ เหมาะกับการเห็นโครงสร้างภาพรวมและหาโหนดศูนย์กลาง แต่ตำแหน่งที่ได้ไม่มีความหมายในตัวเองและเปลี่ยนทุกครั้งที่รันใหม่

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

เหมือนโยนลูกปัดที่ผูกด้วยยางลงบนโต๊ะแล้วปล่อยให้มันจัดตัวเอง ลูกที่ผูกกันหลายเส้นจะถูกดึงมาอยู่ใกล้กัน

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

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

  1. ขั้นที่ 1เตรียมรายการเส้นเชื่อมแบบ source–target
  2. ขั้นที่ 2ตั้งแรงดึงตามเส้นเชื่อมและแรงผลักระหว่างโหนด
  3. ขั้นที่ 3ปล่อยให้ระบบเข้าสู่สมดุล
  4. ขั้นที่ 4ใช้ขนาดโหนดบอกจำนวนเส้นเชื่อม

ตัวอย่างคลาสสิก · เครือข่ายการติดต่อในการสอบสวนโรคระบาด

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

Wikipedia · Social network analysis

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

ข้อมูลเครือข่ายไม่ใช่ต้นไม้ เพราะโหนดหนึ่งมีพ่อแม่ได้หลายตัวและอาจมีวงจร จึงวางเป็นชั้น ๆ แบบต้นไม้ไม่ได้

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

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

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

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

อย่างที่สามคือเมื่อเส้นเชื่อมเยอะเกินราวสองร้อยเส้น ภาพจะกลายเป็นก้อนขนแมวที่อ่านอะไรไม่ได้เลย ซึ่งตอนนั้นต้องเปลี่ยนไปใช้ adjacency matrix แทน

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

รูปทรงของข้อมูลรายการเส้นเชื่อม source–target–value
จำนวนมิติที่เข้ารหัสได้3 มิติ
จำนวนแถวที่เหมาะ20–120 เส้นเชื่อมขั้นต่ำ 5 แถว
เส้นฐานศูนย์ไม่เกี่ยวข้องไม่มีแกนค่า ตำแหน่งเกิดจากการจำลองแรง

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
sourceโหนดต้นทางโหนดpath10–150 โหนดเป็นข้อความชื่อโหนด
targetโหนดปลายทางโหนดpathชุดเดียวกับ sourceโหนดเดียวกันปรากฏได้ทั้งสองฝั่ง
value
ไม่บังคับ
น้ำหนักความสัมพันธ์ตัวเลขต่อเนื่องsizeบวกเท่านั้นใช้ควบคุมความหนาของเส้น

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

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

ค่าที่หายไป — เส้นที่อ้างโหนดไม่ครบจะถูกตัดทิ้ง ต้องรายงานจำนวนที่ตัด

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

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

เกิน 200 เส้นเชื่อม ภาพกลายเป็นก้อนขนแมวที่อ่านไม่ได้

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

source,target,value
เข้าชมเว็บ,ดูสินค้า,7975
เข้าชมเว็บ,ออกทันที,4078

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

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

  • กลุ่มย่อยที่เชื่อมกันหนาแน่น
  • โหนดศูนย์กลางที่มีเส้นเชื่อมมากที่สุด
  • โหนดสะพานที่เชื่อมสองกลุ่มเข้าด้วยกัน
  • โหนดโดดเดี่ยวที่ไม่เชื่อมกับใคร

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

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

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

  1. มองหากระจุกของโหนดก่อนเพื่อรู้ว่ามีกี่กลุ่ม
  2. ดูโหนดที่ใหญ่ที่สุดซึ่งมีเส้นเชื่อมมากสุด
  3. มองหาโหนดที่อยู่ระหว่างกระจุกซึ่งทำหน้าที่สะพาน

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

  • เครือข่ายนี้แบ่งเป็นกี่กลุ่ม
  • ใครเป็นศูนย์กลาง
  • มีใครทำหน้าที่เป็นสะพานระหว่างกลุ่ม

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

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

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

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

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

  1. เขียนกำกับเสมอว่าตำแหน่งไม่มีความหมาย
  2. ติดชื่อเฉพาะโหนดศูนย์กลางและโหนดสะพาน
  3. ใช้สีบอกกลุ่มย่อยที่คำนวณมาก่อน ไม่ใช่สีตามตำแหน่ง

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

เครือข่าย <จำนวน> โหนด แบ่งเป็น <จำนวน> กลุ่ม โดยมี <โหนด> เป็นศูนย์กลาง

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

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

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

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

df = pd.read_csv("force-directed-graph-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "position"   # ← ค่าจาก Playground

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

G = nx.from_pandas_edgelist(df, "source", "target", edge_attr="value")
pos = nx.spring_layout(G, seed=42)                      # seed ล็อกไว้เพื่อให้ผลซ้ำได้
deg = dict(G.degree())
nx.draw_networkx_edges(G, pos, ax=ax, edge_color="#c9c6bd",
                       width=[0.6 + d["value"] / df["value"].max() * 3 for *_, d in G.edges(data=True)])
nx.draw_networkx_nodes(G, pos, ax=ax, node_color=C1,
                       node_size=[120 + deg[n] ** 0.5 * 180 for n in G])
nx.draw_networkx_labels(G, pos, ax=ax, font_size=8)
ax.set_axis_off()

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

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

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

ข้อมูลตัวอย่าง · 8 แถว · seed 239● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
ตำแหน่งมาจากการจำลองแรง
เข้าชมเว็บเข้าชมเว็บ · เชื่อมกับ 2 โหนดดูสินค้าดูสินค้า · เชื่อมกับ 3 โหนดออกทันทีออกทันที · เชื่อมกับ 1 โหนดใส่ตะกร้าใส่ตะกร้า · เชื่อมกับ 3 โหนดออกกลางทางออกกลางทาง · เชื่อมกับ 1 โหนดกรอกที่อยู่กรอกที่อยู่ · เชื่อมกับ 3 โหนดทิ้งตะกร้าทิ้งตะกร้า · เชื่อมกับ 1 โหนดชำระเงินสำเรชำระเงินสำเร็จ · เชื่อมกับ 1 โหนดชำระไม่สำเร็ชำระไม่สำเร็จ · เชื่อมกับ 1 โหนด

ตำแหน่งของโหนดไม่มีความหมายในตัวเอง ระยะห่างบนภาพไม่ใช่ระยะจริงในเครือข่าย

จำนวนโหนด9
จำนวนเส้นเชื่อม8
โหนดที่เชื่อมมากที่สุดดูสินค้า · 3 เส้น
ดูข้อมูลเป็นตาราง (9 แถว)
กราฟเครือข่าย 9 โหนด 8 เส้นเชื่อม
โหนดจำนวนเส้นเชื่อม
ดูสินค้า3
ใส่ตะกร้า3
กรอกที่อยู่3
เข้าชมเว็บ2
ออกทันที1
ออกกลางทาง1
ทิ้งตะกร้า1
ชำระเงินสำเร็จ1
ชำระไม่สำเร็จ1

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

  • สังเกตอะไร: ลองถามตัวเองว่าโหนดที่อยู่มุมขวาบนแปลว่าอะไร — คำตอบคือไม่แปลว่าอะไรเลย นี่คือกับดักที่ผู้อ่านมักตกเสมอ

ข้อควรระวัง

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

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

  • เครือข่ายหนาแน่นเกิน 200 เส้นเชื่อมใช้แทน: adjacency-matrix
  • โครงสร้างเป็นต้นไม้จริง ๆ ไม่มีวงจรใช้แทน: node-link-tree
  • สนใจปริมาณที่ไหลระหว่างโหนดใช้แทน: sankey-diagram

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

เทคนิคถัดไป · เมทริกซ์ความเชื่อมโยง