Plotting Advisor
Rules engine for Python plotting. Use BEFORE writing any plotting code. Returns a structured checklist and a paste-ready YAML decision card. Library mechanics are delegated to scientific-skills:matplotlib, seaborn, or plotly.
When to Use This Skill
Use this skill when:
- A user asks to plot, visualize, chart, or graph any data
- Another agent (
bioinformatician, calculator, notebook-writer) is about to render a figure
- A figure already exists and the user asks "is this plot OK?" / "review this figure" / "make this better" → invokes the lint flow
- Intent is documented (compare, distribution, trend, composition, relation). If not, ask one question before recommending.
When NOT to Use This Skill
Do NOT use this skill when:
- The user only needs
matplotlib/seaborn/plotly syntax mechanics (e.g., "how do I set a log axis") → use scientific-skills:matplotlib
- The plot is a one-off exploratory glance during debugging (advisor overhead not justified)
- The output is a domain-specific established convention with no flexibility (e.g., a regulatory-required forest plot in clinical reporting) — note the convention and step aside
- The work is non-Python (R/ggplot, D3, etc.) — out of scope
Workflow
Two flows, sharing the rule base in references/.
Advisor flow (default)
Intake — gather from the caller:
- Data shape (variable types, n, dimensionality, missingness)
- Intent — one of
{compare, distribution, trend, composition, relation, ranking, geographic, network}
- Audience (print, slides, paper figure, dashboard)
- Constraints (caller-specified chart type, palette, B/W output, journal style)
- If intent is missing, ask one question, then proceed.
Chart selection — open references/chart-selection.md, walk the decision tree for (data shape × intent). Output: recommended chart + 1-2 alternatives + the rule that selected it.
Palette selection — open references/palettes.md:
- Categorical ≤8 → Okabe-Ito
- Continuous unipolar → viridis / cividis
- Diverging around meaningful midpoint → ColorBrewer RdBu / PuOr
- Decorative color → single neutral; reserve color for emphasis
Axis & scale (inline rules):
- Linear axis includes zero unless truncation is justified and annotated
- Log scale only when data spans ≥2 orders of magnitude OR the underlying process is multiplicative
- ~5-7 major ticks per axis
- Shared axes for small multiples
- Date axes redundantly labeled if range crosses years
Annotation & labels (inline rules):
- Axis labels with units in parentheses:
Time (h)
- Direct-label series when ≤5 (Tufte) instead of legend
- Title states the finding; subtitle states the context
- Annotate outliers, intervention points, baselines
Accessibility floor (inline rules, always apply):
- Colorblind-safe palette OR redundant encoding (shape / line style / position)
- Minimum font size: 8pt print / 14pt slide
- Avoid red/green as the only encoding distinction
- Test against deuteranopia simulation when categorical color count ≥3
Anti-pattern vetoes (inline, refuse outright; full list in references/anti-patterns.md):
- No 3D bar/pie/surface
- No dual y-axes
- No rainbow palette (
jet, gist_rainbow, hsv) on continuous data
- No truncated baseline without annotation
- No pie with >5 slices
Output — produce the structured checklist + decision card (formats below).
Lint flow (when the caller hands over a figure)
Input (mutually exclusive modes):
python3 scripts/style_lint.py --image figure.png
python3 scripts/style_lint.py --figure-spec figure.json (caller produces JSON via figure_spec.extract_spec(fig))
python3 scripts/style_lint.py --describe "<text description of the figure>"
Inspection — style_lint.py extracts properties from the input (image / spec / description) and applies the rule base.
Rule check — violations grouped by severity (critical / major / minor) with one-line fixes and reference anchors.
Exit code:
- Default: exit 0 (advisory)
--strict: exit 2 if any critical violation present
The skill never imports matplotlib to render anything.
Output Format
Advisor — Checklist + Decision card
## Plotting Advisor: [chart type recommendation, in one phrase]
### Intent & data
- Intent: compare across 4 conditions
- Data: continuous response, ~30 obs/group, balanced, no missing
- Audience: paper figure (300 dpi print)
### Recommended chart: dot plot with median bar
- Rule: Cleveland 1985 — position encodes more accurately than length/angle/area
- Alternatives considered: box plot (loses obs at n=30); violin (overstates smoothness); raincloud (complexity not justified)
### Palette: Okabe-Ito (categorical, 4 colors)
- Hex: #E69F00, #56B4E9, #009E73, #F0E442
- Why: colorblind-safe, ≤8 levels supported, perceptually balanced
- Reference: references/palettes.md#okabe-ito
### Axes & scale
- Y: linear, include zero (response is a count); 5 major ticks
- X: categorical, ordered by condition (control first)
- Units: `Response (counts/min)`
### Annotation
- Direct-label each group (n=4 ≤5 rule)
- Annotate sample size per group below x-axis
- Title states finding; subtitle states context
### Accessibility floor
- ✓ Colorblind-safe palette
- ✓ Redundant encoding (group also encoded by x-position)
- ✓ Font size 9pt (print floor 8pt)
- ✓ No red/green-only distinction
### Anti-pattern check
- ✓ No 3D, no dual axes, no rainbow, no truncated baseline, no pie
### Decision card
```yaml
chart: dot_plot_with_median
library: seaborn
palette:
type: categorical
name: okabe_ito
hex: ["#E69F00", "#56B4E9", "#009E73", "#F0E442"]
axes:
x: {type: categorical, order: [control, t1, t2, t3]}
y: {type: linear, include_zero: true, label: "Response (counts/min)"}
encoding:
position: condition
color: condition # redundant with position — fine
annotation:
direct_labels: true
sample_size_per_group: true
title: "<state finding>"
subtitle: "<state context>"
accessibility:
colorblind_safe: true
min_font_pt: 9
output:
dpi: 300
format: pdf
delegate_to: scientific-skills:seaborn
Every recommendation includes a rule citation (short tag like `[Cleveland 1985]` or a `references/<file>.md#anchor` pointer). Full bibliography lives in `references/sources.md`.
### Lint output
```markdown
## Plotting Advisor: figure check — N issues found
### Critical (n)
- <issue>. <impact>.
- Fix: <one-line fix>
- Rule: references/<file>.md#<anchor>
### Major (n)
- ...
### Minor (n)
- ...
Plus JSON to stdout for machine consumption (see scripts/style_lint.py --help).
Integration with Existing Skills
scientific-skills:matplotlib / seaborn / plotly — explicit delegate_to field in the decision card names the right software skill. This advisor never duplicates their syntax content.
bioinformatician — its visualization reference can point at references/scientific-conventions.md (follow-up PR).
notebook-writer — invoke this skill for every plotting cell.
editor / consistency-auditor — precedent for the rules-engine-as-skill pattern.
References
references/chart-selection.md — decision tree by intent × data shape (general only)
references/palettes.md — Okabe-Ito, viridis family, ColorBrewer, Tol bright
references/anti-patterns.md — 12-15 anti-patterns with citations
references/accessibility.md — WCAG, colorblind sim, fonts, journal overrides
references/interactive-adaptation.md — how rules shift for plotly/bokeh/altair
references/scientific-conventions.md — volcano, UMAP, Manhattan, forest, ROC, etc.
references/sources.md — full bibliography
Scripts
scripts/palettes.py — canonical hex lists, is_colorblind_safe_categorical(hex_list) helper
scripts/figure_spec.py — extract_spec(fig) for matplotlib Figure → JSON; CLI mode also accepts a pickled figure
scripts/style_lint.py — main CLI with three input modes; --strict for CI use
Run tests with:
python3 -m unittest discover -s claude-config/skills/plotting-advisor/scripts/tests -v
1---2name: plotting-advisor3description: Use BEFORE writing any Python plotting code (matplotlib, seaborn, plotly) — recommends chart type, color palette, axis treatment, and accessibility choices using Tufte, Cleveland, Wong, and Wilke principles. Also lints existing figures on request. Triggers on 'plot', 'chart', 'figure', 'heatmap', or any mention of matplotlib/seaborn/plotly.4---56# Plotting Advisor78Rules engine for Python plotting. Use BEFORE writing any plotting code. Returns a structured checklist and a paste-ready YAML decision card. Library mechanics are delegated to `scientific-skills:matplotlib`, `seaborn`, or `plotly`.910## When to Use This Skill1112Use this skill when:1314- A user asks to plot, visualize, chart, or graph any data15- Another agent (`bioinformatician`, `calculator`, `notebook-writer`) is about to render a figure16- A figure already exists and the user asks "is this plot OK?" / "review this figure" / "make this better" → invokes the lint flow17- Intent is documented (compare, distribution, trend, composition, relation). If not, ask one question before recommending.1819## When NOT to Use This Skill2021Do NOT use this skill when:2223- The user only needs `matplotlib`/`seaborn`/`plotly` syntax mechanics (e.g., "how do I set a log axis") → use `scientific-skills:matplotlib`24- The plot is a one-off exploratory glance during debugging (advisor overhead not justified)25- The output is a domain-specific established convention with no flexibility (e.g., a regulatory-required forest plot in clinical reporting) — note the convention and step aside26- The work is non-Python (R/ggplot, D3, etc.) — out of scope2728## Workflow2930Two flows, sharing the rule base in `references/`.3132### Advisor flow (default)33341. **Intake** — gather from the caller:35 - Data shape (variable types, n, dimensionality, missingness)36 - Intent — one of `{compare, distribution, trend, composition, relation, ranking, geographic, network}`37 - Audience (print, slides, paper figure, dashboard)38 - Constraints (caller-specified chart type, palette, B/W output, journal style)39 - If intent is missing, ask **one** question, then proceed.40412. **Chart selection** — open `references/chart-selection.md`, walk the decision tree for (data shape × intent). Output: recommended chart + 1-2 alternatives + the rule that selected it.42433. **Palette selection** — open `references/palettes.md`:44 - Categorical ≤8 → Okabe-Ito45 - Continuous unipolar → viridis / cividis46 - Diverging around meaningful midpoint → ColorBrewer RdBu / PuOr47 - Decorative color → single neutral; reserve color for emphasis48494. **Axis & scale** (inline rules):50 - Linear axis includes zero unless truncation is justified and annotated51 - Log scale only when data spans ≥2 orders of magnitude OR the underlying process is multiplicative52 - ~5-7 major ticks per axis53 - Shared axes for small multiples54 - Date axes redundantly labeled if range crosses years55565. **Annotation & labels** (inline rules):57 - Axis labels with units in parentheses: `Time (h)`58 - Direct-label series when ≤5 (Tufte) instead of legend59 - Title states the finding; subtitle states the context60 - Annotate outliers, intervention points, baselines61626. **Accessibility floor** (inline rules, always apply):63 - Colorblind-safe palette OR redundant encoding (shape / line style / position)64 - Minimum font size: 8pt print / 14pt slide65 - Avoid red/green as the only encoding distinction66 - Test against deuteranopia simulation when categorical color count ≥367687. **Anti-pattern vetoes** (inline, refuse outright; full list in `references/anti-patterns.md`):69 - No 3D bar/pie/surface70 - No dual y-axes71 - No rainbow palette (`jet`, `gist_rainbow`, `hsv`) on continuous data72 - No truncated baseline without annotation73 - No pie with >5 slices74758. **Output** — produce the structured checklist + decision card (formats below).7677### Lint flow (when the caller hands over a figure)78791. **Input** (mutually exclusive modes):80 - `python3 scripts/style_lint.py --image figure.png`81 - `python3 scripts/style_lint.py --figure-spec figure.json` (caller produces JSON via `figure_spec.extract_spec(fig)`)82 - `python3 scripts/style_lint.py --describe "<text description of the figure>"`83842. **Inspection** — `style_lint.py` extracts properties from the input (image / spec / description) and applies the rule base.85863. **Rule check** — violations grouped by severity (critical / major / minor) with one-line fixes and reference anchors.87884. **Exit code**:89 - Default: exit 0 (advisory)90 - `--strict`: exit 2 if any critical violation present9192The skill never imports matplotlib to render anything.9394## Output Format9596### Advisor — Checklist + Decision card9798```markdown99## Plotting Advisor: [chart type recommendation, in one phrase]100101### Intent & data102- Intent: compare across 4 conditions103- Data: continuous response, ~30 obs/group, balanced, no missing104- Audience: paper figure (300 dpi print)105106### Recommended chart: dot plot with median bar107- Rule: Cleveland 1985 — position encodes more accurately than length/angle/area108- Alternatives considered: box plot (loses obs at n=30); violin (overstates smoothness); raincloud (complexity not justified)109110### Palette: Okabe-Ito (categorical, 4 colors)111- Hex: #E69F00, #56B4E9, #009E73, #F0E442112- Why: colorblind-safe, ≤8 levels supported, perceptually balanced113- Reference: references/palettes.md#okabe-ito114115### Axes & scale116- Y: linear, include zero (response is a count); 5 major ticks117- X: categorical, ordered by condition (control first)118- Units: `Response (counts/min)`119120### Annotation121- Direct-label each group (n=4 ≤5 rule)122- Annotate sample size per group below x-axis123- Title states finding; subtitle states context124125### Accessibility floor126- ✓ Colorblind-safe palette127- ✓ Redundant encoding (group also encoded by x-position)128- ✓ Font size 9pt (print floor 8pt)129- ✓ No red/green-only distinction130131### Anti-pattern check132- ✓ No 3D, no dual axes, no rainbow, no truncated baseline, no pie133134### Decision card135136```yaml137chart: dot_plot_with_median138library: seaborn139palette:140 type: categorical141 name: okabe_ito142 hex: ["#E69F00", "#56B4E9", "#009E73", "#F0E442"]143axes:144 x: {type: categorical, order: [control, t1, t2, t3]}145 y: {type: linear, include_zero: true, label: "Response (counts/min)"}146encoding:147 position: condition148 color: condition # redundant with position — fine149annotation:150 direct_labels: true151 sample_size_per_group: true152 title: "<state finding>"153 subtitle: "<state context>"154accessibility:155 colorblind_safe: true156 min_font_pt: 9157output:158 dpi: 300159 format: pdf160delegate_to: scientific-skills:seaborn161```162```163164Every recommendation includes a rule citation (short tag like `[Cleveland 1985]` or a `references/<file>.md#anchor` pointer). Full bibliography lives in `references/sources.md`.165166### Lint output167168```markdown169## Plotting Advisor: figure check — N issues found170171### Critical (n)172- <issue>. <impact>.173 - Fix: <one-line fix>174 - Rule: references/<file>.md#<anchor>175176### Major (n)177- ...178179### Minor (n)180- ...181```182183Plus JSON to stdout for machine consumption (see `scripts/style_lint.py --help`).184185## Integration with Existing Skills186187- **`scientific-skills:matplotlib` / `seaborn` / `plotly`** — explicit `delegate_to` field in the decision card names the right software skill. This advisor never duplicates their syntax content.188- **`bioinformatician`** — its visualization reference can point at `references/scientific-conventions.md` (follow-up PR).189- **`notebook-writer`** — invoke this skill for every plotting cell.190- **`editor` / `consistency-auditor`** — precedent for the rules-engine-as-skill pattern.191192## References193194- `references/chart-selection.md` — decision tree by intent × data shape (general only)195- `references/palettes.md` — Okabe-Ito, viridis family, ColorBrewer, Tol bright196- `references/anti-patterns.md` — 12-15 anti-patterns with citations197- `references/accessibility.md` — WCAG, colorblind sim, fonts, journal overrides198- `references/interactive-adaptation.md` — how rules shift for plotly/bokeh/altair199- `references/scientific-conventions.md` — volcano, UMAP, Manhattan, forest, ROC, etc.200- `references/sources.md` — full bibliography201202## Scripts203204- `scripts/palettes.py` — canonical hex lists, `is_colorblind_safe_categorical(hex_list)` helper205- `scripts/figure_spec.py` — `extract_spec(fig)` for matplotlib Figure → JSON; CLI mode also accepts a pickled figure206- `scripts/style_lint.py` — main CLI with three input modes; `--strict` for CI use207208Run tests with:209210```bash211python3 -m unittest discover -s claude-config/skills/plotting-advisor/scripts/tests -v212```