01ภาพรวมของกราฟนี้
Sunburst chart วางลำดับชั้นเป็นวงแหวนซ้อนกัน โดยวงในสุดคือราก และวงนอกคือระดับที่ลึกลงไป จุดแข็งเหนือ treemap คือความลึกของโครงสร้างเห็นได้ทันทีจากจำนวนวง แต่จุดอ่อนคือวงนอกมีพื้นที่มากกว่าวงในที่มุมเท่ากัน จึงดูสำคัญกว่าความจริง
เปรียบเทียบให้เห็นภาพ
เหมือนวงปีของต้นไม้ที่แต่ละวงบอกช่วงเวลาหนึ่ง เพียงแต่ที่นี่แต่ละวงบอกระดับหนึ่งของโครงสร้าง
ใช้ที่ไหนบ้าง — โครงสร้างองค์กร เส้นทางการคลิกในเว็บไซต์ หมวดหมู่สินค้าหลายระดับ หรือโครงสร้างงบประมาณที่ลึกหลายชั้น
สร้างอย่างไร
- ขั้นที่ 1จัดข้อมูลเป็นลำดับชั้น
- ขั้นที่ 2คำนวณมุมของแต่ละโหนดตามสัดส่วน
- ขั้นที่ 3วางวงแหวนจากในออกนอกตามระดับ
- ขั้นที่ 4ใช้สีตามกลุ่มระดับบนเพื่อให้ตามสายได้
ตัวอย่างคลาสสิก · Sunburst ในเครื่องมือวิเคราะห์เส้นทางผู้ใช้เว็บไซต์
เครื่องมือวิเคราะห์เว็บใช้ sunburst แสดงเส้นทางการคลิกของผู้ใช้ โดยวงในคือหน้าแรกและวงนอกคือหน้าที่คลิกต่อไปเรื่อย ๆ รูปแบบนี้ตรงกับข้อมูลพอดีเพราะเส้นทางเป็นลำดับชั้นจริง และผู้อ่านต้องการเห็นว่าคนแตกไปทางไหนบ้างหลังจากหน้าแรก
02อธิบายภาษาคนเข้าใจง่าย ๆ
Treemap แสดงลำดับชั้นได้ก็จริง แต่ความลึกของโครงสร้างดูยาก เพราะทุกอย่างอยู่บนระนาบเดียวกันและต้องอาศัยเส้นขอบกับสีเพื่อบอกว่าอะไรอยู่ในอะไร
Sunburst แก้ด้วยการใช้รัศมีแทนความลึก วงในสุดคือราก วงถัดออกมาคือลูก วงถัดไปคือหลาน ผู้อ่านจึงนับจำนวนวงแล้วรู้ทันทีว่าโครงสร้างลึกกี่ชั้น
การอ่านก็เป็นธรรมชาติ ลากสายตาจากในออกนอกคือการเดินลงลำดับชั้น และมุมที่ครองอยู่คือสัดส่วนของสาขานั้น
ปัญหาที่หนีไม่พ้นคือเรขาคณิต วงนอกมีเส้นรอบวงยาวกว่าวงใน ดังนั้นโหนดที่มุมเท่ากันในวงนอกจะมีพื้นที่มากกว่า และตาเราอ่านพื้นที่ไปด้วยโดยไม่รู้ตัว
ผลคือรายการย่อยในชั้นลึกดูสำคัญกว่าความจริง ซึ่งเป็นการบิดที่แก้ไม่ได้ในเชิงโครงสร้าง ถ้าต้องการความแม่นให้ใช้ icicle chart ที่เป็นแท่งตรงแทน
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
id | ชื่อโหนด | โหนด | angle | 10–100 โหนด | ต้องไม่ซ้ำกัน |
parent | โหนดแม่ | โหนด | row | อ้างถึง id ที่มีจริง | รากใช้ค่าว่าง |
value | ขนาด | ตัวเลขต่อเนื่อง | area | บวกเท่านั้น | ใส่ค่าเฉพาะที่ใบ |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- ตรวจโครงสร้างว่าไม่มีวงจร
- ใส่ค่าเฉพาะที่ใบ
- จำกัดความลึกไม่เกิน 4 ระดับ
ค่าที่หายไป — โหนดที่ไม่มีค่าจะหายไปพร้อมลูกทั้งหมดของมัน ต้องตรวจผลรวมก่อนวาดเสมอ
สเกลของแกน — มุมแปรตรงกับค่า ส่วนความหนาของวงควรเท่ากันทุกชั้นเพื่อไม่ให้ชั้นใดดูสำคัญกว่าโดยการจัดหน้า
กราฟจะเริ่มพังเมื่อไร
เกิน 4 ระดับ วงนอกจะบางจนอ่านไม่ได้ และเกิน 100 โหนดมุมจะแคบเกินกด
หน้าตาไฟล์ที่ต้องเตรียม
id,parent,value
บริษัท,,0
สินค้าอุปโภค,บริษัท,0
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- ความลึกของโครงสร้างจากจำนวนวง
- สาขาที่กินสัดส่วนมากที่สุดจากมุมที่ครอง
- สาขาที่แตกย่อยมากกับสาขาที่มีลูกน้อย
สิ่งที่กราฟนี้ตอบไม่ได้
- ★ เทียบขนาดข้ามชั้นไม่ได้ เพราะวงนอกมีพื้นที่มากกว่าที่มุมเท่ากัน
- อ่านค่าที่แม่นยำไม่ได้จากมุม
- ชื่อโหนดในวงนอกมักยาวเกินกว่าจะเขียนได้
อ่านตามลำดับนี้
- เริ่มที่วงในสุดแล้วลากสายตาออกนอก
- ดูมุมที่แต่ละสาขาครองเพื่ออ่านสัดส่วน
- นับจำนวนวงเพื่อรู้ความลึก
คำถามที่ตอบได้
- โครงสร้างลึกกี่ชั้น
- สาขาไหนใหญ่ที่สุด
- ภายในสาขานี้แบ่งย่อยอย่างไร
สัญญาณว่ามีอะไรผิด
- วงนอกบางมากจนอ่านไม่ได้
- ความหนาของวงไม่เท่ากันโดยไม่มีเหตุผล
- มีเกิน 4 ระดับ
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- ใช้สีเดียวกันตลอดสายเพื่อให้ตามสาขาได้
- ติดป้ายเฉพาะวงในที่มีที่พอ และใช้ tooltip สำหรับวงนอก
- ใส่ยอดรวมไว้กลางวง
รูปแบบชื่อกราฟที่ควรใช้
<สาขา> ครอง <เปอร์เซ็นต์> ของทั้งหมด โดยแตกย่อยเป็น <จำนวน> รายการ
06เอาไปใช้ต่อ
โค้ดทุกชิ้นอ่านไฟล์ sunburst-chart-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง
# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด sunburst-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("sunburst-chart-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "rings" # ← ค่าจาก Playground
fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)
# matplotlib ไม่มี sunburst ในตัว — วาดด้วย wedge สองชั้น
leaves = df[df["value"] > 0]
groups = leaves.groupby("parent")["value"].sum()
total = groups.sum()
ax.pie(groups.values, radius=1.0, labels=groups.index, labeldistance=0.75,
colors=[C1, C2, C3, "#eda100"][: len(groups)],
wedgeprops=dict(width=0.34, edgecolor=SURFACE, linewidth=2))
inner_colors = [[C1, C2, C3, "#eda100"][list(groups.index).index(p) % 4] for p in leaves["parent"]]
ax.pie(leaves["value"], radius=1.36, colors=inner_colors, # วงนอก = ระดับลึกกว่า
wedgeprops=dict(width=0.34, edgecolor=SURFACE, linewidth=2, alpha=0.65))
ax.text(0, 0, f"{total:,.0f}", ha="center", va="center", fontsize=15)
ax.set_aspect("equal")
ax.spines[["top", "right"]].set_visible(False) # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("sunburst-chart.png", dpi=160)
print("เขียน sunburst-chart.png แล้ว ·", len(df), "แถว")07ลองเล่นเพื่อเข้าใจ
วงนอกมีพื้นที่มากกว่าวงในที่มุมเท่ากัน จึงดูสำคัญกว่าความจริง
ดูข้อมูลเป็นตาราง (7 แถว)
| กลุ่ม | รายการ | ค่า | สัดส่วน |
|---|---|---|---|
| สินค้าอุปโภค | ของใช้ในบ้าน | 91.0 | 14.4% |
| สินค้าอุปโภค | ของใช้ส่วนตัว | 107 | 16.9% |
| อาหาร | เครื่องดื่ม | 87.0 | 13.8% |
| อาหาร | ขนม | 117 | 18.5% |
| อาหาร | อาหารแช่แข็ง | 41.0 | 6.5% |
| บริการ | ซ่อมบำรุง | 102 | 16.1% |
| บริการ | จัดส่ง | 87.0 | 13.8% |
ลองแล้วจะเห็นอะไร
- สังเกตอะไร: หาโหนดในวงนอกที่ครองมุมเท่ากับโหนดในวงใน แล้วเทียบพื้นที่ จะเห็นว่าวงนอกกินพื้นที่มากกว่าชัดเจนทั้งที่สัดส่วนเท่ากัน
ข้อควรระวัง
- ใช้ลึกเกิน 4 ระดับ ทำให้วงนอกบางจนอ่านไม่ได้
- ลืมว่าวงนอกดูใหญ่กว่าความจริงเพราะพื้นที่เพิ่มตามรัศมี
- พยายามติดป้ายทุกโหนด ทั้งที่วงนอกไม่มีที่พอ
- ใช้สีต่างกันในแต่ละชั้น ทำให้ตามสาขาไม่ได้
เมื่อไหร่ไม่ควรใช้กราฟนี้
- ต้องการเทียบขนาดให้แม่น→ใช้แทน: icicle-chart ที่เป็นแท่งตรง หรือ treemap
- ชื่อโหนดยาว→ใช้แทน: icicle-chart ที่เขียนชื่อในแนวนอนได้
- มีระดับเดียว→ใช้แทน: pie-chart หรือ horizontal-bar