# nature-figure skill Submission-grade scientific figures for Nature-tier journals and high-impact academic venues, with both Python and R plotting tracks. The skill starts from a figure contract: core conclusion, evidence hierarchy, archetype, backend choice, journal/export constraints, statistics, and source-data traceability. Plotting templates are used only after the scientific logic is clear. Python remains the best-supported low-level layout path through `matplotlib`, `seaborn`, `subplot_mosaic`, and `statsmodels`. R is supported through `ggplot2`, `patchwork`, `ComplexHeatmap`, `ggrepel`, `svglite`, `cairo_pdf`, and `ragg`. If private template collections are used, their paths, filenames, and provenance must not appear in user-facing output. Derived from production scripts in [figures4papers](https://github.com/ChenLiu-1996/figures4papers) (published in *Nature Machine Intelligence* and top ML/bioinformatics venues). --- ## Example output gallery The images below are simulated data mockups generated with this skill's rules: editable SVG-first export, restrained semantic palettes, lowercase panel labels, and asymmetric multi-panel information architecture. They are PNG previews for README display; production use should still export SVG/PDF from the plotting script. | Figure | Preview | What the skill demonstrates | |--------|---------|-----------------------------| | Material design and physical validation | Material design and physical validation | Schematic-led composite, SEM-like image panel, rheology, release kinetics, retention map, correlation and endpoint quantification | | Spatial retention and uptake | Spatial retention and uptake | Dark microscopy plate, channel rows, zoom crops, depth profiles, uptake histograms, 3D penetration heatmap and image-derived correlation | | In vivo efficacy and tolerability | In vivo efficacy and tolerability | Experimental timeline, longitudinal tumour curves, individual growth traces, waterfall response, forest plot, histology, immune composition and toxicity panels | | Single-cell systems figure | Single-cell systems figure | UMAP-style embedding, composition, marker heatmap, pseudotime, volcano plot, enrichment, ligand-receptor bubble matrix and spatial niche adjacency | | Perturbation validation | Perturbation validation | Mechanistic perturbation timeline, relapse endpoint, polar summary, dose response, synergy matrix, biodistribution, cytokines, flow-like scatter and safety score | **Gallery file policy** Keep only lightweight PNG previews in `assets/gallery/`. Do not commit large generated SVG/PDF outputs unless they are needed for a tutorial, because real users should regenerate editable outputs from source data and scripts. --- ## Chart-type atlas The gallery below classifies the skill by chart family. Each preview is a dense 4 x 4 atlas of small panels, designed to show the range of visual grammars that can be combined inside a larger *Nature*-style result figure. | Type | Preview | Common use | |------|---------|------------| | Bar charts | Bar chart atlas | Group comparisons, signed deltas, grouped-within-grouped designs, stacked composition | | Line and longitudinal trends | Line chart atlas | Time courses, uncertainty ribbons, intervention marks, individual traces | | Heatmaps | Heatmap atlas | Z-score matrices, sequential abundance maps, annotated tables, clustered blocks | | Scatter and bubble plots | Scatter and bubble atlas | Correlation, clusters, volcano-style tests, quadrant summaries, third-variable bubbles | | Radar and polar charts | Radar and polar atlas | Multi-axis benchmarking, circular summaries, polar histograms, directional density | | Distribution plots | Distribution plot atlas | Histograms, violins, boxes, ridgelines and sample-level spread | | Forest and interval plots | Forest and interval atlas | Effect sizes, confidence intervals, point ranges, paired slope comparisons | | Area and stacked trends | Area and stacked trend atlas | Filled trajectories, stacked shares, cumulative curves, stream-like compositions | | Image plates | Image plate atlas | Microscopy channels, overlays, crops, scale bars and dark-panel layouts | | Network and matrix charts | Network and matrix atlas | Bubble matrices, adjacency maps, node-link diagrams and bipartite interaction panels | --- ## File structure ``` nature-figure/ ├── SKILL.md ← skill trigger & overview (loaded by Claude automatically) ├── README.md ← this file ├── assets/ │ ├── gallery/ ← result-figure preview PNGs │ └── chart-atlas/ ← chart-type taxonomy preview PNGs └── references/ ├── figure-contract.md ← core conclusion, evidence hierarchy, panel map ├── backend-selection.md ← Python vs R decision rules ├── r-workflow.md ← R scaffold, patchwork, ComplexHeatmap, export ├── r-template-index.md ← local R template atlas ├── qa-contract.md ← submission/revision QA checklist ├── api.md ← PALETTE constants, helper function signatures ├── design-theory.md ← typography, color theory, layout, export policy ├── common-patterns.md ← reusable code patterns (bars, legends, heatmaps) ├── tutorials.md ← end-to-end walkthroughs └── chart-types.md ← radar, 3D sphere, scatter, fill_between, log-scale ``` --- ## Backend and contract rules Ask the user to choose **Python or R** unless the backend is already specified. If they ask for a recommendation, use `references/backend-selection.md`. After a backend is selected, use it exclusively for plotting, previews, exports, and visual QA. If the selected runtime or packages are missing, stop and report the blocker; do not render a fallback preview with the other language. This applies in both directions: no Python substitute for R, and no R substitute for Python. Before plotting, write or infer the core conclusion, figure archetype, panel map, evidence hierarchy, target output, statistics/source-data needs, and export bundle. The figure must serve the scientific logic first. Aesthetic polish and template matching are secondary. User-facing output must not disclose private local paths, private filenames, internal reference documents, template identifiers, or private-material provenance unless the user explicitly asks for that audit trail. --- ## Python mandatory rules ### 1. Three required rcParams — editable SVG text ```python plt.rcParams['font.family'] = 'sans-serif' plt.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'Liberation Sans'] plt.rcParams['svg.fonttype'] = 'none' ``` **Why `svg.fonttype = 'none'`** Matplotlib's default (`'path'`) converts every glyph to a bezier curve. The result is visually identical but every `` element becomes a `` — unselectable, unsearchable, and impossible to realign in Illustrator or Inkscape. With `'none'`, text stays as SVG `` nodes. Font substitution happens at render time. **Why three fonts in the stack** `Arial` is standard on macOS/Windows. `DejaVu Sans` ships with matplotlib and is the Linux fallback. `Liberation Sans` is metric-compatible with Arial on RHEL/Ubuntu. The cascade guarantees identical letter-spacing on all platforms. ### 2. Primary output format is SVG ```python fig.savefig('figure.svg', bbox_inches='tight') # primary — editable text fig.savefig('figure.png', dpi=300, bbox_inches='tight') # optional raster preview ``` Never use PNG alone when the figure will go into a paper or slide deck that requires post-hoc text adjustment. ### 3. Always close the figure ```python plt.close(fig) ``` --- ## Quick-start template ```python import matplotlib matplotlib.use('Agg') # headless / server rendering import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec import numpy as np # ── MANDATORY ───────────────────────────────────────────────────────────────── plt.rcParams['font.family'] = 'sans-serif' plt.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'Liberation Sans'] plt.rcParams['svg.fonttype'] = 'none' # ── Style ────────────────────────────────────────────────────────────────────── plt.rcParams.update({ 'font.size': 12, 'axes.spines.right': False, 'axes.spines.top': False, 'axes.linewidth': 2.0, 'legend.frameon': False, 'xtick.major.width': 1.5, 'ytick.major.width': 1.5, }) # ── Figure ────────────────────────────────────────────────────────────────────── fig, ax = plt.subplots(figsize=(8, 5)) ax.spines['bottom'].set_linewidth(2) ax.spines['left'].set_linewidth(2) # ... your plot code ... fig.tight_layout(pad=2) fig.savefig('output.svg', bbox_inches='tight') fig.savefig('output.png', dpi=300, bbox_inches='tight') plt.close(fig) ``` --- ## Color palette ```python PALETTE = { # Primary / hero method 'blue_main': '#0F4D92', 'blue_secondary': '#3775BA', # Positive / improvement shades 'green_1': '#DDF3DE', 'green_2': '#AADCA9', 'green_3': '#8BCF8B', # Baseline / contrast 'red_1': '#F6CFCB', 'red_2': '#E9A6A1', 'red_strong': '#B64342', # Neutral support 'neutral_light': '#CFCECE', 'neutral_mid': '#767676', 'neutral_dark': '#4D4D4D', 'neutral_black': '#272727', # Accent (use sparingly) 'gold': '#FFD700', 'teal': '#42949E', 'violet': '#9A4D8E', 'magenta': '#EA84DD', } ``` **Semantic mapping convention** `blue_main` = your method / hero series. `green_3` = positive variants. `red_strong` = baselines. `neutral_light` = reference / background. Apply this consistently across every panel in the figure. **Unified palette policy (recommended for recent Nature Machine Intelligence-style layouts)** Do not maximize hue separation by default. In dense multi-panel figures, prefer **one coherent baseline family** and **one coherent hero family**, then reserve green/red for delta markers or genuinely signed semantics. ```python PALETTE_NMI_PASTEL = { # Baseline / comparison family (cool blue-grey) 'baseline_dark': '#484878', 'baseline_mid': '#7884B4', 'baseline_soft': '#B4C0E4', # Hero / proposed family (lilac → rose) 'ours_tiny': '#E4E4F0', 'ours_base': '#E4CCD8', 'ours_large': '#F0C0CC', # Background blocks for overview / concept panels 'bg_lilac': '#E0E0F0', 'bg_aqua': '#E0F0F0', 'bg_peach': '#F0E0D0', # Neutral support 'neutral_light': '#D8D8D8', 'neutral_mid': '#A8A8A8', 'neutral_dark': '#606060', # Accent only for directional annotations 'delta_up': '#2E9E44', 'delta_down': '#E53935', } DEFAULT_COLORS_NMI_PASTEL = [ PALETTE_NMI_PASTEL['baseline_dark'], PALETTE_NMI_PASTEL['baseline_mid'], PALETTE_NMI_PASTEL['baseline_soft'], PALETTE_NMI_PASTEL['ours_tiny'], PALETTE_NMI_PASTEL['ours_base'], PALETTE_NMI_PASTEL['ours_large'], ] ``` Use `DEFAULT_COLORS_NMI_PASTEL` when: - comparing related model families such as `Tiny / Base / Large` - building 1-page result atlases where multiple panels must feel visually unified - matching low-saturation editorial styling rather than maximum category separation **Practical rule** The same method family keeps the same hue family in every panel. Do not recolor a model from blue-grey in panel `a` to green in panel `d` just because that panel needs more contrast. --- ## Supported chart types | Chart | File | Key pattern | |-------|------|-------------| | Grouped bar | `tutorials.md` | `ax.bar()` with `x + offset`, legend-only last panel | | Stacked bar | `common-patterns.md` | iterate `col_order`, accumulate `bottom` | | Horizontal ablation bar | `tutorials.md` | `ax.barh()`, alpha-gradient for completeness encoding | | Trend / line | `tutorials.md` + `api.md` | `make_trend()`, `fill_between` for uncertainty shadow | | Heatmap (sequential) | `api.md` | `make_heatmap()`, `YlOrRd`, cell annotation with luminance check | | Heatmap (diverging / z-score) | `design-theory.md §11` | `RdBu_r`, `vmin=-2.5, vmax=2.5` | | Bubble scatter | `design-theory.md §11` | x/y = two compartments, `s=` = third variable | | Radar / polar | `chart-types.md` | `projection='polar'`, custom spokes, per-spoke normalization | | 3D sphere / illustration | `chart-types.md` | Lambertian shading via ray-cast on numpy grid | | Fill-between (stacked area) | `chart-types.md` | hatch for print-safe grayscale | | Log-scale bar | `chart-types.md` | `set_yscale('log')`, expand top for annotations | | Multi-panel GridSpec | `chart-types.md` | `GridSpec(rows, cols)`, `gs[0, :]` for full-width spans | --- ## Multi-panel information architecture Each panel in a multi-panel figure must answer a **unique** scientific question. Covering any one panel should leave a gap that cannot be recovered from the others. ### Three-level progressive complexity (recommended) | Level | Question | Encoding | |-------|----------|----------| | Overview | "What is the landscape?" | Stacked bar, composition | | Deviation | "What is distinctive per group?" | Z-score heatmap, diverging cmap | | Relationship | "How do variables co-vary?" | Bubble scatter, correlation | ### Common redundancy traps | Trap | Example | Fix | |------|---------|-----| | Absolute + absolute | Stacked bar (%) + heatmap of same % | Replace heatmap with z-score deviation | | Subset of parent | Tumor-only ranked bar is just one column of the stacked bar | Swap for scatter: tumor % vs immune % | | Two rankings | Two ranked bars on related metrics | Replace one with bubble scatter | | Different chart, same data | Pie + stacked bar | Merge or replace with a relationship plot | ### Z-score deviation heatmap ```python z = (heat - heat.mean(axis=0)) / heat.std(axis=0) im = ax.imshow(z.values, cmap='RdBu_r', aspect='auto', vmin=-2.5, vmax=2.5) cbar.set_label('Z-score vs pan-cohort mean') ``` `RdBu_r`: red = enriched above average, blue = depleted. Orthogonal to absolute % shown in panel a. ### Bubble scatter with quadrant labels ```python ax.scatter(x, y, s=size_var * scale, c=colors, edgecolors='white', linewidth=0.8, alpha=0.9) ax.axvline(np.median(x), lw=1.2, ls='--', color='#767676', alpha=0.6) ax.axhline(np.median(y), lw=1.2, ls='--', color='#767676', alpha=0.6) ``` Label quadrants at corners with small grey italic text (`fontsize=7.5, color='#888888', style='italic'`). --- ## Layout rules ### Figure sizes | Type | `figsize` | |------|-----------| | Multi-metric bar (3–4 metrics + legend panel) | `(28–45, 6–12)` | | Grand multi-panel (3 panels, 2-row GridSpec) | `(22, 17)` | | Compact single bar | `(9–16, 5–8)` | | Trend / line multi-panel | `(14, 4)` or `(9, 8)` | | Heatmap single | `(8–20, 5–9)` | | Radar polar | `(12, 10)` | **Rule**: width ≈ 3–4× height for comparison bar panels. ### Panel labels ```python ax.text(-0.05, 1.06, 'a', transform=ax.transAxes, fontsize=22, fontweight='bold', va='top', ha='right') ``` Use lowercase bold (`a`, `b`, `c`) at top-left of each subplot axes, placed via `transAxes`. ### Legend - For multi-axis figures: give the legend its own axis (`ax.set_axis_off()`). - Always `frameon=False`. - When the legend is large, place it `bbox_to_anchor=(0.5, -0.24), loc='upper center'` below the panel. --- ## Font size hierarchy | Context | `font.size` | |---------|-------------| | Base (compact subfigures) | 12–16 | | Large bar panels (figsize > 28 in) | 24 | | Axis labels (large panels) | 32–54 via per-label override | | In-bar / in-cell annotations | 6.5–12 | | Panel letter labels | 20–22 | | Legend | 8–14 | --- ## Axes & spines rules ```python plt.rcParams['axes.spines.right'] = False # always off plt.rcParams['axes.spines.top'] = False # always off plt.rcParams['legend.frameon'] = False ax.spines['bottom'].set_linewidth(2) # thicker for emphasis ax.spines['left'].set_linewidth(2) ``` No gridlines by default. Use sparse `set_yticks` to guide the eye. Y-limits tightened to data range — never use `0–100` when all values sit in `80–95`. --- ## In-cell / in-bar text contrast ```python def luminance_text_color(hex_color): c = hex_color.lstrip('#') r, g, b = int(c[0:2],16)/255, int(c[2:4],16)/255, int(c[4:6],16)/255 return 'white' if 0.299*r + 0.587*g + 0.114*b < 0.5 else '#333333' ``` --- ## Reproduction checklist - [ ] Core conclusion and panel map are clear before styling - [ ] Backend is explicitly Python or R - [ ] **Lines 1–3**: `font.family`, `font.sans-serif` (three fonts), `svg.fonttype = 'none'` - [ ] Primary output is **SVG** (`bbox_inches='tight'`) - [ ] Right and top spines off; `legend.frameon = False` - [ ] Font size matches final use: 5–7 pt for dense journal output, larger only for slide-sized panels - [ ] Colors come from one coherent palette system: either semantic `PALETTE` or unified `PALETTE_NMI_PASTEL` - [ ] Related model sizes / variants share a hue family; do not assign unrelated saturated colors to siblings - [ ] Green / red reserved for gains, drops, thresholds, or truly signed semantics - [ ] Y-limits tightened to data range - [ ] Multi-panel figures: each panel answers a **different** question (anti-redundancy checklist passed) - [ ] Panel labels (`a`, `b`, `c`) are bold lowercase and sized for final output - [ ] Statistics, `n`, source data, and image-integrity notes are documented when manuscript-facing - [ ] `tight_layout(pad=2)` before save - [ ] `plt.close(fig)` after save