Communitygithub.com

storytelling-viz:可直接用的图

使用 plotnine 製作可直接用於簡報、文件、週/月報及期刊論文的靜態圖表。根據使用者要比較的項目(排名、趨勢、變化、構成等)自動選擇圖形,輸出符合規格的 SVG、PDF 及 PNG,並自動檢查版面。

storytelling-viz:可直接用的图 是什麼?

此技能專為開發者、資料分析師及 AI 助手設計,負責從資料到「可直接交付」的靜態圖表,支援中文與英文。它內建多種圖表函數(如長條圖、趨勢線、瀑布圖、KPI 卡片等),使用者只需指定要比較的維度(排名、趨勢、構成、貢獻、進度等)即可自動產生對應圖形。技能分為兩種場景:「經營彙報」與「通用/出版」,兩者在顏色規則、漲跌標示(▲▼)、異常判斷、口徑註腳等方面有不同約定,經營彙報場景必須參考 `references/business.md` 中的規定。輸出規格支援幻燈片、文件、網頁、期刊單欄/雙欄等,並會自動檢查文字重疊、對比度、排版超出版面等問題。所有圖表函數共用 `highlight=`、`fmt=` 等參數,且支援多面板組合。平台依賴為 Python + plotnine(0.15.x)、pandas、pillow、pypdf。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Jaschenn/my-skills/tree/HEAD/storytelling-viz

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

storytelling-viz:可直接用的圖

目標是一張能直接放進彙報、文件或期刊的圖,而不是隨手畫的長條圖。它要滿足三點:

  • 圖形對。 圖形與要比較的東西匹配,焦點清楚,直接標註,不用圖例。
  • 在真實資料上不崩。 長標籤、負值、缺失、類別多、離群值都能正確顯示。
  • 符合輸出規格。 尺寸、字型大小、線寬、格式、字型嵌入都合規。

本 skill 只管圖。選題、分析和報告結構不在本 skill 範圍內。

工作流程

  1. 判斷場景。 場景決定顏色、註腳、數字等預設慣例(見「兩種場景的慣例」):
    • 經營彙報:給業務方或管理層看的經營指標圖,不論頻率和場合(週報、月報、日報看板、異動歸因、管理層 / 董事會 / 戰略會的經營資料頁)。判斷依據是「內容是不是經營指標、讀者是不是要據此做業務決策」,不是「是不是週報」。
    • 通用 / 出版:分析或研究文件、實驗結果、網頁、論文配圖等。
    • 拿不定時按經營彙報處理(慣例更嚴,只多一行註腳)。經營彙報場景必須先讀 references/business.md,即使範例程式碼看起來已經夠用。
  2. 按「要比較什麼」選函數。 見下方選函數表;兩種場景用同一張表。
  3. 選輸出規格。 用 spec= 指定(幻燈片、文件、期刊……)。
  4. 出圖,看自動檢查結果。 figure() / save() 渲染完會印出 QA … ok 或列出每條警告;回傳值的 .qa 是同一份問題清單(空清單 = 通過)。有警告就改,不交付。
  5. 開啟 PNG 按檢查清單檢查。 用 Read 開啟產生的 PNG,按文末的檢查清單逐條核對。

快速開始

# /// script
# dependencies = ["plotnine>=0.15,<0.16", "pandas", "pillow", "pypdf"]
# ///
import sys; sys.path.insert(0, "<skill>/scripts")
from viz_common import figure, titled, set_lang, save
import charts as C          # 通用圖表函數
import biz as B             # 經營彙報圖表函數(與 C 混用無妨)

set_lang("en")                                   # 或 "zh"(預設)
c = C.bar_rank(df, "channel", "gmv", highlight="POS", top_n=8, fmt="num")
figure([[(titled(c, title="POS is the only channel that shrank"), 1)]],
       spec="doc", name="fig1", outdir="out")
save(c, "fig2", spec="journal-1col", outdir="out")   # 期刊單圖:不加標題

# 經營彙報:同樣的 figure(),但必須寫口徑註腳
k = B.kpi_cards([dict(label="GMV (MTD)", value=16.5e6, prev=15.9e6, target=57e6, progress=0.29)])
figure([[(k, 1)]], spec="doc", size=(6.5, 2), name="kpi", outdir="out",
       caption="Basis: paid-order GMV (USD); MTD to Oct 9 vs Sep 1–9.")

選函數

按要比較的東西找行。C. = charts.py,B. = biz.py;標 ★ 的是為經營彙報設計的函數,但任何場景都能用。

要比較的函數重點
排名、一個時點的構成、正負值C.bar_rank(df, cat, value, highlight=, top_n=, fmt=)自動排序;超過 top_n 合併為「其他/Other」;值寫在條內或條外
誰帶來了變化(分維度的規模 + 增速 + 貢獻)★ B.contrib_table(df, dim, prev, cur, top_n=, growth_alert=)規模資料條、▲▼ 增速、變化量、佔總變化;合計行置頂
趨勢:1–N 條線放一起C.line_trend(df, x, y, group=, highlight=, band=, ref=)線尾直接標註;按月自動兩層月份標籤;缺失斷線;band= 信賴帶
趨勢:多組分開看C.small_multiples(df, x, y, facet, highlight=)所有格子同一 y 刻度;灰色背景線是其他組
趨勢:一個經營指標 + 同期對比★ B.trend(df, date, value, compare="month"/"year", events=, as_of=, period_end=)自動疊加對齊好的同期線;標註事件;未完結時期虛線 + 預估
進度:期內累計 vs 目標★ B.cumulative_vs_target(df, date, value, target, start, end, as_of=, prior=)累計實際、目標進度線、上期累計、按日均折算的預估
實際 vs 目標(多行)★ B.bullet(df, cat, actual, target, progress=)實際條 + 目標豎線 + 「按進度應完成」虛線;落後進度的行變紅
實際 vs 區間(預測區間、正常範圍)C.range_dot(df, cat, low, high, value, flag="below", bench=)越界的行用焦點色;bench= 指定基準行
每組兩個值(前後、計畫與實際)C.dumbbell(df, cat, start, end, labels=, highlight=)空心點 = 起點,實心點 = 終點;按終點排序;每行帶變化率
首尾兩期,各組規模差異大C.slope(df, cat, start, end, mode="change", groups=)mode="change" 時所有線從 0% 出發;起點為 0 的行剔除並打 WARNING
增減橋接:上期 → 各項貢獻 → 本期C.waterfall(df, cat, delta, start=, end_label=)增減預設中性色 + 正負號;好壞確有業務意義時傳 up=PAL.good, down=PAL.bad
指標拆解(GMV = 使用者 × 頻次 × 客單價)★ B.driver_tree(nodes)每個節點寫目前值、▲▼ 變化、貢獻(乘法用對數份額,加法用變化量)
構成:多組各自的佔比C.stacked_100(df, cat, part, value, order=, focus_cat=)部分按自然順序、單色漸變;圖例行在 chart.legend
二維表(組 × 時間、組 × 組)C.heatmap(df, x, y, value, fmt=)白到單色,色階取 2%–98% 分位;深色格白字
規模 + 比率(申請數與通過率)★ B.bar_line_dual(df, x, bar, line, line_range=)唯一允許的雙軸:規模柱(左)+ 比率線(右),比率軸從 0 或固定範圍
分佈C.distribution(df, value, group=, kind="box"/"strip"/"hist")按中位數排序;鬚線 1.5×IQR;點多時抽樣
兩個連續變數的關係C.scatter(df, x, y, label=, fit="linear", groups=, x_title=, y_title=)OLS + 信賴帶,顯示 R²;只標高亮的點;groups= 時圖例在 chart.legend
估計值與信賴區間C.point_range(df, cat, est, low, high, ref=0)森林圖;右側寫「估計值 [下限, 上限]」
幾個關鍵數字的摘要(報表頂部)★ B.kpi_cards([dict(label, value, prev, target, progress, spark, …), …])大數字、▲▼ 變化、達成率與時間進度、迷你趨勢線;異常才變紅

★ 函數的參數細節(kpi_cards 的欄位、driver_tree 的節點結構、trend 的 events= / as_of=、cumulative_vs_target 的 prior=、異常規則、同期對齊方式)見 references/business.md。

兩行都像時怎麼選:

  • B.contrib_table vs C.waterfall:要同時看規模、增速和佔比,或維度超過約 8 個 → 表;要講「上期怎麼一步步變成長這期」、項目不超過約 8 個 → 瀑布圖。歸因頁常兩個並排。
  • C.line_trend vs C.small_multiples:有一條要突出的線、或不超過 4 條 → line_trend(超過 4 條加 highlight=);要逐組看形狀、各組地位相當 → small_multiples。期刊單欄(3.5in)放不下 5 個以上分面,用 line_trend + highlight=。
  • 每組兩個值,先看各組規模:相近(最大 / 最小 < 約 10 倍)→ C.dumbbell。相差一個數量級以上時,小組在共同刻度上看不清,改為直接畫變化率:不超過約 8 組 → C.slope(mode="change");更多組 → 先算出變化率列,用 C.bar_rank(df, cat, "chg", fmt="pct"),絕對規模寫進標籤或旁邊的表。

所有函數共用的參數和回傳值:

  • highlight=:類別名、清單或函數。被選中的項用焦點色,其餘變灰,類別名也會同色加粗。
  • fmt=:"num"、"pct"、"pp"、"int"、"raw"、格式字串或函數;nd= 控制小數位。
  • 回傳的是面板物件,直接放進 figure() / save();chart + plotnine層 可繼續疊加;chart.legend 是一行彩色詞圖例,titled() 會自動用上。

沒有合適的函數時,自己畫:

  1. 用 C.Frame(w, h, xr, yr, left=…, …) 按文字的實際寬度(text_width)預留四周空白;軸範圍用 C.axis_range(lo, hi)。
  2. 在 C.Canvas() 上用 rect、seg、pt、text、line、band 畫元素,用 cv.plot(fr) 產生圖。
  3. 包成 C.Chart(build),其中 build(w, h) 在拿到最終尺寸後才計算佈局。

不要在程式碼裡寫死座標或軸範圍。

兩種場景的慣例

函數本身在兩種場景下畫法一樣;不同的是這些預設慣例。程式碼裡沒有「場景」參數:★ 函數自帶經營彙報的畫法(▲▼、異常才著紅),並會讓 figure() 在缺口徑註腳時報 WARNING;同一頁上的 C. 圖則由你按下表的慣例傳參(如 caption=、highlight=、up= / down= 的顏色)。

項通用 / 出版經營彙報
漲跌好壞確有意義時才用 good / bad,且必須配正負號或文字一律 ▲▼ + 中性色,不用紅綠表示漲跌
什麼時候著紅只在表示「壞」時只有被判為異常的(落後進度 / 超閾值 / 超出歷史 ±2σ),好方向的異常只加粗
要讀者「看這裡」(如拖累成長的渠道)highlight=(焦點色橙)同左;橙色只表示「看這裡」,不表示好壞,是否異常仍由 ▲▼ 和紅色表達
口徑註腳建議寫必填(caption=):分子分母、幣種、時間窗口、對比基準;缺了 figure() 會報 WARNING
對比基準按分析需要預設月環比、本月累計 vs 上月同期(同樣天數)、目標達成;日度流量指標按星期對齊同期
未完結的當期淺色或虛線 + 「截至 X 日」同左,並畫按進度折算的全期預估
數字按領域習慣金額 K/M/B;比率變化用 pp,增速用 %;單位寫在標題或註解裡
雙軸不用只允許「規模柱 + 比率線」(B.bar_line_dual)
圓餅圖 / 環圈圖盡量不用禁用
常用規格doc、web、journal-*PPT 用 slide;飛書 / Lark / Notion 文件或聊天裡用 doc(PNG 插圖);使用者沒說放哪時用 doc

原因與反例見 references/rules.md(經營彙報部分是 B1–B7)。

輸出規格(spec=)

spec尺寸(英寸)正文字型大小預設格式用途
slide / slide-4x313.33×7.5 / 10×7.512 / 11.5輪廓 SVG、可編輯 SVG、PNG(150dpi)彙報幻燈片
web8×4.510.5SVG、PNG(200dpi)網頁、郵件
doc6.5×49PDF、SVG、PNG(300dpi)文件正文裡的一張圖
a4 / letter整頁10PDF、SVG、PNG整頁報告
journal-1col / -1.5col / -2col3.5 / 5.35 / 7.2 寬7–7.5PDF、SVG、PNG(600dpi)期刊(89 / 136 / 183 mm)
  • size=(w, h) 可以覆蓋預設尺寸,比如更高的期刊圖、更矮的 KPI 卡片行。
  • 字型大小、線寬和點的大小都會隨規格縮放;PDF 用 Type 42 嵌入字型,滿足期刊要求。
  • 期刊圖不加標題,圖註寫在正文裡:單張圖用 save(chart, name, spec="journal-1col");多面板圖用 figure(rows, spec="journal-2col"),不傳 title=,面板標號用 titled(chart, title="a")。
  • figure() 出一整頁(可有標題、副標題、口徑註腳、多個面板);save(chart, "fig2", spec="journal-1col") 只導出一張圖,沒有頁標題和註腳。經營彙報要註腳,所以用 figure()。
  • 兩種 SVG:<name>.svg 的文字轉成了輪廓,任何地方開啟都一樣;<name>_editable.svg 保留文字,可在 Figma / PPT 裡改字,但對方電腦要有同樣的字型。PPT 裡要改字用後者,否則用前者或 PNG。
  • slide / web 預設不出 PDF,需要時傳 formats=("pdf", "png")。

組合多面板

  • figure(rows, title=, subtitle=, caption=, spec=):rows=[[(panel, span), …], …],span 是同一行內的相對寬度。
  • 行高預設自動分配:只放文字或表格的行按內容取高度,其餘行平分剩下的空間。
  • titled(chart, title=, legend=, note=):在圖上方加面板標題、圖例行和註解;legend 不填時自動用 chart.legend。
  • 標題支援富文字標記:**粗體**;[短語](focus) 表示加粗並著色。顏色可以寫 focus、brand、good、bad、muted、ink 或 #hex。
  • 其他面板:text_panel(para(...)) 放段落,table_panel(rows, header=) 放小表(數字列自動右對齊),column(a, b) 把兩個面板上下疊放。
  • 每個面板都按最終尺寸單獨渲染,所以字型大小就是實際磅值;拼接 SVG 時會自動給各面板的元素 id 加前綴,避免衝突。

視覺規則(函數已預設執行,自己畫圖時照做)

  • 顏色分層: focus(橙)標出唯一要看的东西,本身不代表好壞;brand(深藍)用於常規系列;灰色用於背景和參照;good / bad 的用法按場景(見上表)。
  • 同色即同義。 文字關鍵詞與對應資料用同一種顏色;意義不同的元素不要共用顏色。
  • 文字要讀得清。 文字對比度至少 4.5:1。焦點色做文字時用 readable(color) 自動加深;深色底上的字用 ink_on(fill)。函數會自動處理,自己畫圖時 Canvas.text 也會檢查並警告。
  • 用直接標註代替圖例。 軸標題不能是欄位名。所有柱子都標了數值時,就不要 y 軸和網格。
  • 排序要有意義。 有自然順序的(階段、等級)按自然順序,否則按數值排序;焦點項放在最顯眼的位置;不要按字母序。
  • 座標軸。 長條圖必須從 0 開始;折線圖資料靠近 0 時從 0 開始(zero="auto");相關的幾個面板用同一刻度;雙軸只有「規模柱 + 比率線」一種。
  • 精度。 夠區分就行。數字一律由資料計算,用 pct / pp / num / fmt_value 格式化,不要手寫。
  • 按要比較的東西選圖: 佔比變化不要用絕對值堆積柱;多組構成不要畫多個圓餅圖;超過 4 條線時用 highlight= 突出重點或改用小多圖;樹狀圖、雷達圖比較兩項要超過約 2 秒就換成橫條。
  • 資料問題要在圖上標出。 未完結的當期用淺色或虛線並註明「截至 X 日」;小樣本寫出 n;缺失時斷線,不要連起來。

細節與原因見 references/rules.md;SWD 改圖案例的設計做法見 references/swd_practices.md。

語言與排版

  • set_lang("zh" | "en") 會同時切換預設字型、月份標籤(中文「1月\n2026」,英文「Jan\n2026」)和「其他/Other」。金額單位兩種語言都預設 K/M/B,需要萬/億時用 set_number_style("cn")。含中文的標籤自動改用 CJK 字型。T("其他", "Other") 按目前語言取字串。
  • 中文字串會自動經過 zh():標點轉為全形,去掉全形標點旁多餘的空格,網址和縮寫保持不變。
  • 換行規則:數字和英文單詞不拆開;標點不出現在行首;左括號不留在行尾。
  • 啟動時會印出所用字型。出現 WARNING: no CJK font 時需要安裝 Noto Sans SC。

交付前檢查

自動檢查(看 figure() 的輸出或 .qa):

  • QA … ok:文字沒有重疊、沒有跑出面板、沒有被繪圖區裁切。
  • 沒有對比度警告,也沒有 rows do not fit、labels take most of the panel、cells too small、basis caption 這類警告。

人工檢查(開啟 PNG):

  • 遮住標題,只看圖,能說出要表達的內容嗎?焦點只有一個嗎?
  • 標題和標註裡的數字,與圖上的數值來自同一個變數嗎?
  • 圖形匹配要比較的東西嗎(見選函數表)?排序有意義嗎?相關面板刻度一致嗎?
  • 顏色用法符合場景慣例嗎?去掉顏色後,好壞 / 漲跌還能從符號或文字讀出來嗎?
  • 沒有預設圖例、欄位名、3D 效果或漸層嗎?
  • 尺寸和字型大小符合目標規格嗎(期刊正文 7pt 左右,幻燈片 12pt 以上)?需要 PDF 的已經導出了嗎?

經營彙報另加:

  • 口徑註腳寫全了嗎?和其他報表對過數嗎(同一份報告裡,同一個數在各頁一致)?
  • 單位、小數位、格式全圖統一嗎?

環境與測試

  • 依賴: plotnine>=0.15,<0.16(已在 0.15.8 + matplotlib 3.11 下驗證)、pandas、pillow,匯出 PDF 還要 pypdf。程式碼在 scripts/viz_common.py(底層工具、規格、匯出、QA)、scripts/charts.py(C.)和 scripts/biz.py(B.)。
  • 輸出位置: 把 outdir 設成使用者能看到的目錄;不要寫進 skill 目錄本身。
  • 範例圖庫: uv run references/gallery.py [outdir](每個 C. 函數,中文幻燈片 + 英文期刊);uv run references/business_gallery.py [outdir](每個 B. 函數,中文幻燈片 + 英文文件)。預設輸出到目前目錄的 out/。
  • 回歸測試: uv run references/tests/edge_cases.py 用 38 組邊界資料,在兩種規格下(共 76 項)檢查函數的表現和 QA 是否符合預期;改了圖表函數後必須重跑,全部通過才能發布。
  • 沙箱裡裝不上 plotnine 時: 先用 PYTHONPATH=<skill>/scripts/plotnine_preview 的 matplotlib 模擬器預覽版式和跑 QA(線寬和裁切規則與 plotnine 一致)。模擬器的 QA ok 不算交付標準:把腳本交給使用者本機 uv run,並明確告訴使用者這張圖還沒有用真 plotnine 渲染和檢查過。

相關技能