storytelling-viz:すぐに使える図
目標はレポート、ドキュメント、ジャーナルにそのまま載せられる一枚の図であり、でたらめに描かれた棒グラフではありません。以下の3点を満たす必要があります。
- グラフの種類が適切であること。 比較したいものにグラフの種類が一致し、焦点が明確で、凡例ではなく直接ラベルが付けられていること。
- 実データで壊れないこと。 長いラベル、負の値、欠損値、カテゴリ数の多さ、外れ値があっても正しく表示されること。
- 出力仕様に適合していること。 サイズ、フォントサイズ、線幅、フォーマット、フォントの埋め込みがすべて仕様を満たしていること。
このスキルは図だけを扱います。トピックの選定、分析、レポートの構成はこのスキルの範囲外です。
ワークフロー
- シナリオを判断します。 シナリオによって、色、フッター、数字などのデフォルトの規則が決まります(「2つのシナリオの規則」を参照)。
- ビジネスレビュー: ビジネス関係者や経営陣向けのビジネスKPIの図。頻度や場面(週報、月報、日次ダッシュボード、異常原因分析、経営会議・取締役会・戦略会議の経営データページ)は問いません。判断基準は「内容がビジネスKPIであり、読者がそれに基づいてビジネス上の意思決定を行うかどうか」であり、「週報かどうか」ではありません。
- 汎用/出版: 分析・研究ドキュメント、実験結果、Webページ、論文の図など。
- 迷った場合はビジネスレビューとして扱います(規則がより厳格で、フッターの行が1行多くなるだけです)。ビジネスレビューシナリオでは、サンプルコードで十分に見えても、
references/business.mdを最初に読む必要があります。
- 「比較したいもの」に従って関数を選択します。 下の関数選択表を参照。両方のシナリオで同じ表を使用します。
- 出力仕様を選択します。
spec=で指定します(スライド、文書、ジャーナルなど)。 - 図を出力し、自動チェック結果を確認します。
figure()/save()のレンダリング後、QA … okまたは各警告のリストが表示されます。戻り値の.qaは同じ問題リストです(空リスト = 合格)。警告がある場合は修正し、そのまま納品してはいけません。 - 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=) | 線の端に直接ラベル; 月単位で自動的に2段階の月ラベル; 欠損値は線を断線; band= で信頼帯 |
| トレンド: 複数のグループを個別に表示 | C.small_multiples(df, x, y, facet, highlight=) | すべてのパネルで同じY軸スケール; 灰色の背景線は他のグループ |
| トレンド: 1つのビジネスKPI + 前年同期比 | ★ 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= で基準行を指定 |
| グループごとの2つの値(前後、計画と実績) | C.dumbbell(df, cat, start, end, labels=, highlight=) | 白抜き点 = 開始点、塗りつぶし点 = 終了点; 終了点でソート; 各行に変化率を表示 |
| 最初と最後の2期間、グループ間の規模の差が大きい場合 | 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 に |
| 2次元表(グループ × 時間、グループ × グループ) | 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; 点が多い場合はサンプリング |
| 2つの連続変数の関係 | 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 を参照してください。
2つの行が両方とも適切に見える場合の選択基準:
B.contrib_tablevsC.waterfall: 規模、成長率、割合を同時に見たい場合、またはディメンションが約8個を超える場合 → 表。「前期から今期への段階的な変化」を説明したい場合、項目が約8個以下の場合 → ウォーターフォール図。原因分析ページでは、これら2つを並べて使うことがよくあります。C.line_trendvsC.small_multiples: 強調したい線が1本ある場合、または4本以下の場合 →line_trend(4本を超える場合はhighlight=を追加)。グループごとの形状を個別に見たい場合、各グループの重要度が同等の場合 →small_multiples。ジャーナルの1カラム(3.5インチ)では5つ以上のファセットを収めるのは難しいため、line_trend+highlight=を使用します。- グループごとに2つの値がある場合、まず各グループの規模を確認します。規模が近い場合(最大/最小 < 約10倍)→
C.dumbbell。規模が1桁以上異なる場合、小規模グループは共通スケールで見えにくくなるため、代わりに変化率を直接描画します。グループ数が約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()が自動的に使用します。
適切な関数がない場合、自分で描画します:
C.Frame(w, h, xr, yr, left=…, …)を使用し、テキストの実際の幅(text_width)に基づいて周囲の余白を予約します。軸の範囲はC.axis_range(lo, hi)を使用します。C.Canvas()上でrect、seg、pt、text、line、bandを使用して要素を描画し、cv.plot(fr)で図を生成します。C.Chart(build)でラップします。ここでbuild(w, h)は最終的なサイズが確定してからレイアウトを計算します。
コード内で座標や軸の範囲をハードコードしてはいけません。
2つのシナリオの規則
関数自体は両方のシナリオで同じ描画を行います。異なるのは以下のデフォルトの規則です。コードには「シナリオ」パラメータはありません: ★ 関数はビジネスレビューの描画方法(▲▼、異常のみ赤色)を内蔵しており、口径フッターがない場合、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。Feishu / Lark / Notion ドキュメントやチャットでは doc(PNG挿絵)。ユーザーが配置場所を指定しない場合は doc を使用 |
理由とアンチパターンについては references/rules.md を参照してください(ビジネスレビュー部分は B1–B7)。
出力仕様(spec=)
| spec | サイズ(インチ) | 本文フォントサイズ | デフォルト形式 | 用途 |
|---|---|---|---|---|
slide / slide-4x3 | 13.33×7.5 / 10×7.5 | 12 / 11.5 | アウトライン化SVG、編集可能SVG、PNG(150dpi) | プレゼンテーションスライド |
web | 8×4.5 | 10.5 | SVG、PNG(200dpi) | Webページ、メール |
doc | 6.5×4 | 9 | PDF、SVG、PNG(300dpi) | ドキュメント本文の図 |
a4 / letter | ページ全体 | 10 | PDF、SVG、PNG | フルページレポート |
journal-1col / -1.5col / -2col | 幅 3.5 / 5.35 / 7.2 | 7–7.5 | PDF、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()は1ページ全体を出力します(タイトル、サブタイトル、口径フッター、複数パネルを含むことができます)。save(chart, "fig2", spec="journal-1col")は単一の図のみをエクスポートし、ページタイトルやフッターは含みません。ビジネスレビューではフッターが必要なため、figure()を使用します。- 2種類の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)で2つのパネルを上下に重ねます。 - 各パネルは最終的なサイズに基づいて個別にレンダリングされるため、フォントサイズは実際のポイントサイズになります。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項目を比較するのに約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()を通過します。句読点は全角に変換され、全角句読点の隣の余分なスペースは削除され、URLや略語はそのまま保持されます。 - 改行規則: 数字と英単語は分割されません。句読点は行頭に配置されません。左括弧は行末に残りません。
- 起動時に使用されているフォントが表示されます。
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を開く):
- タイトルを隠して、図だけを見て、伝えたい内容を言い当てられますか?焦点は1つだけですか?
- タイトルやラベルの数字は、図の値と同じ変数から来ていますか?
- グラフの種類は比較したいものと一致していますか(関数選択表を参照)?ソートに意味はありますか?関連するパネルのスケールは一致していますか?
- 色の使い方はシナリオの規則に従っていますか?色を取り除いた後でも、良い/悪い、増加/減少を記号やテキストから読み取れますか?
- デフォルトの凡例、フィールド名、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をユーザーが参照できるディレクトリに設定します。スキルディレクトリ自体に書き込まないでください。 - サンプルギャラリー:
uv run references/gallery.py [outdir](各C.関数、中国語スライド + 英語ジャーナル)。uv run references/business_gallery.py [outdir](各B.関数、中国語スライド + 英語ドキュメント)。デフォルトではカレントディレクトリのout/に出力されます。 - 回帰テスト:
uv run references/tests/edge_cases.pyは38組のエッジケースデータを使用し、2つの仕様(合計76項目)で関数の動作とQAが期待通りかをチェックします。グラフ関数を変更した後は必ず再実行し、すべて合格してからリリースします。 - サンドボックスにplotnineがインストールできない場合: まず
PYTHONPATH=<skill>/scripts/plotnine_previewのmatplotlibモックを使用して、レイアウトのプレビューとQAを実行します(線幅とクリッピングルールはplotnineと一致)。モックのQA okは納品基準にはなりません。スクリプトをユーザーのローカル環境でuv runするよう指示し、このグラフがまだ実際のplotnineでレンダリングおよびチェックされていないことを明確に伝えます。