01ภาพรวมของกราฟนี้
Icicle chart คือ sunburst ที่คลี่ออกเป็นเส้นตรง แต่ละชั้นเป็นแถวของแท่งที่ความกว้างแปรตามสัดส่วน ข้อดีที่ชัดเจนคือความกว้างเทียบกันได้แม่นกว่ามุม และชื่อเขียนในแนวนอนได้เต็ม ซึ่งเป็นสองปัญหาหลักของ sunburst
เปรียบเทียบให้เห็นภาพ
เหมือนสารบัญหนังสือที่หัวข้อใหญ่กินบรรทัดยาวและหัวข้อย่อยอยู่ใต้ลงมา เพียงแต่ความยาวบอกขนาดด้วย
ใช้ที่ไหนบ้าง — โครงสร้างองค์กรที่ชื่อยาว หมวดหมู่สินค้าหลายระดับ หรือแทน sunburst เมื่อต้องการความแม่น
สร้างอย่างไร
- ขั้นที่ 1จัดข้อมูลเป็นลำดับชั้น
- ขั้นที่ 2วางรากเป็นแท่งเต็มความกว้างที่แถวบนสุด
- ขั้นที่ 3แบ่งแต่ละแถวถัดลงมาตามสัดส่วนของลูก
- ขั้นที่ 4เขียนชื่อในแท่งที่กว้างพอ
ตัวอย่างคลาสสิก · Icicle plot ในการวิเคราะห์ประสิทธิภาพของโปรแกรม
เครื่องมือวิเคราะห์ประสิทธิภาพใช้ icicle chart แสดงเวลาที่ใช้ในแต่ละฟังก์ชัน โดยแถวบนคือฟังก์ชันที่เรียก และแถวล่างคือฟังก์ชันที่ถูกเรียก ความกว้างคือเวลาที่ใช้ ทำให้เห็นทันทีว่าเวลาหมดไปกับสายการเรียกไหน
02อธิบายภาษาคนเข้าใจง่าย ๆ
Sunburst มีปัญหาสองข้อที่แก้ไม่ได้ในเชิงเรขาคณิต คือวงนอกดูใหญ่กว่าความจริง และชื่อโหนดในวงนอกไม่มีที่เขียนเพราะต้องโค้งตามวง
Icicle แก้ทั้งสองข้อด้วยการคลี่วงออกเป็นเส้นตรง แต่ละชั้นกลายเป็นแถวแนวนอน และแต่ละโหนดเป็นแท่งที่ความกว้างแปรตามสัดส่วน
ทันทีที่เปลี่ยนจากมุมเป็นความกว้าง ความแม่นก็ดีขึ้นมาก เพราะการเทียบความยาวบนเส้นฐานเดียวกันเป็นสิ่งที่สายตาทำได้ดีที่สุด
ชื่อก็เขียนได้เต็มในแนวนอนโดยไม่ต้องโค้งหรือหมุน ซึ่งทำให้ใช้กับข้อมูลที่ชื่อยาวอย่างชื่อหน่วยงานหรือชื่อโครงการได้จริง
สิ่งที่เสียไปคือความสวยงามและความรู้สึกเป็น "ทั้งหมด" ที่รูปวงกลมให้ นี่คือการแลกเปลี่ยนตรงไปตรงมาระหว่างความสวยกับความแม่น ซึ่งงานวิเคราะห์ควรเลือกความแม่น
03ข้อมูลที่ต้องใช้
คอลัมน์ที่ต้องเตรียม
| คอลัมน์ | ทำหน้าที่ | ชนิด | เข้ารหัสด้วย | จำนวนค่าที่เหมาะ | หมายเหตุ |
|---|---|---|---|---|---|
id | ชื่อโหนด | โหนด | x | 10–120 โหนด | ชื่อยาวได้เพราะเขียนในแนวนอน |
parent | โหนดแม่ | โหนด | row | อ้างถึง id ที่มีจริง | รากใช้ค่าว่าง |
value | ขนาด | ตัวเลขต่อเนื่อง | length | บวกเท่านั้น | ใส่ค่าเฉพาะที่ใบ |
ต้องเตรียมข้อมูลอย่างไรก่อนวาด
- ตรวจโครงสร้างว่าไม่มีวงจร
- ใส่ค่าเฉพาะที่ใบ
- ตัดสินใจว่าจะเรียงลูกตามค่าหรือตามชื่อ
ค่าที่หายไป — โหนดที่ไม่มีค่าจะหายพร้อมลูก ต้องตรวจผลรวมก่อนวาด
สเกลของแกน — ความกว้างแปรตรงกับค่า และทุกแถวใช้ความกว้างรวมเท่ากันเสมอ
กราฟจะเริ่มพังเมื่อไร
เกิน 6 ระดับ แถวจะเตี้ยจนอ่านชื่อไม่ได้
หน้าตาไฟล์ที่ต้องเตรียม
id,parent,value
บริษัท,,0
สินค้าอุปโภค,บริษัท,0
…04อ่านแล้วเห็นอะไร
สิ่งที่เห็นได้จากภาพ
- สัดส่วนของแต่ละสาขาจากความกว้าง ซึ่งเทียบได้แม่นกว่ามุม
- ความลึกของโครงสร้างจากจำนวนแถว
- สาขาที่แตกย่อยมากกับสาขาที่ตัน
สิ่งที่กราฟนี้ตอบไม่ได้
- ไม่สื่อความเป็น "ทั้งหมด" ได้ทันทีเหมือนรูปวงกลม
- แถวล่างที่แท่งแคบยังเขียนชื่อไม่ได้อยู่ดี
อ่านตามลำดับนี้
- อ่านจากแถวบนลงล่างคือเดินลงลำดับชั้น
- เทียบความกว้างของแท่งในแถวเดียวกัน
- ดูจำนวนแถวเพื่อรู้ความลึก
คำถามที่ตอบได้
- สาขาไหนใหญ่ที่สุดและใหญ่กว่ากันเท่าไร
- โครงสร้างลึกกี่ชั้น
- ภายในสาขานี้แบ่งอย่างไร
สัญญาณว่ามีอะไรผิด
- แถวเตี้ยจนอ่านชื่อไม่ได้
- ความกว้างรวมของแต่ละแถวไม่เท่ากัน ซึ่งแปลว่าข้อมูลขาด
05เหมาะกับการนำเสนอแบบไหน
ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย
- เขียนชื่อและค่าในแท่งที่กว้างพอ
- ใช้สีตามสาขาระดับบนเพื่อให้ตามสายได้
- ทำให้แถวบนสุดหนากว่าเล็กน้อยเพื่อเน้นว่าเป็นราก
รูปแบบชื่อกราฟที่ควรใช้
<สาขา> ครอง <เปอร์เซ็นต์> ของทั้งหมด และแตกย่อยลึก <จำนวน> ชั้น
06เอาไปใช้ต่อ
โค้ดทุกชิ้นอ่านไฟล์ icicle-chart-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง
# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด icicle-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("icicle-chart-sample.csv")
df["value"] = pd.to_numeric(df["value"], errors="coerce")
NOTE = "width" # ← ค่าจาก 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)
total = groups.sum()
ax.barh([2], [total], height=0.7, color="#256abf", edgecolor=SURFACE, linewidth=2)
ax.text(total / 2, 2, f"ทั้งหมด {total:,.0f}", ha="center", va="center", color="white", fontsize=10)
left = 0
for i, (g, v) in enumerate(groups.items()):
ax.barh([1], [v], left=left, height=0.7, color=[C1, C2, C3, "#eda100"][i % 4],
edgecolor=SURFACE, linewidth=2)
if v / total > 0.12: ax.text(left + v / 2, 1, g, ha="center", va="center", color="white", fontsize=9)
sub_left = left
for _, r in leaves[leaves["parent"] == g].iterrows():
ax.barh([0], [r["value"]], left=sub_left, height=0.7, alpha=0.6,
color=[C1, C2, C3, "#eda100"][i % 4], edgecolor=SURFACE, linewidth=2)
sub_left += r["value"]
left += v
ax.set_yticks([0, 1, 2]); ax.set_yticklabels(["รายการย่อย", "กลุ่ม", "ทั้งหมด"]); ax.set_xticks([])
ax.spines[["top", "right"]].set_visible(False) # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("icicle-chart.png", dpi=160)
print("เขียน icicle-chart.png แล้ว ·", len(df), "แถว")07ลองเล่นเพื่อเข้าใจ
ดูข้อมูลเป็นตาราง (7 แถว)
| กลุ่ม | รายการ | ค่า |
|---|---|---|
| สินค้าอุปโภค | ของใช้ในบ้าน | 91.0 |
| สินค้าอุปโภค | ของใช้ส่วนตัว | 107 |
| อาหาร | เครื่องดื่ม | 87.0 |
| อาหาร | ขนม | 117 |
| อาหาร | อาหารแช่แข็ง | 41.0 |
| บริการ | ซ่อมบำรุง | 102 |
| บริการ | จัดส่ง | 87.0 |
ลองแล้วจะเห็นอะไร
- สังเกตอะไร: เปิดหน้า sunburst-chart ที่ใช้ข้อมูลชุดเดียวกัน แล้วลองจัดอันดับสาขาจากทั้งสองภาพ จะเห็นว่าแท่งตรงทำได้ง่ายกว่าชัดเจน
ข้อควรระวัง
- ใช้ลึกเกิน 6 ระดับ ทำให้แถวเตี้ยจนอ่านชื่อไม่ได้
- ใส่ค่าทั้งที่ใบและที่โหนดแม่ ทำให้ความกว้างรวมของแต่ละแถวไม่เท่ากัน
- ใช้สีต่างกันในแต่ละชั้นจนตามสาขาไม่ได้
- เลือก sunburst แทนทั้งที่ข้อมูลชุดนี้ชื่อยาวและต้องเทียบขนาด
เมื่อไหร่ไม่ควรใช้กราฟนี้
- ต้องการสื่อความเป็น "ทั้งหมด" อย่างชัดเจน→ใช้แทน: sunburst-chart หรือ pie-chart
- มีระดับเดียว→ใช้แทน: horizontal-bar
- สนใจโครงสร้างการเชื่อมต่อมากกว่าขนาด→ใช้แทน: node-link-tree