$npx -y skills add neuromechanist/research-skills --skill plot-stylingThis skill should be used when the user asks to "plot data", "make a plot for a paper", "make a publication plot", "create a journal-quality plot", "matplotlib for a figure", "use seaborn", "use plotnine", "use ggplot in Python", "improve plot quality", "style my plot", "clean up
| 1 | # Plot Styling |
| 2 | |
| 3 | Choose the right Python plotting library for the chart type, then apply journal-quality defaults so the panel that comes out of `savefig` is ready for the `[[scientific-figure]]` composer — no manual cleanup, no font-size firefighting, no chart junk. |
| 4 | |
| 5 | ## Two questions, two minutes |
| 6 | |
| 7 | 1. **What chart type am I drawing?** That answer picks the library (`references/library-decision-tree.md`). |
| 8 | 2. **Which journal am I targeting?** That answer picks the style sheet and font minimum (`references/sciplots-recipes.md`). |
| 9 | |
| 10 | Run those choices through the export conventions below and the output SVG should pass `figure-qa`'s plot-script branch and feed `scientific-figure` without scale-down surprises. |
| 11 | |
| 12 | ## Library decision tree (summary) |
| 13 | |
| 14 | | Chart type | Library | Why | |
| 15 | |---|---|---| |
| 16 | | Line / scatter / bar with custom layout | **matplotlib** | Most control; the lingua franca. Pair with SciencePlots styles to fix defaults. | |
| 17 | | Statistical (box, violin, regression, faceted) | **seaborn** | Better defaults than matplotlib; less code for the common cases. Built on top of matplotlib so the SciencePlots style still applies. | |
| 18 | | Grammar-of-graphics / R-style faceting | **plotnine** | Same `geom_*` API as ggplot2 without the rpy2 bridge. | |
| 19 | | Interactive HTML supplement (deck / dashboard) | **plotly** | Use plotly for the interactive companion; ship a matplotlib version for print. | |
| 20 | | 3D / volumetric / mesh | **PyVista** (when interactive matters) or **matplotlib 3d** (for static print) | matplotlib 3d is acceptable for simple panels; PyVista when the figure is the interaction. | |
| 21 | | Already in matplotlib, output looks ugly | **stay matplotlib + SciencePlots** | `plt.style.use(['science', 'nature'])` fixes 80% of "ugly defaults" complaints without rewriting. | |
| 22 | |
| 23 | Full decision tree with concrete code snippets per branch: `references/library-decision-tree.md`. |
| 24 | |
| 25 | ## Journal-quality defaults (matplotlib / seaborn) |
| 26 | |
| 27 | The cleanest path is to install [SciencePlots](https://github.com/garrettj403/SciencePlots) and apply its style sheet at the top of every plot script. The relevant style names: |
| 28 | |
| 29 | - `science` — base style (sans-serif, no chart junk, tight margins) |
| 30 | - `nature` — Nature column dimensions and font sizing |
| 31 | - `ieee` — IEEE narrow-column with grayscale-safe palette |
| 32 | - `vibrant` / `bright` / `high-contrast` — colorblind-safe palettes from Paul Tol |
| 33 | - `notebook` — slightly larger fonts for screen reading (avoid for paper) |
| 34 | |
| 35 | ```python |
| 36 | import matplotlib.pyplot as plt |
| 37 | import scienceplots # noqa: F401 (registers the styles) |
| 38 | |
| 39 | plt.style.use(["science", "nature", "no-latex"]) |
| 40 | ``` |
| 41 | |
| 42 | `no-latex` is non-optional unless you have system LaTeX installed AND want path-rendered text (in which case `validate_fonts.py` cannot inspect font sizes — see the "no-latex" note in `references/sciplots-recipes.md`). |
| 43 | |
| 44 | For a Nature panel at 1-column width with three lines: |
| 45 | |
| 46 | ```python |
| 47 | import numpy as np |
| 48 | import matplotlib.pyplot as plt |
| 49 | import scienceplots # noqa: F401 |
| 50 | |
| 51 | plt.style.use(["science", "nature", "no-latex"]) |
| 52 | |
| 53 | fig, ax = plt.subplots(figsize=(3.5, 2.5)) # 89 mm x ~63 mm |
| 54 | t = np.linspace(0, 1, 200) |
| 55 | for k, label in enumerate(("control", "drug A", "drug B")): |
| 56 | ax.plot(t, np.sin(2 * np.pi * (k + 1) * t), label=label, linewidth=1.0) |
| 57 | ax.set_xlabel("time (s)") |
| 58 | ax.set_ylabel("amplitude (a.u.)") |
| 59 | ax.legend(frameon=False, loc="upper right") |
| 60 | fig.savefig("panel_a.svg", bbox_inches="tight", transparent=True) |
| 61 | ``` |
| 62 | |
| 63 | That single style declaration sets sans-serif fonts at journal-appropriate sizes (Nature 5–7 pt range), removes the right and top spines, applies tight margin defaults, and picks a colorblind-safe palette. See `references/sciplots-recipes.md` for IEEE, Science, and APS variants. |
| 64 | |
| 65 | ## Export conventions |
| 66 | |
| 67 | Every plot panel that will be composed by `[[scientific-figure]]` should: |
| 68 | |
| 69 | 1. **Save as SVG.** `savefig("panel.svg", ...)`. SVG preserves text as `<text>` so `validate_fonts.py` can inspect every label. |
| 70 | 2. **Use `transparent=True`.** The composer expects panels with transparent backgrounds so they composite cleanly onto the figure canvas. `figure-qa`'s plot-script branch flags `transparent=False` and missing-`transparent` as issues. |
| 71 | 3. **Use `bbox_inches= |