| name | scientific-visualization |
| description | Publication-ready data plots for CS papers (systems, ML, networking, security). Use when creating figures with multi-panel layouts, error bars, colorblind-safe palettes, log-scale axes, latency CDFs, throughput curves, training curves, ablation bars, or speedup comparisons for IEEE / ACM / USENIX / NeurIPS / ICML / ICLR submissions. Orchestrates matplotlib, seaborn, and plotly with venue-specific styling and PDF output that drops cleanly into LaTeX. For one-off exploration use seaborn or plotly directly; for system architecture diagrams use scientific-schematics instead. |
| license | MIT license |
| metadata | {"skill-author":"K-Dense Inc. (upstream); CS adaptation by superpower-writing"} |
Scientific Visualization (CS Edition)
Overview
Turn benchmark and experiment data into publication-quality figures for CS venues. This skill is opinionated for systems, ML, networking, and security papers: it covers latency CDFs, throughput curves, training-loss plots, ablation bars, speedup comparisons, and Pareto fronts. It replaces the upstream biology-leaning version (microscopy, gene expression, fluorophores) with CS conventions and CS venue specs (IEEE, ACM, USENIX, NeurIPS, ICML, ICLR, arXiv).
The plugin produces LaTeX-only manuscripts. PDF is the default figure format — it embeds losslessly into LaTeX via \includegraphics{...}, scales without artifacts, and matches every CS venue's preferred line-art format. Use PNG only for raster screenshots, profilers, or visualizations that already started as raster.
Scope
Use this skill when:
- Creating plots for a CS paper (latency, throughput, accuracy, loss, memory, F1, CDF, etc.)
- Targeting IEEE Transactions, an ACM conference (
acmart), a USENIX venue, NeurIPS / ICML / ICLR, or arXiv
- Building multi-panel figures with consistent styling
- Ensuring figures stay readable in grayscale and for colorblind reviewers
- Exporting at the right resolution and format for LaTeX inclusion
Do not use this skill for:
- Architecture / data-flow / pipeline schematics — use
superpower-writing:scientific-schematics
- Ad-hoc exploratory plots in a notebook — just import seaborn / plotly directly
- Tables — use LaTeX
booktabs directly; no Python needed
Quick Start
Single-column IEEE figure (most common case)
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams.update({
'figure.dpi': 100, 'savefig.dpi': 600,
'font.family': 'sans-serif', 'font.sans-serif': ['Arial', 'Helvetica'],
'font.size': 8, 'axes.labelsize': 9, 'axes.titlesize': 9,
'xtick.labelsize': 7, 'ytick.labelsize': 7, 'legend.fontsize': 7,
'axes.linewidth': 0.5, 'lines.linewidth': 1.5,
'axes.spines.top': False, 'axes.spines.right': False,
'savefig.format': 'pdf', 'savefig.bbox': 'tight',
})
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=[
'#0072B2', '#D55E00', '#009E73', '#CC79A7', '#E69F00', '#56B4E9'
])
fig, ax = plt.subplots(figsize=(3.5, 2.4))
x = np.array([1, 2, 4, 8, 16, 32])
baseline_p99 = np.array([12.4, 13.1, 14.8, 18.6, 27.3, 49.5])
proposed_p99 = np.array([10.2, 10.4, 11.1, 12.7, 16.4, 24.0])
ax.plot(x, baseline_p99, marker='o', label='Baseline')
ax.plot(x, proposed_p99, marker='s', label='Ours')
ax.set_xlabel('Concurrent clients')
ax.set_ylabel(r'p99 latency (ms)')
ax.set_xscale('log', base=2)
ax.legend(frameon=False, loc='upper left')
fig.savefig('latency_p99.pdf', bbox_inches='tight')
The output drops into LaTeX with \includegraphics[width=\columnwidth]{figures/latency_p99.pdf}.
Using a bundled style file
The plugin ships matplotlib style files under ${CLAUDE_PLUGIN_ROOT}/skills/scientific-visualization/assets/. Load one with the absolute path:
import os, matplotlib.pyplot as plt
PLUGIN = os.environ['CLAUDE_PLUGIN_ROOT']
plt.style.use(f'{PLUGIN}/skills/scientific-visualization/assets/ieee.mplstyle')
To make figures self-contained (no env-var dependency), copy the chosen style file into .writing/figures/ once and reference it locally.
Core Principles
1. Format and resolution
- Vector PDF for every plot. Embeds in LaTeX without rasterization, scales perfectly. Use
fig.savefig('foo.pdf', bbox_inches='tight').
- PNG at 300 – 600 DPI only for raster content (screenshots, profilers, heatmaps with thousands of cells where a vector explodes). Set
savefig.dpi: 600 for PNGs going into camera-ready.
- Never JPEG for plots. The lossy artifacts around lines and text are obvious in print.
- Embed fonts in PDFs — matplotlib does this by default with
pdf.fonttype: 42 (TrueType). Without it, IEEE / ACM sometimes flag text as "Type 3 fonts" during eXpress / paperplaza checks.
plt.rcParams['pdf.fonttype'] = 42
plt.rcParams['ps.fonttype'] = 42
2. Color: colorblind-safe by default
Roughly 8 % of male readers have a red-green color-vision deficiency. Reviewers, area chairs, and ML conference attendees in dim halls fail to distinguish poorly chosen palettes. Default to Okabe-Ito for categorical data and viridis / cividis for continuous data.
okabe_ito = ['#E69F00', '#56B4E9', '#009E73', '#F0E442',
'#0072B2', '#D55E00', '#CC79A7', '#000000']
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=okabe_ito)
For categorical 3 – 4-series plots, prefer the high-contrast subset ['#0072B2', '#D55E00', '#009E73', '#CC79A7'] (blue / vermillion / green / pink) — it stays distinguishable in B/W print.
For heatmaps:
- Sequential single-variable data (latency, accuracy, count):
viridis or cividis. Both are perceptually uniform and colorblind-safe.
- Diverging data (speedup vs baseline, log-fold change, error signed):
RdBu_r or PuOr centered at the neutral value. Avoid RdGn / RdYlGn.
- Never
jet or rainbow. Not perceptually uniform; over-emphasizes mid-range; bad in B/W. Reviewers will flag this.
Always add redundant encoding beyond color: line styles ('-', '--', ':'), markers ('o', 's', '^'), or hatching on bars. If the figure still reads in grayscale, the color choice is solid.
3. Typography
- Sans-serif at the final printed size: Arial, Helvetica, or DejaVu Sans (the matplotlib default fallback).
- Minimums at the size that lands on the page:
- Axis labels: 8 pt
- Tick labels: 7 pt
- Legend: 7 pt
- Panel labels (a, b, c): 9 – 10 pt bold
- Embed fonts. Use
pdf.fonttype: 42 so TrueType fonts ship inside the PDF.
- Sentence case:
Throughput (req/s) not THROUGHPUT (REQ/S).
- Always include units in parentheses. Use SI / IEC prefixes consistently (
KB, MiB, μs, not K, M, us).
4. Figure dimensions for CS venues
| Venue | Single column | Double column / full | Typical class file |
|---|
| IEEE Transactions / Conf. | 3.5 in (89 mm) | 7.16 in (182 mm) | IEEEtran |
| ACM (sigconf, acmsmall) | 3.33 in (8.45 cm) | 7.0 in (17.78 cm) | acmart |
| USENIX (Annual / OSDI / NSDI / Sec.) | 3.33 in | 7.0 in | usenix2019_v3 |
| NeurIPS / ICML / ICLR | n/a (one column) | 5.5 in (14.0 cm) | venue style |
| arXiv | follow target venue | follow target venue | venue style |
Match \columnwidth exactly when you use \includegraphics[width=\columnwidth]. Set figsize=(3.5, ...) for IEEE single-column figures and let LaTeX scale only when the figure spans both columns.
See references/venue_requirements.md for the full per-venue spec, including \figcomp / \columnsep quirks and ACM's two-class width difference.
5. Multi-panel layout
- Label panels with bold lowercase letters: (a), (b), (c) — IEEE and ACM both use lowercase parenthesized.
- Use
GridSpec for non-uniform layouts; plt.subplots() for uniform grids.
- Align panels along edges; equalize axis ranges across panels comparing the same quantity.
- Use
constrained_layout=True (replaces tight_layout) — handles colorbars and panel labels better.
from string import ascii_lowercase
fig = plt.figure(figsize=(7.0, 4.0), constrained_layout=True)
gs = fig.add_gridspec(2, 3)
axes = [fig.add_subplot(gs[i // 3, i % 3]) for i in range(6)]
for i, ax in enumerate(axes):
ax.text(-0.20, 1.05, f'({ascii_lowercase[i]})',
transform=ax.transAxes, fontsize=10, fontweight='bold', va='top')
Multi-panel anti-redundancy checklist
A multi-panel figure should read as one figure where each panel earns its space. Covering any one panel must leave a gap the others cannot fill. Run this before finalizing a composite:
Common redundancy traps and the fix:
| Trap | CS example | Fix |
|---|
| Absolute + absolute | Grouped-bar accuracy + heatmap of the same accuracies | Replace the heatmap with a deviation view (per-method delta vs baseline) |
| Subset of parent | "Ours-only" latency bar that is one column of the grouped bar | Swap for a scatter: latency vs throughput across methods |
| Two rankings | Two ranked bars on accuracy and on F1 (highly correlated) | Replace one with an accuracy-vs-F1 scatter |
| Same slice, different chart | Pie plus stacked bar of one breakdown | Merge, or replace one with a trade-off plot |
When a panel shows absolute values, a good companion panel shows what is atypical (a signed deviation from a baseline or mean, on a diverging colormap like RdBu_r) rather than the same absolutes again. A third panel that reveals co-variation between two of the quantities (a scatter or bubble plot, with reference lines at the medians) adds a dimension the first two lack.
Three generalizable multi-panel techniques
These transfer to almost any CS composite:
-
Luminance-based in-bar text color. When you print values inside bars, pick black or white text by the bar's luminance so the number stays readable on both light and dark fills:
for bar, value in zip(bars, values):
r, g, b = bar.get_facecolor()[:3]
luminance = 0.299 * r + 0.587 * g + 0.114 * b
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height(),
f'{value:.2f}', ha='center', va='bottom',
color='white' if luminance < 0.5 else 'black')
-
Dynamic y-limit tightening. Never leave 0–100 when the values sit in a narrow band (e.g. accuracies of 80–95). A fixed wide range flattens the differences the figure exists to show. Tighten to the data with a small margin:
lo, hi = data.min(), data.max()
margin = 0.05 * (hi - lo)
ax.set_ylim(lo - margin, hi + margin)
For a bar chart this trades off against the "bars must start at zero" rule (see Common pitfalls): if you tighten a bar-chart axis, add a broken-axis indicator so the truncation is honest.
-
Dedicated legend-only subplot. For a multi-axis figure where one shared legend serves all panels, reserve the last cell for the legend alone instead of crowding one panel or repeating legends:
ax_legend = fig.add_subplot(gs[-1])
ax_legend.legend(handles, labels, loc='center', frameon=False)
ax_legend.set_axis_off()
Prefer one shared legend strip (or direct labels on stable regions) over repeating a legend inside every axis.
Common CS Figure Patterns
Reference implementations live in references/matplotlib_examples.md. The patterns covered:
| Pattern | Use case | Notes |
|---|
| Latency CDF / ECDF | Tail behavior of a request distribution | Log-scale x-axis; cap at p99 or p99.9; include median + tail callouts |
| Throughput vs concurrency | Scaling curves | Log-scale x; mark saturation knee; pair with median latency |
| Speedup bar chart | Per-benchmark improvement vs baseline | Always include 1.0 × reference line; group by suite |
| Training / loss curves | ML convergence | Show mean over seeds + shaded std; log-scale y for loss |
| Pareto front (acc vs params / FLOPs) | Model-size tradeoff | Highlight your model; annotate frontier |
| Stacked bar (latency breakdown) | Where time goes | Stable color order across runs |
| Roofline plot | Compute vs memory bound | Log-log axes; ridge line at peak FLOPS |
| Heatmap (cache miss / attention) | 2D intensity | viridis or cividis; never jet |
| Box / violin (per-seed variability) | Multi-run distribution | Overlay individual seeds as strip plot |
| Significance bars | A / B / ablation comparison | Optional in CS — only when stats are central to the claim |
Statistical rigor (CS-specific)
CS reviewers care less about p-values than biomedicine reviewers, but more about run-to-run variability and error bars over seeds. Standard expectations:
- Run each measurement ≥ 3 times (≥ 5 for ML, ≥ 10 for noisy systems benchmarks). Report mean and std or 95 % bootstrap CI.
- Show individual data points (strip plot) when n is small (≤ 10) — averages alone hide bimodal failure modes.
- State error type in the caption:
Error bars: 95 % CI over 5 seeds or Shaded region: ±1 std over 10 runs.
- For ML, also report median + min/max if you train fewer than 5 seeds.
- For latency, report a percentile (median, p99, p99.9) — never just the mean. The mean of a heavy-tail distribution is a lie.
Don't fabricate error bars. If a measurement is from a single run, say so in the caption and prefer a bar / scatter without error indicators rather than zero-width error bars.
Choosing a library
| Library | Best for | Notes |
|---|
| matplotlib | Multi-panel, full control, anything that needs custom annotations | Default for camera-ready CS figures |
| seaborn | Statistical plots with automatic CI / bootstrap | Built on matplotlib; styles compose; great for box / violin / regression |
| plotly | Interactive HTML for project pages or supplementary | Use kaleido to export static PDF |
| pgfplots / TikZ | When you want LaTeX to compile the figure | Best for diagrams that mix math and data; covered by scientific-schematics |
If a figure is reused across paper / poster / slides, render at multiple sizes from the same script — keep a single source-of-truth Python file under .writing/figures/src/.
Workflow inside this plugin
Confirmation gates — pause for the user before these three points:
- Before writing a generator script, confirm the target venue, the column width (single vs full), and the data source path. If any is unknown, ask rather than assume a default — guessing the venue silently produces a figure sized for the wrong template.
- Never invent measurements. If
.writing/figures/data/<fig_id>.{csv,jsonl} is missing or the user hasn't supplied numbers, stop and ask for the data. Do not synthesize plausible-looking benchmark values, error bars, or trend lines to fill a plot — a polished figure built on fabricated data is worse than no figure. Placeholder values are acceptable only when the user explicitly asks for a layout mock-up and the figure is labeled as such.
- Before overwriting an existing
.writing/figures/<fig_id>.pdf, confirm with the user (or note that you are about to replace it) so a prior camera-ready figure isn't clobbered silently.
Then proceed:
- Plan from the outline. Each figure listed in
.writing/plan.md should have a one-line claim it supports (fig:cdf-latency → "our system has a 2.4× lower p99 than the baseline at 32 clients"). For a figure that doubles as the paper's results-storytelling figure (the headline result a reviewer sees first), check tikz-figures/references/figure-rhetoric.md: it covers the three-figure storytelling model, the Figure-1 performance-teaser paradigm and its avoid-condition (do not teaser a marginal gain, it shows the weakness unkindly), and the 30-second comprehension test. That reference is venue-agnostic across our figure skills. When these plots form the full figure deck of a paper, run one editor pass over the whole deck before submission: the per-deck narrative review in figure-rhetoric.md §5 (hook verdict, figure arc, misplaced panels, missing panels, kill list), which is distinct from the per-figure defect hunt in the Final Checklist below. When one figure is itself multi-panel (an ablation grid, a schematic paired with its result), figure-rhetoric.md §6 covers outlining the panels from the claim, drawing each with the matching skill, and reviewing the composed image as one artifact with verify_layout.py.
- Write a generator script in
.writing/figures/src/<fig_id>.py. The script reads from .writing/figures/data/<fig_id>.{csv,jsonl} and writes .writing/figures/<fig_id>.pdf. This separation lets reviewers (and you) regenerate figures from raw data.
- Apply this skill's style at the top of the script — copy the bundled
.mplstyle file or use the inline rcParams snippet from Quick Start.
- Verify before commit. Open the PDF in a viewer at 100 % zoom and at the column width it will appear at in the paper. If text is cramped at column width, the font sizes are wrong. For a mechanical pre-check, call
verify(fig) from scripts/verify_layout.py at the end of the generator script: it flags text that collides with other text or a foreign axes spine and text clipped off the canvas, and, with crop_dir=, writes one PNG per panel so you (or a review agent) run the perceptual pass the geometry check cannot. Open each crop with the Read tool and check contrast, leader crossings, color-identity confusion, and legend binding.
- Caption discipline. The figure caption belongs in the
.tex file, not on the figure itself. Repeat axes labels in the caption; spell out what error bars represent; state the sample size.
Common pitfalls (CS-specific)
- Y-axis in milliseconds for tail latency. A linear axis hides the long tail. Use log y for p99 / p99.9 plots.
- Bar chart starting non-zero. Misleading. Either start at zero or annotate clearly with a broken-axis indicator.
- One color per algorithm but identical line style. Fails in grayscale. Add markers + line styles.
- Legend over data. Use
loc='upper left' and verify visually; or place outside with bbox_to_anchor.
- Default matplotlib font rendering. Matplotlib's default
mathtext uses computer-modern italics; mixed with Arial body text it looks broken. Either set text.usetex: True (slow) or use mathtext.fontset: 'cm' only when you need real LaTeX math.
tight_layout() clipping panel labels. Use constrained_layout=True instead.
- DPI confusion. For PDF,
dpi only affects rasterized embedded objects. For PNG, set both figure.dpi (display) and savefig.dpi (output) — savefig.dpi: 600 for camera-ready.
plt.show() in a script that runs in CI. Will hang. Use fig.savefig(...) only and skip plt.show() in scripted figure generation.
- Forgetting
bbox_inches='tight'. Default save leaves margin around the figure. With bbox_inches='tight', the saved PDF crops to the actual content — what you want for \includegraphics.
- Reusing colors across figures. Pin one color to one method everywhere. Define a
METHOD_COLORS dict in a shared .writing/figures/src/_palette.py.
Bundled resources
| Path | Purpose |
|---|
references/publication_guidelines.md | Universal best practices: typography, layout, axes, accessibility, grayscale tests |
references/venue_requirements.md | Per-venue specs (IEEE, ACM, USENIX, NeurIPS, ICML, ICLR, arXiv) with exact widths, font sizes, and class-file gotchas |
references/color_palettes.md | Categorical (Okabe-Ito, Wong, Tol Bright/Muted), sequential (viridis family), diverging (RdBu, PuOr, BrBG); with grayscale and colorblind testing recipes |
references/matplotlib_examples.md | Twelve runnable patterns — CDF, throughput, training curve, Pareto, ablation, stacked-bar breakdown, heatmap, roofline, box+strip, multi-panel composition, \columnwidth IEEE template, \textwidth ACM template |
scripts/style_presets.py | apply_publication_style(name), set_color_palette(name), configure_for_venue('ieee' / 'acm' / 'usenix' / 'neurips' / 'icml' / 'iclr' / 'arxiv'). Copy into a project to pin the rcParams. |
scripts/figure_export.py | save_publication_figure() (multi-format), save_for_venue() (per-venue defaults), check_figure_size() (warns when figsize doesn't match the venue's column width). |
scripts/verify_layout.py | verify(fig): geometric self-check (text/text + text/spine overlap, off-canvas text) plus per-panel crops for the perceptual pass. Call at the end of a generator script before committing the PDF. |
assets/color_palettes.py | Importable color constants and apply_palette('okabe_ito' / 'wong' / 'tol_bright' / …). |
assets/publication.mplstyle | Generic CS publication baseline. Use when no specific venue style applies. |
assets/ieee.mplstyle | IEEE Transactions / conferences (3.5 in single column, 8 pt body). |
assets/acm.mplstyle | ACM acmart sigconf / acmsmall (3.33 in single, 7.0 in two-column). |
assets/neurips.mplstyle | NeurIPS / ICML / ICLR (single column 5.5 in textwidth, 9 pt body). |
assets/presentation.mplstyle | Larger fonts and thicker lines for posters and slides. |
Final Checklist
Frame this as defect-hunting, not confirmation. Before adding \includegraphics{...},
open the rendered PDF at the column width it will print at and actively look for what is
wrong, rather than ticking boxes to confirm the figure is fine. Walk the items below,
write a short defect list, and tag each finding high / medium / low. Fix every high
and every medium you can fix cheaply; list low items but do not block on them. Findings
are advisory: surface them to the user and let the user decide, never silently redraw a
camera-ready figure or overwrite it.
high: misleads the reader or makes the figure look broken:
- Non-PDF vector output where vector was expected, or a PNG below 300 DPI going to camera-ready.
- A truncated / non-zero-baseline axis that exaggerates the gain with no broken-axis indicator.
- Fabricated or unsupported data: invented error bars, a synthesized trend, zero-width error bars standing in for "single run".
- Colorblind-unsafe palette or color-only encoding (jet / rainbow, red-green pairs); figure unreadable in grayscale.
- Key labels or tick labels illegible at print size, or text clipped.
- A
figsize that does not match the target column width, so LaTeX rescales and distorts fonts.
- An excluded or failed run (crashed, diverged, OOM'd, cold-start outlier) silently folded into a mean, bar, or error band, or silently dropped with no caption note. Excluded data is omitted or drawn with a distinct marker, never averaged in with the runs you kept.
- The same quantity carrying two canonical values across figures, caption, and abstract (2.4× in one place, 2.6× in another for "the speedup"). One number per claim, everywhere. See
references/publication_guidelines.md → Data Fidelity and Self-Consistency for both of these.
medium: reduces professionalism or comprehension:
- Chartjunk: 3D effects, decorative gridlines, drop shadows, top / right spines left on.
- Missing units on axis labels, or inconsistent SI / IEC prefixes.
- Panel labels missing or inconsistent (
(a) (b) (c), lowercase parenthesized).
- Method colors not pinned consistently across every figure in the paper.
- Redundant panels in a composite: re-run the multi-panel anti-redundancy checklist in §5.
- Error-bar definition missing from the caption (or single-run not noted).
low: cosmetic, does not affect comprehension:
- Minor alignment or spacing imperfections.
- Palette or typography refinements.
- Non-embedded fonts where the venue tolerates it (still prefer
pdffonts foo.pdf showing "yes" under "emb").
This severity framing aligns with tikz-figures mode-C, which already runs the strongest
form of defect-hunting (independent multi-lens adversarial review). For data plots there is
no independent reviewer pass; the discipline is the same, but the final oracle is the user
looking at the rendered PDF.