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

กราฟวงกลมซ้อนCircle Packing

เห็นการซ้อนของกลุ่มได้ชัดกว่า treemap

4
ขั้นสูงควรรู้ก่อน · เข้าใจว่าพื้นที่แทนปริมาณและรัศมีต้องใช้รากที่สอง
ลองเล่น

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

Circle packing บรรจุวงกลมของโหนดลูกไว้ภายในวงกลมของพ่อแม่ ทำให้ความสัมพันธ์แบบ "อยู่ใน" เห็นได้ทันทีโดยไม่ต้องอาศัยเส้นขอบหรือสีเหมือน treemap แลกกับพื้นที่ที่เสียไปกับช่องว่างระหว่างวงประมาณหนึ่งในสี่

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

เหมือนกล่องใส่ของที่มีกล่องเล็กซ้อนอยู่ข้างใน เราเห็นทันทีว่าอะไรอยู่ในอะไรโดยไม่ต้องอ่านป้าย

ใช้ที่ไหนบ้าง — โครงสร้างองค์กรที่มีขนาดทีมต่างกัน โครงสร้างโค้ดที่แต่ละโมดูลมีขนาดต่างกัน หรือหมวดหมู่ที่ต้องเห็นทั้งการซ้อนและขนาด

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

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

ตัวอย่างคลาสสิก · Circle packing ในการแสดงโครงสร้างโค้ดของโปรแกรม

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

Wikipedia · Circle packing

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

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

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

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

ราคาที่จ่ายคือพื้นที่ วงกลมบรรจุในวงกลมเหลือช่องว่างประมาณหนึ่งในสี่เสมอ ต่างจาก treemap ที่ใช้พื้นที่เต็มร้อยเปอร์เซ็นต์

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

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

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

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

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
idชื่อโหนดโหนดtext10–150 โหนดต้องไม่ซ้ำกัน
parentโหนดแม่โหนดrowอ้างถึง id ที่มีจริงรากเป็นวงนอกสุด
valueขนาดตัวเลขต่อเนื่องareaบวกเท่านั้น★ ใส่ค่าเฉพาะที่ใบ และรัศมีต้องแปรตามรากที่สอง

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

  1. ตรวจโครงสร้างว่าไม่มีวงจร
  2. ใส่ค่าเฉพาะที่ใบ
  3. คำนวณรัศมีจากรากที่สองของค่า

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

สเกลของแกน — พื้นที่แปรตรงกับค่า ซึ่งแปลว่ารัศมีแปรตามรากที่สอง

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

เกิน 200 โหนด วงในสุดจะเล็กจนกดและอ่านไม่ได้

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

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

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

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

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

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

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

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

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

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

  • อะไรอยู่ในอะไร
  • กลุ่มไหนใหญ่ที่สุด
  • โครงสร้างภายในกลุ่มนี้เป็นอย่างไร

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

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

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

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

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

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

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

<กลุ่ม> ใหญ่ที่สุดและประกอบด้วย <จำนวน> รายการย่อย

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

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

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

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

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

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

leaves = df[df["value"] > 0]
groups = leaves.groupby("parent")["value"].sum().sort_values(ascending=False)
data = [{"id": g, "datum": v,
         "children": [{"id": r["id"], "datum": r["value"]}
                      for _, r in leaves[leaves["parent"] == g].iterrows()]}
        for g, v in groups.items()]
circles = circlify.circlify(data, show_enclosure=False, target_enclosure=circlify.Circle(x=0, y=0, r=1))
for c in circles:
    depth_color = C1 if c.level == 1 else "#9ec5f4"
    ax.add_patch(plt.Circle((c.x, c.y), c.r, fill=c.level > 1, alpha=0.6,
                            facecolor=depth_color, edgecolor=C1, linewidth=1.8))
    if c.r > 0.12:
        ax.text(c.x, c.y, c.ex["id"], ha="center", va="center", fontsize=8)
ax.set_xlim(-1.05, 1.05); ax.set_ylim(-1.05, 1.05); ax.set_aspect("equal"); ax.axis("off")

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

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

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

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

พื้นที่ว่างระหว่างวงไม่ได้มีความหมาย แต่กินที่ประมาณ 20% ของภาพ

จำนวนกลุ่ม3
จำนวนรายการ7
กลุ่มใหญ่สุดอาหาร
ดูข้อมูลเป็นตาราง (7 แถว)
circle packing 7 รายการใน 3 กลุ่ม
กลุ่มรายการค่า
สินค้าอุปโภคของใช้ในบ้าน91.0
สินค้าอุปโภคของใช้ส่วนตัว107
อาหารเครื่องดื่ม87.0
อาหารขนม117
อาหารอาหารแช่แข็ง41.0
บริการซ่อมบำรุง102
บริการจัดส่ง87.0

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

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

ข้อควรระวัง

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

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

  • พื้นที่จำกัดและต้องใช้ให้คุ้มใช้แทน: treemap
  • ต้องเทียบขนาดข้ามกลุ่มให้แม่นใช้แทน: horizontal-bar
  • ขนาดไม่สำคัญ สนใจแค่โครงสร้างใช้แทน: node-link-tree

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

เทคนิคถัดไป · กราฟเครือข่าย