Skip to content

🏆 Tiêu chuẩn — Visualization (Matplotlib & Seaborn)

Các tiêu chuẩn giúp bạn tạo biểu đồ rõ ràng, chuyên nghiệp, ai cũng đọc được — thay vì chart "đẹp mắt" nhưng gây hiểu lầm

Tổng quan tiêu chuẩn buổi 10

Buổi 10 là bước iNterpret trong hành trình OSEMN — biến insight từ EDA thành visual story mà stakeholder hiểu ngay. Nhưng "vẽ chart" không đồng nghĩa "communicate tốt". Một biểu đồ sai chart type, thừa trang trí, hay không accessibility-friendly có thể gây hiểu lầm hoặc loại trừ người xem. Theo nghiên cứu của Nielsen Norman Group (2024), 65% decision-makers đánh giá tính chuyên nghiệp của analyst qua chất lượng biểu đồ — trước cả nội dung phân tích.

Thiếu tiêu chuẩn visualization, DA thường mắc 3 sai lầm:

  1. Chart junk — thêm 3D effect, gradient, icon trang trí → che mất data thật
  2. Inconsistency — mỗi chart một style, màu sắc, font → report trông nghiệp dư
  3. Inaccessible — dùng red-green color scheme → ~8% nam giới (colorblind) không đọc được

Buổi này tập trung vào 3 tiêu chuẩn cốt lõi cho visualization:

  • IBCS — Tiêu chuẩn quốc tế cho business chart, đảm bảo consistency toàn tổ chức
  • Tufte's Principles — Nguyên tắc data-ink ratio và chart declutter từ Edward Tufte
  • Accessibility Standards — Thiết kế chart cho mọi người, bao gồm người khiếm thị màu

📋 Danh sách tiêu chuẩn liên quan

#Tiêu chuẩnTổ chức / Tác giảÁp dụng cho Buổi 10
1IBCSInternational Business Communication StandardsChart standards quốc tế — notation, layout, structure
2Tufte's PrinciplesEdward Tufte (1983–2006)Data-ink ratio, chart declutter, small multiples
3Accessibility StandardsW3C / WCAG + Cộng đồng Data VizColorblind-safe design, contrast, alt-text

1️⃣ IBCS — International Business Communication Standards

Giới thiệu

IBCS (International Business Communication Standards) là bộ tiêu chuẩn quốc tế do Dr. Rolf Hichert khởi xướng từ năm 2004, nhằm tạo ra "ngôn ngữ chung" cho business charts — giống như IFRS là chuẩn chung cho kế toán. IBCS được áp dụng bởi các tập đoàn lớn như SAP, Siemens, Swiss Re, Bosch và đang trở thành tiêu chuẩn phổ biến nhất cho báo cáo doanh nghiệp.

Ý tưởng cốt lõi: Mọi biểu đồ trong tổ chức phải "nói cùng ngôn ngữ" — khi nhìn column đen, ai cũng biết đó là Actual; column trắng viền đen là Plan/Budget. Không cần legend giải thích.

IBCS tuân theo nguyên tắc SUCCESS:

S — Say           → Truyền đạt thông điệp rõ ràng
U — Unify         → Thống nhất notation qua toàn bộ report
C — Condense      → Cô đọng — nhiều thông tin trong ít không gian
C — Check         → Kiểm tra tính chính xác
E — Express       → Chọn đúng chart type cho đúng data
S — Simplify      → Loại bỏ trang trí thừa
S — Structure     → Cấu trúc layout hợp lý

Áp dụng trong buổi học

IBCS Notation — Quy ước màu sắc & pattern

Ký hiệuÝ nghĩaMatplotlib Code
██ Đen (solid)Actual — số liệu thực tếcolor='black'
░░ Xám nhạtPrevious Year — kỳ trướccolor='#B0B0B0'
▢▢ Trắng viền đenPlan / Budgetfacecolor='white', edgecolor='black'
▨▨ Gạch chéoForecast — dự báohatch='///', facecolor='white'
🔺 Tam giác xanhPositive variance (thuận lợi)color='#2E7D32' (green)
🔻 Tam giác đỏNegative variance (bất lợi)color='#C62828' (red)

Ví dụ — IBCS Bar Chart: Actual vs Plan

python
import matplotlib.pyplot as plt
import numpy as np

# Data: Doanh thu 4 quý
quarters = ["Q1", "Q2", "Q3", "Q4"]
actual = [42, 48, 45, 52]     # Actual — đen
plan = [40, 50, 47, 55]       # Plan — trắng viền đen
variance = [a - p for a, p in zip(actual, plan)]  # Variance

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5),
                                gridspec_kw={"width_ratios": [3, 1]})

# --- Chart 1: Actual vs Plan (IBCS notation) ---
x = np.arange(len(quarters))
width = 0.35

# Plan bars — trắng viền đen (IBCS)
ax1.bar(x - width/2, plan, width, label="Plan",
        facecolor="white", edgecolor="black", linewidth=1.5)
# Actual bars — đen (IBCS)
ax1.bar(x + width/2, actual, width, label="Actual",
        color="black")

ax1.set_xlabel("Quarter")
ax1.set_ylabel("Revenue (Billion VND)")
ax1.set_title("Revenue: Actual vs Plan — FY2025", fontsize=13, fontweight="bold")
ax1.set_xticks(x)
ax1.set_xticklabels(quarters)
ax1.legend(frameon=False)
ax1.spines[["top", "right"]].set_visible(False)  # IBCS: bỏ border thừa

# --- Chart 2: Variance (IBCS: green/red) ---
colors = ["#2E7D32" if v >= 0 else "#C62828" for v in variance]
ax2.barh(quarters, variance, color=colors, edgecolor="none")
ax2.set_xlabel("Variance (B VND)")
ax2.set_title("ΔPlan", fontsize=13, fontweight="bold")
ax2.axvline(0, color="black", linewidth=0.8)
ax2.spines[["top", "right"]].set_visible(False)

plt.tight_layout()
plt.savefig("ibcs_actual_vs_plan.png", dpi=150, bbox_inches="tight")
plt.show()

💡 IBCS = consistency > creativity

Trong business reporting, consistency quan trọng hơn sáng tạo. Khi cả team dùng chung IBCS notation, sếp nhìn chart Q1 hay Q4, Việt Nam hay Singapore, đều hiểu ngay — zero learning curve. Matplotlib hoàn toàn có thể tuân thủ IBCS qua custom colors, hatches, và styles.

Checklist IBCS

  • [ ] Notation thống nhất — Actual = đen, Plan = trắng viền đen, Forecast = gạch chéo
  • [ ] Variance chart — luôn đi kèm main chart, green = tốt, red = xấu
  • [ ] Không 3D effect — tất cả chart 2D flat
  • [ ] Bỏ border thừaspines[["top", "right"]].set_visible(False)
  • [ ] Label trực tiếp — data label trên bar thay vì phụ thuộc legend
  • [ ] Consistent scale — cùng metric phải cùng scale trên các chart khác nhau
  • [ ] Title = message — title chart nêu insight, không chỉ mô tả ("Revenue grew 12%" thay vì "Revenue Chart")

2️⃣ Tufte's Principles — Data-Ink Ratio & Chart Declutter

Giới thiệu

Edward Tufte là giáo sư Yale và được gọi là "Leonardo da Vinci of Data" — tác giả 4 cuốn sách kinh điển về data visualization, bắt đầu từ "The Visual Display of Quantitative Information" (1983). Nguyên tắc cốt lõi của Tufte: mỗi giọt mực trên biểu đồ phải truyền tải thông tin — nếu không, xóa đi.

Tufte giới thiệu khái niệm Data-Ink Ratio:

Data-Ink Ratio=Data Ink (mực truyền tải data)Total Ink (tổng mực trên chart)

Mục tiêu: Data-Ink Ratio càng gần 1.0 càng tốt — nghĩa là mọi thứ trên chart đều phục vụ data, không có trang trí thừa.

Áp dụng trong buổi học

5 nguyên tắc Tufte cốt lõi

#Nguyên tắcGiải thíchMatplotlib Action
1Maximize Data-Ink RatioLoại bỏ mọi thứ không truyền tải dataBỏ gridlines nặng, borders, background color
2Erase Non-Data InkXóa chart junk: 3D, shadows, gradientsax.spines[...].set_visible(False)
3Erase Redundant Data InkKhông lặp lại thông tinKhông vừa data label vừa y-axis vừa legend cho cùng 1 data
4Revise & EditChart đầu tiên luôn cần chỉnh sửaIterate: draft → review → polish
5Small MultiplesNhiều chart nhỏ cùng scale tốt hơn 1 chart phức tạpplt.subplots(2, 3) với shared axes

Ví dụ — Before & After Tufte Declutter

python
import matplotlib.pyplot as plt
import numpy as np

months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
revenue = [32, 35, 38, 36, 42, 45]

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))

# ❌ BEFORE — Chart Junk (low data-ink ratio)
ax1.bar(months, revenue, color="royalblue", edgecolor="gold",
        linewidth=2)
ax1.set_facecolor("#F0F0F0")           # Background xám — thừa
ax1.grid(True, color="white", linewidth=2)  # Grid nặng — thừa
ax1.set_title("Monthly Revenue Report!!!", fontsize=16,
              color="red", fontweight="bold")  # Title quá lố
ax1.set_ylabel("Revenue (Billion VND)", fontsize=12)
ax1.legend(["Revenue"], loc="upper left", frameon=True,
           shadow=True)                        # Legend thừa (chỉ 1 series)
# Giữ nguyên tất cả spines — border box nặng

# ✅ AFTER — Tufte Declutter (high data-ink ratio)
ax2.bar(months, revenue, color="#333333", width=0.6)
ax2.spines[["top", "right", "left"]].set_visible(False)  # Bỏ borders thừa
ax2.tick_params(left=False)                               # Bỏ tick marks trái
ax2.set_yticks([])                                        # Bỏ y-axis (dùng data labels)
ax2.set_title("Revenue tăng đều — Q1-Q2/2025",
              fontsize=13, fontweight="bold", loc="left")  # Title = insight

# Data labels trực tiếp trên bar
for i, v in enumerate(revenue):
    ax2.text(i, v + 0.5, f"{v}B", ha="center", fontsize=11, fontweight="bold")

fig.suptitle("Before vs After — Tufte Declutter", fontsize=15, y=1.02)
plt.tight_layout()
plt.savefig("tufte_before_after.png", dpi=150, bbox_inches="tight")
plt.show()

Tufte's "Lie Factor"

Tufte cũng cảnh báo về Lie Factor — khi visual effect phóng đại hoặc thu nhỏ sự khác biệt thực tế:

Lie Factor=Size of effect shown in chartSize of effect in data
  • Lie Factor = 1.0 → trung thực
  • Lie Factor > 1.05 → phóng đại (misleading)
  • Lie Factor < 0.95 → thu nhỏ (misleading)

Ví dụ sai lầm phổ biến: truncated y-axis — cắt y-axis không bắt đầu từ 0 với bar chart → khác biệt 5% trông như 50%.

python
# ❌ Truncated y-axis — Lie Factor cao
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))

brands = ["Brand A", "Brand B", "Brand C"]
share = [32, 34, 33]

# Misleading: y-axis từ 30 → 35
ax1.bar(brands, share, color=["#e74c3c", "#3498db", "#2ecc71"])
ax1.set_ylim(30, 36)  # ← TRUNCATED! Brand B trông gấp 5x Brand A
ax1.set_title("❌ Misleading — Truncated Y-axis", fontsize=12)
ax1.set_ylabel("Market Share (%)")

# Honest: y-axis từ 0
ax2.bar(brands, share, color=["#e74c3c", "#3498db", "#2ecc71"])
ax2.set_ylim(0, 40)   # ← HONEST! Gần bằng nhau — đúng thực tế
ax2.set_title("✅ Honest — Full Y-axis", fontsize=12)
ax2.set_ylabel("Market Share (%)")

plt.tight_layout()
plt.show()

⚠️ Truncated y-axis

Bar chart phải bắt đầu y-axis từ 0 — vì chiều cao cột biểu thị magnitude. Nếu cắt y-axis, sai biệt 2% trông như gấp đôi. Line chart có thể truncate (vì thể hiện trend, không phải magnitude), nhưng cần ghi chú rõ.

Checklist Tufte's Principles

  • [ ] Data-Ink Ratio > 0.8 — mỗi element trên chart đều truyền tải data
  • [ ] Không 3D, shadow, gradient — chart flat 2D
  • [ ] Bỏ grid nặng — nếu cần grid, dùng light dotted lines
  • [ ] Bỏ spines thừa — chỉ giữ bottom (và left nếu cần)
  • [ ] Data labels trực tiếp — giảm phụ thuộc vào legend
  • [ ] Title = insight — "Revenue tăng 12% Q2" thay vì "Revenue Chart"
  • [ ] Không truncate y-axis cho bar chart
  • [ ] Small multiples khi có nhiều categories — subplot thay vì 1 chart phức tạp
  • [ ] Lie Factor ≈ 1.0 — visual không phóng đại hay thu nhỏ data

3️⃣ Accessibility Standards — Colorblind-Safe & Inclusive Design

Giới thiệu

Accessibility trong data visualization đảm bảo rằng mọi người — bao gồm ~8% nam giới và ~0.5% nữ giới mắc chứng mù màu (color vision deficiency / CVD) — đều đọc được biểu đồ. Tiêu chuẩn WCAG 2.1 (Web Content Accessibility Guidelines) từ W3C là nền tảng, được bổ sung bởi best practices từ cộng đồng data visualization (Chartability, Visa Chart Components, US Web Design Standards).

Theo thống kê, trong phòng họp 25 người, trung bình 1-2 người không phân biệt được red-green — palette phổ biến nhất trong business charts. Nếu chart dùng "đỏ = xấu, xanh = tốt" mà không có indicator khác (pattern, label), 1-2 stakeholder sẽ không hiểu chart.

Áp dụng trong buổi học

3 nguyên tắc Accessibility cho chart

#Nguyên tắcGiải thíchMatplotlib Implementation
1Không dùng color aloneDùng color + pattern/shape/labelhatch, marker, data labels
2Colorblind-safe paletteTránh red-green; dùng blue-orangePalette: tab10, colorblind (Seaborn)
3Contrast ratio ≥ 4.5:1Text & data đủ tương phản với backgroundDùng dark colors trên white background

Ví dụ — Colorblind-Safe Chart

python
import matplotlib.pyplot as plt
import seaborn as sns

# Colorblind-safe palette — IBM Design / Wong (2011)
cb_palette = ["#648FFF",   # Blue
              "#DC267F",   # Magenta
              "#FE6100",   # Orange
              "#FFB000",   # Gold
              "#785EF0"]   # Violet

# ❌ Red-Green palette (không accessible)
bad_palette = ["#FF0000", "#00FF00", "#FF0000", "#00FF00", "#FF0000"]

# ✅ Accessible: color + pattern + label
categories = ["Engineering", "Marketing", "Sales", "HR", "Finance"]
values = [520, 300, 375, 150, 155]
hatches = ["", "//", "\\\\", "xx", ".."]  # Patterns khác nhau

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))

# ❌ Not accessible — red-green only
ax1.bar(categories, values, color=["red", "green", "red", "green", "red"])
ax1.set_title("❌ Red-Green Only — Not Accessible", fontsize=12)
ax1.set_ylabel("Headcount")
ax1.tick_params(axis="x", rotation=45)

# ✅ Accessible — colorblind-safe + hatches + labels
bars = ax2.bar(categories, values, color=cb_palette, edgecolor="black",
               linewidth=0.8)
for bar, hatch in zip(bars, hatches):
    bar.set_hatch(hatch)

# Data labels
for bar, val in zip(bars, values):
    ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 8,
             str(val), ha="center", fontsize=11, fontweight="bold")

ax2.set_title("✅ Colorblind-Safe + Patterns + Labels", fontsize=12)
ax2.set_ylabel("Headcount")
ax2.tick_params(axis="x", rotation=45)
ax2.spines[["top", "right"]].set_visible(False)

plt.tight_layout()
plt.savefig("accessibility_comparison.png", dpi=150, bbox_inches="tight")
plt.show()

Seaborn Colorblind Palette

python
import seaborn as sns

# Seaborn built-in colorblind palette
sns.set_palette("colorblind")

# Hoặc set globally
plt.rcParams["axes.prop_cycle"] = plt.cycler(
    color=sns.color_palette("colorblind")
)

# Từ giờ tất cả chart tự động dùng colorblind-safe colors
sns.barplot(data=df, x="department", y="salary", palette="colorblind")
plt.title("Salary by Department — Colorblind-Safe Palette")
plt.show()

Alt-Text cho Chart

Khi export chart vào report (PDF, HTML, slide), luôn thêm alt-text — mô tả chart bằng text cho screen reader:

python
# Thêm alt-text vào figure metadata
fig, ax = plt.subplots()
ax.bar(categories, values, color=cb_palette)
ax.set_title("Headcount by Department")

# Alt-text (dùng khi embed vào HTML/report)
alt_text = ("Bar chart showing headcount by department. "
            "Engineering leads with 520 employees, followed by "
            "Sales (375), Marketing (300), Finance (155), and HR (150).")

# Matplotlib metadata
fig.text(0, -0.05, f"Alt: {alt_text}", fontsize=7, color="gray",
         style="italic", transform=fig.transFigure)

plt.tight_layout()
plt.show()

💡 Test accessibility dễ

Cách test nhanh chart có accessible không:

  1. In trắng đen (grayscale) — nếu vẫn phân biệt được các series → OK
  2. Bỏ legend — nếu vẫn hiểu chart nhờ data labels → OK
  3. Coblis simulator (coblis.com) — upload chart PNG để xem dưới mắt người colorblind

Checklist Accessibility Standards

  • [ ] Không dùng color alone — mỗi series có thêm pattern, marker, hoặc label
  • [ ] Palette colorblind-safe — dùng sns.color_palette("colorblind") hoặc Wong palette
  • [ ] Không red-green — thay bằng blue-orange hoặc blue-red (xanh dương-đỏ OK vì ít người mù blue)
  • [ ] Contrast ≥ 4.5:1 — text đủ đậm trên background
  • [ ] Font size ≥ 10pt cho labels, ≥ 12pt cho title
  • [ ] Alt-text — mô tả chart bằng text khi embed vào report
  • [ ] Test grayscale — print chart trắng đen, vẫn đọc được
  • [ ] Legible axis labels — không chồng chéo, rotation nếu cần

📊 Bảng tổng hợp

Tiêu chuẩnPhạm viÁp dụng Buổi 10Mức độ bắt buộc
IBCSBusiness reporting — notation thống nhấtActual/Plan/Forecast notation, variance chart, consistency⭐⭐⭐ Nên tuân thủ — tiêu chuẩn enterprise hàng đầu
Tufte's PrinciplesDesign — data-ink ratio, declutterLoại chart junk, data labels, honest axes, small multiples⭐⭐⭐ Gần như bắt buộc — nền tảng mọi visualization tốt
AccessibilityInclusive design — colorblind-safePalette safe, pattern + label, alt-text, contrast⭐⭐ Nên tuân thủ — đặc biệt khi chart cho external audience

⚠️ Quan trọng

3 tiêu chuẩn này không mâu thuẫn — chúng bổ sung nhau. IBCS cho bạn notation (cái gì = màu gì). Tufte cho bạn nguyên tắc thiết kế (bỏ cái gì). Accessibility cho bạn tính bao trùm (ai cũng đọc được). Chart chuyên nghiệp nhất là chart tuân thủ cả 3: consistent notation (IBCS) + clean design (Tufte) + accessible palette (WCAG).


📚 Tài liệu tham khảo

Tài liệuLinkGhi chú
IBCS Standards (Official)ibcs.com/standardsBộ tiêu chuẩn SUCCESS đầy đủ
The Visual Display of Quantitative Information — Edward Tufte (1983)edwardtufte.comSách gốc data-ink ratio
WCAG 2.1 — W3Cw3.org/TR/WCAG21Web Content Accessibility Guidelines
Wong (2011) — Points of View: Color BlindnessNature MethodsColorblind-safe palette gốc
Chartabilitychartability.fizz.studioFramework test accessibility cho chart
Seaborn Color Palettesseaborn.pydata.org/tutorial/color_palettes.htmlHướng dẫn chọn palette
Coblis — Color Blindness Simulatorcolor-blindness.com/coblisUpload chart → xem dưới mắt colorblind