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

ต้นไม้รัศมีRadial Tree

ใส่ต้นไม้ที่ใบเยอะลงในพื้นที่สี่เหลี่ยมจัตุรัส

4
ขั้นสูงควรรู้ก่อน · อ่านแผนภาพต้นไม้เป็น
ลองเล่น

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

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

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

เหมือนมองรากไม้จากด้านบนที่แผ่ออกทุกทิศ แทนที่จะมองต้นไม้จากด้านข้างที่กิ่งแผ่ได้แค่สองทาง

ใช้ที่ไหนบ้าง — โครงสร้างหมวดหมู่ที่มีใบจำนวนมาก ต้นไม้วิวัฒนาการของสิ่งมีชีวิต หรือโครงสร้างไฟล์ที่ลึกและกว้าง

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

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

ตัวอย่างคลาสสิก · ต้นไม้วิวัฒนาการของสิ่งมีชีวิตในงานชีววิทยา

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

Wikipedia · Phylogenetic tree

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

ต้นไม้แนวตรงมีข้อจำกัดชัดเจน คือความกว้างของภาพถูกกำหนดโดยจำนวนใบ ถ้ามี 100 ใบ ภาพก็ต้องกว้าง 100 ช่อง ซึ่งเกินหน้าจอแน่นอน

Radial tree แก้ด้วยเรขาคณิต เมื่อดัดให้แผ่เป็นวง ที่ว่างสำหรับใบก็คือเส้นรอบวง ซึ่งโตตามรัศมี ดังนั้นยิ่งต้นไม้ลึกยิ่งมีที่ให้ใบมาก

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

ราคาที่จ่ายอย่างแรกคือการรับรู้ระยะ โหนดที่อยู่ชั้นนอกดูห่างกันมากกว่าโหนดชั้นในที่อยู่ห่างกันเท่ากันในเชิงโครงสร้าง เพราะเส้นรอบวงยาวกว่า

อย่างที่สองคือป้ายชื่อ ชื่อที่วางตามรัศมีจะเอียงไปทุกทิศ ผู้อ่านต้องหมุนหัวตาม ซึ่งช้ากว่าการอ่านชื่อที่เรียงแนวนอนอย่างมาก

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

รูปทรงของข้อมูลลำดับชั้น parent–child
จำนวนมิติที่เข้ารหัสได้2 มิติ
จำนวนแถวที่เหมาะ30–150 โหนดขั้นต่ำ 10 แถว
เส้นฐานศูนย์ไม่เกี่ยวข้องไม่มีแกนค่า ตำแหน่งสื่อโครงสร้าง

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
idชื่อโหนดโหนดangle15–200 โหนดรองรับโหนดได้มากกว่าต้นไม้แนวตรง
parentโหนดแม่โหนดpathอ้างถึง id ที่มีจริงรากอยู่ตรงกลางวง
value
ไม่บังคับ
ขนาดตัวเลขต่อเนื่องsizeบวกเท่านั้นไม่บังคับ

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

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

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

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

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

เกิน 250 โหนด ใบจะเบียดกันรอบวงจนชื่อชนกัน

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

id,parent,value
บริษัท,,0
สินค้าอุปโภค,บริษัท,0

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

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

  • ความลึกของโครงสร้างจากจำนวนวง
  • สาขาที่กินมุมกว้างซึ่งคือสาขาที่มีใบมาก
  • ความสมมาตรหรือไม่สมมาตรของโครงสร้าง

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

  • ★ ระยะที่เห็นระหว่างโหนดชั้นนอกดูห่างกว่าความจริงเพราะเส้นรอบวงยาวกว่า
  • อ่านชื่อได้ช้ากว่าต้นไม้แนวตรงมากเพราะป้ายเอียงไปทุกทิศ

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

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

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

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

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

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

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

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

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

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

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

โครงสร้าง <จำนวน> โหนด ลึก <จำนวน> ชั้น โดยสาขา <สาขา> ใหญ่ที่สุด

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

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

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

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

df = pd.read_csv("radial-tree-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "space"   # ← ค่าจาก Playground

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

fig.clf()
ax = fig.add_subplot(projection="polar")
G = nx.DiGraph()
for _, r in df.iterrows():
    if isinstance(r["parent"], str) and r["parent"]:
        G.add_edge(r["parent"], r["id"])
root = [n for n in G if G.in_degree(n) == 0][0]
depth = nx.shortest_path_length(G, root)
leaves = [n for n in G if G.out_degree(n) == 0]
angle = {n: 2 * np.pi * i / len(leaves) for i, n in enumerate(leaves)}
for n in reversed(list(nx.topological_sort(G))):       # พ่อแม่อยู่กึ่งกลางลูก
    kids = list(G.successors(n))
    if kids: angle[n] = np.mean([angle[k] for k in kids])
for a, b in G.edges():
    ax.plot([angle[a], angle[b]], [depth[a], depth[b]], color="#c9c6bd", linewidth=1.4)
for n in G:
    ax.plot(angle[n], depth[n], "o", markersize=7, color=C1)
ax.set_yticklabels([]); ax.set_xticklabels([])

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

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

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

ข้อมูลตัวอย่าง · 11 แถว · seed 211● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
ชั้นนอกมีที่ให้ใบมากกว่าชั้นใน
บริษัทสินค้าอุปโภคอาหารบริการของใช้ในบ้ของใช้ในบ้านของใช้ส่วนของใช้ส่วนตัวเครื่องดื่เครื่องดื่มขนมขนมอาหารแช่แขอาหารแช่แข็งซ่อมบำรุงซ่อมบำรุงจัดส่งจัดส่ง

ชั้นนอกมีเส้นรอบวงยาวกว่า ทำให้โหนดชั้นนอกดูห่างกันมากกว่าความจริง

จำนวนโหนด11
ความลึก3
ใบ7
ดูข้อมูลเป็นตาราง (11 แถว)
radial tree 11 โหนด
โหนดพ่อแม่
บริษัท(ราก)
สินค้าอุปโภคบริษัท
อาหารบริษัท
บริการบริษัท
ของใช้ในบ้านสินค้าอุปโภค
ของใช้ส่วนตัวสินค้าอุปโภค
เครื่องดื่มอาหาร
ขนมอาหาร
อาหารแช่แข็งอาหาร
ซ่อมบำรุงบริการ
จัดส่งบริการ

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

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

ข้อควรระวัง

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

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

  • มีโหนดน้อยกว่า 20 โหนดใช้แทน: node-link-tree ที่อ่านชื่อง่ายกว่า
  • ชื่อโหนดยาวใช้แทน: icicle-chart ที่เขียนชื่อแนวนอนได้
  • ขนาดของโหนดสำคัญใช้แทน: circle-packing หรือ treemap

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

เทคนิคถัดไป · กราฟวงกลมซ้อน