---
id: gh-nature-figure
name: "nature-figure"
url: https://skills.yangsir.net/skill/gh-nature-figure
author: yuan1z0825
domain: science
tags: ["scientific-figures", "data-visualization", "academic-publishing", "python-r", "research"]
install_count: 18500
rating: 4.60 (120 reviews)
github: https://github.com/yuan1z0825/nature-skills/tree/main/plugins/nature-skills/skills/nature-figure
---

# nature-figure

> 这个技能旨在帮助用户制作出版级别的科学图表，将其作为视觉论证而非孤立的图示。它强调在编码和美化之前，先确立核心论点、证据链和评审风险。支持Python (matplotlib) 和 R (ggplot2) 等工具，确保图表符合科学逻辑，可辩护且可评审，提升学术论文的视觉表达质量和效率。

**Stats**: 18,500 installs · 4.6/5 (120 reviews)

## Before / After 对比

### 图表修改轮次

**Before**:

在没有明确的图表论证框架和出版标准指导下，科学图表往往需要经历多轮修改，以满足审稿人和期刊的要求，耗费大量时间和精力。

**After**:

通过在编码前确立核心论点和证据链，并遵循出版质量标准，图表能更有效地作为视觉论证，大幅减少修改轮次，加速论文发表进程。

| Metric | Before | After | Change |
|---|---|---|---|
| 图表修改轮次 | 5轮 | 1轮 | -80% |

## Readme

# Nature Figure Making Skill

A guide for producing publication-quality scientific figures as a visual argument, not
as isolated pretty plots. Every figure starts from a claim, an evidence hierarchy, and a
review-risk check before code or aesthetics.

The older Python/matplotlib rules in this skill remain valid. The skill now also supports
R, especially `ggplot2 + patchwork + ComplexHeatmap + ggrepel + svglite/cairo_pdf + ragg`.
If the user provides a private plotting template collection, use it only as an internal
adaptation source and do not reveal its path, filenames, or provenance in user-facing output.

Color policy: prefer **unified method families across all panels** over maximal hue separation.
For dense Nature Machine Intelligence-style figure pages, use the low-saturation `NMI pastel`
family described in `references/api.md` and reserve green/red mainly for gains, drops, and other directional cues.

## First move: figure contract before plotting

Before generating or editing code, establish the contract below.

**Backend selection is a blocking gate.** If the user has not explicitly chosen Python
or R in the current request or provided a clearly language-specific input file/workflow,
ask one concise question: **Python or R?** Then stop and wait for the user's answer.
Do not generate mock data, write scripts, create figures, or choose Python/R by default.
This overrides general autonomy/default-execution behavior for figure tasks.

**The selected backend is exclusive for all figure generation.** Once Python or R is
selected, every plotting script, preview image, SVG/PDF/TIFF/PNG export, QA render,
and visual workaround must be produced by that same backend. Do not use Python to
draw a preview for an R figure, and do not use R to draw a preview for a Python figure,
even if the selected runtime or packages are missing locally. The non-selected language
may only be used for non-visual file inspection or data conversion when it does not
open a graphics device, import plotting libraries, create image/vector files, or
change the final visual appearance.

**Missing runtime/package rule.** After the backend is selected, check the selected
runtime early (`Rscript`/R for R; Python and required plotting packages for Python).
If the selected runtime or required packages are unavailable, stop before rendering
and report the exact blocker. You may provide a selected-backend script and installation
commands, or ask permission to install dependencies, but you must not fall back to the
other language to make a substitute figure.

Only recommend a backend when the user explicitly asks you to choose or recommend one.
In that case, use `references/backend-selection.md`, state the reason, and then proceed
with the recommended backend.

1. Core conclusion: write the one-sentence claim the figure must defend.
2. Evidence chain: map each planned panel to the claim, and drop panels that do not carry
   a unique piece of evidence.
3. Archetype: classify the figure as `quantitative grid`, `schematic-led composite`,
   `image plate + quant`, or `asymmetric mixed-modality figure`.
4. Backend: use the selected Python or R track exclusively for all figure drawing,
   previewing, exporting, and visual QA. Do not cross-render with the other language.
5. Journal/export contract: set final dimensions, editable text, source data, statistics,
   image-integrity notes, and export formats before styling.

The highest-priority rule is: **the chart serves the scientific logic**. Aesthetic polish,
template matching, and complex layout are subordinate to making the core conclusion clear,
defensible, and reviewable.

## User-facing privacy rule

Do not disclose private local paths, private filenames, chat-attachment names, internal
reference filenames, template identifiers, or the provenance of private working materials
in user-facing replies, generated code comments, figure legends, reports, or manuscript
text. Use generic descriptions such as "the provided R template collection", "a private
working draft", or "the internal figure contract". Only reveal an exact path or source
file when the user explicitly asks for that audit trail.

## Python quick-start

**Python-only execution rule.** When the user has selected Python, do all figure
drawing, previewing, exporting, and visual QA in Python. Do not call R/ggplot2,
ComplexHeatmap, patchwork, or any R graphics device to create a temporary preview,
fallback export, or layout approximation. If Python or required Python plotting
packages are missing, stop before rendering and report the missing dependency. You
may still write the Python script, provide `pip`/environment install commands, or
ask permission to install dependencies, but do not cross-render the figure in R.

```python
import matplotlib as mpl
import matplotlib.pyplot as plt

mpl.rcParams.update({
    "font.family": "sans-serif",
    "font.sans-serif": ["Arial", "Helvetica", "DejaVu Sans", "sans-serif"],
    "svg.fonttype": "none",     # editable text in SVG
    "pdf.fonttype": 42,         # editable TrueType text in PDF
    "font.size": 7,             # use 15-24 only for large slide-sized panels
    "axes.spines.right": False,
    "axes.spines.top": False,
    "axes.linewidth": 0.8,
    "legend.frameon": False,
})

def save_pub_py(fig, filename, dpi=600):
    fig.savefig(f"{filename}.svg", bbox_inches="tight")
    fig.savefig(f"{filename}.pdf", bbox_inches="tight")
    fig.savefig(f"{filename}.tiff", dpi=dpi, bbox_inches="tight")
```

Use `text.usetex = True` only when LaTeX is installed and math-rich labels are required.

## R quick-start

```r
library(ggplot2)
library(patchwork)

theme_set(
  theme_classic(base_size = 6.5, base_family = "Arial") +
    theme(
      axis.line = element_line(linewidth = 0.35, colour = "black"),
      axis.ticks = element_line(linewidth = 0.35, colour = "black"),
      legend.title = element_text(size = 6.2),
      legend.text = element_text(size = 5.8),
      strip.text = element_text(size = 6.2, face = "bold"),
      plot.title = element_text(size = 7, face = "bold"),
      panel.grid = element_blank()
    )
)

save_pub_r <- function(plot, filename, width_mm = 183, height_mm = 120, dpi = 600) {
  w <- width_mm / 25.4
  h <- height_mm / 25.4
  svglite::svglite(paste0(filename, ".svg"), width = w, height = h)
  print(plot)
  dev.off()
  grDevices::cairo_pdf(paste0(filename, ".pdf"), width = w, height = h, family = "Arial")
  print(plot)
  dev.off()
  ragg::agg_tiff(paste0(filename, ".tiff"), width = w, height = h, units = "in", res = dpi)
  print(plot)
  dev.off()
}
```

## Default operating stance

- Start by classifying the requested figure into one of four archetypes:
  `quantitative grid`, `schematic-led composite`, `image plate + quant`, or `asymmetric mixed-modality figure`.
- Prefer one **hero panel** plus subordinate evidence panels over filling the canvas with equal-sized subplots.
- If the user asks for a single chart, still identify its role in the manuscript claim:
  discovery, mechanism, validation, comparison, robustness, or clinical/biological relevance.
- Keep the background white for plots and diagrams; switch to black only for microscopy / volume-rendering image plates.
- Prefer direct labels over legends when categories are spatially fixed or the legend would force unnecessary eye travel.
- Keep one restrained palette per figure: usually one neutral family, one signal family, and one accent family.
- Treat statistics, `n`, error-bar definitions, source-data traceability, and image-integrity notes as part of the figure,
  not as optional caption cleanup.
- When the user asks for broad `Nature` style rather than ML/NMI-specific style, read `references/nature-2026-observations.md` before choosing layout.
- When the user references `figures4papers` or the older `scientific-figure-making` skill,
  treat this skill as the successor and open `references/demos.md` for bundled Python demo scripts.

## When to load this skill

- Python or R figures for **papers, slides, or reports** targeting Nature, Science, Cell, NeurIPS, ICLR, or similar venues.
- Requests involving **grouped bars, trend lines, heatmaps, radar plots, multi-panel grids**, or **PDF/SVG/high-DPI** output.
- Any mention of "Nature style", "publication figure", "paper figure", "SCI figure", "figures4papers", "scientific-figure-making", "R plotting template", or "high-quality scientific plot".
- Requests to improve a figure's logic, aesthetics, panel layout, figure legend, export quality, or journal-readiness.

## When NOT to load

- Plotly, Altair, Bokeh, or other interactive/web-first plotting.
- EDA-only plots without a publication target.
- Primary workflow is 3D, GIS, or non-scientific illustration tooling.
- Illustrator / Figma–first layout.

## Related files

| File | Open when |
|------|-----------|
| [references/figure-contract.md](references/figure-contract.md) | Need to convert a user request into core conclusion, evidence hierarchy, panel map, and review-risk checks |
| [references/backend-selection.md](references/backend-selection.md) | User has not chosen Python/R, asks for a recommendation, or a mixed Python/R workflow is possible |
| [references/r-workflow.md](references/r-workflow.md) | User chooses R or provides R scripts/templates/data |
| [references/r-template-index.md](references/r-template-index.md) | Need to adapt a user-provided or private R template collection without exposing source paths |
| [references/qa-contract.md](references/qa-contract.md) | Before final delivery, revision package, microscopy/blot figure, or journal-specific audit |
| [references/design-theory.md](references/design-theory.md) | Typography, color theory, layout rationale, export policy |
| [references/api.md](references/api.md) | Python PALETTE, helper function signatures, validation rules |
| [references/common-patterns.md](references/common-patterns.md) | Python layout patterns: hero panels, legend-only axes, dark image plates, asymmetric layouts |
| [references/nature-2026-observations.md](references/nature-2026-observations.md) | Real `Nature` page archetypes: schematic-led composites, dark image plates, clinical triptychs, asymmetric hero layouts |
| [references/tutorials.md](references/tutorials.md) | End-to-end walkthroughs: bars, trends, heatmaps |
| [references/chart-types.md](references/chart-types.md) | Radar, 3D sphere, fill_between, scatter patterns |
| [references/demos.md](references/demos.md) | Bundled figures4papers Python scripts and output previews for concrete pattern adaptation |


---

# 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).
The original figures4papers demo scripts and preview images are also bundled under
`assets/figures4papers/` for pattern-level adaptation.

---

## 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 | <a href="assets/gallery/fig1-material-mechanism-rich.png"><img src="assets/gallery/fig1-material-mechanism-rich.png" width="260" alt="Material design and physical validation"></a> | Schematic-led composite, SEM-like image panel, rheology, release kinetics, retention map, correlation and endpoint quantification |
| Spatial retention and uptake | <a href="assets/gallery/fig2-spatial-imaging-rich.png"><img src="assets/gallery/fig2-spatial-imaging-rich.png" width="260" alt="Spatial retention and uptake"></a> | Dark microscopy plate, channel rows, zoom crops, depth profiles, uptake histograms, 3D penetration heatmap and image-derived correlation |
| In vivo efficacy and tolerability | <a href="assets/gallery/fig3-in-vivo-efficacy-rich.png"><img src="assets/gallery/fig3-in-vivo-efficacy-rich.png" width="260" alt="In vivo efficacy and tolerability"></a> | Experimental timeline, longitudinal tumour curves, individual growth traces, waterfall response, forest plot, histology, immune composition and toxicity panels |
| Single-cell systems figure | <a href="assets/gallery/fig4-single-cell-systems-rich.png"><img src="assets/gallery/fig4-single-cell-systems-rich.png" width="260" alt="Single-cell systems figure"></a> | UMAP-style embedding, composition, marker heatmap, pseudotime, volcano plot, enrichment, ligand-receptor bubble matrix and spatial niche adjacency |
| Perturbation validation | <a href="assets/gallery/fig5-validation-perturbation-rich.png"><img src="assets/gallery/fig5-validation-perturbation-rich.png" width="260" alt="Perturbation validation"></a> | 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 | <a href="assets/chart-atlas/atlas-01-bar-charts.png"><img src="assets/chart-atlas/atlas-01-bar-charts.png" width="240" alt="Bar chart atlas"></a> | Group comparisons, signed deltas, grouped-within-grouped designs, stacked composition |
| Line and longitudinal trends | <a href="assets/chart-atlas/atlas-02-line-trends.png"><img src="assets/chart-atlas/atlas-02-line-trends.png" width="240" alt="Line chart atlas"></a> | Time courses, uncertainty ribbons, intervention marks, individual traces |
| Heatmaps | <a href="assets/chart-atlas/atlas-03-heatmaps.png"><img src="assets/chart-atlas/atlas-03-heatmaps.png" width="240" alt="Heatmap atlas"></a> | Z-score matrices, sequential abundance maps, annotated tables, clustered blocks |
| Scatter and bubble plots | <a href="assets/chart-atlas/atlas-04-scatter-bubble.png"><img src="assets/chart-atlas/atlas-04-scatter-bubble.png" width="240" alt="Scatter and bubble atlas"></a> | Correlation, clusters, volcano-style tests, quadrant summaries, third-variable bubbles |
| Radar and polar charts | <a href="assets/chart-atlas/atlas-05-radar-polar.png"><img src="assets/chart-atlas/atlas-05-radar-polar.png" width="240" alt="Radar and polar atlas"></a> | Multi-axis benchmarking, circular summaries, polar histograms, directional density |
| Distribution plots | <a href="assets/chart-atlas/atlas-06-distributions.png"><img src="assets/chart-atlas/atlas-06-distributions.png" width="240" alt="Distribution plot atlas"></a> | Histograms, violins, boxes, ridgelines and sample-level spread |
| Forest and interval plots | <a href="assets/chart-atlas/atlas-07-forest-interval.png"><img src="assets/chart-atlas/atlas-07-forest-interval.png" width="240" alt="Forest and interval atlas"></a> | Effect sizes, confidence intervals, point ranges, paired slope comparisons |
| Area and stacked trends | <a href="assets/chart-atlas/atlas-08-area-stacked.png"><img src="assets/chart-atlas/atlas-08-area-stacked.png" width="240" alt="Area and stacked trend atlas"></a> | Filled trajectories, stacked shares, cumulative curves, stream-like compositions |
| Image plates | <a href="assets/chart-atlas/atlas-09-image-plates.png"><img src="assets/chart-atlas/atlas-09-image-plates.png" width="240" alt="Image plate atlas"></a> | Microscopy channels, overlays, crops, scale bars and dark-panel layouts |
| Network and matrix charts | <a href="assets/chart-atlas/atlas-10-network-matrix.png"><img src="assets/chart-atlas/atlas-10-network-matrix.png" width="240" alt="Network and matrix atlas"></a> | 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
│   └── figures4papers/          ← original demo scripts and preview assets
└── 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
    └── demos.md                 ← figures4papers demo map and routing guide
```

---

## 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 `<text>` element becomes a `<path d="M...">` — unselectable,
unsearchable, and impossible to realign in Illustrator or Inkscape.  
With `'none'`, text stays as SVG `<text>` 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


---
*Source: https://skills.yangsir.net/skill/gh-nature-figure*
*Markdown mirror: https://skills.yangsir.net/api/skill/gh-nature-figure/markdown*