@yibeichan/claude-skills 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,6 +11,7 @@ A collection of reusable Claude skills for neuroimaging research workflows and s
11
11
  | [bids-format](skills/bids-format/SKILL.md) | `npx @yibeichan/claude-skills install bids-format` | BIDS standard for all data types — naming conventions, dataset creation, multi-modal conversion (heudiconv, MNE-BIDS, pypet2bids), validation, derivatives, project organization, DataLad, sharing. |
12
12
  | [neuroimaging-qc](skills/neuroimaging-qc/SKILL.md) | `npx @yibeichan/claude-skills install neuroimaging-qc` | Evidence-based QC decisions for fMRI, EEG, fNIRS using metrics from fMRIPrep, MRIQC, FreeSurfer. |
13
13
  | [bidsapp-nidm-standards](skills/bidsapp-nidm-standards/SKILL.md) | `npx @yibeichan/claude-skills install bidsapp-nidm-standards` | Standards for creating NIDM-integrated BIDSapps that run through BABS. |
14
+ | [neuro-plotting](skills/neuro-plotting/SKILL.md) | `npx @yibeichan/claude-skills install neuro-plotting` | Publication-quality matplotlib for neuroscience: colorblind palettes, journal sizing, brain surfaces, transition matrices, heatmaps, multi-panel composition. |
14
15
  | [scientific-writer](skills/scientific-writer/SKILL.md) | `npx @yibeichan/claude-skills install scientific-writer` | Rigorous scientific manuscripts following IMRAD, CONSORT/STROBE/PRISMA guidelines. |
15
16
 
16
17
  ## Installation
@@ -96,4 +97,4 @@ See [CLAUDE.md](CLAUDE.md) for development guidelines or [MAINTAINERS.md](MAINTA
96
97
 
97
98
  ## License
98
99
 
99
- MIT
100
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yibeichan/claude-skills",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Claude skills for neuroimaging research workflows and scientific writing",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,500 @@
1
+ ---
2
+ name: neuro-plotting
3
+ description: >
4
+ Publication-quality matplotlib plotting for scientific papers, with deep support for
5
+ cognitive neuroscience. Covers: colorblind-friendly palettes and colormaps, compact figure
6
+ sizing for journal columns, font scaling by figure width, separate colorbar placement,
7
+ clean x-axis labels (no rotation), rcParams for publication DPI/fonts/SVG, headless
8
+ rendering on SLURM/HPC, and savefig wrappers that prevent memory leaks. Also covers
9
+ brain-specific plotting: cortical surface rendering (surfplot, yabplot, nilearn), Yeo-7
10
+ network colors, atlas parcellation heatmaps, transition matrices, and state occupancy
11
+ plots. Use this skill whenever the user is making any matplotlib figure for a paper,
12
+ poster, or presentation — whether it's a bar chart, heatmap, scatter plot, line plot,
13
+ violin plot, or brain map. Use when the user asks about figure DPI, font sizes, colormaps,
14
+ colorbar placement, colorblind-safe colors, journal figure formatting, or matplotlib
15
+ styles. Also use when they mention brain surfaces, Yeo networks, Schaefer parcellations,
16
+ fMRI activation maps, transition matrices, dwell times, or any neuroimaging visualization.
17
+ ---
18
+
19
+ # Publication-Quality Scientific Plotting
20
+
21
+ Standards and patterns for creating clean, consistent, journal-ready figures in
22
+ matplotlib. General-purpose plotting conventions (colors, fonts, sizing, colorbars)
23
+ with deep support for cognitive neuroscience visualizations (brain surfaces, network
24
+ palettes, parcellation heatmaps).
25
+
26
+ ## When to Use This Skill
27
+
28
+ - Creating or editing any matplotlib figure for a scientific paper, poster, or presentation
29
+ - Setting up consistent matplotlib styles across a project's figures
30
+ - Choosing colormaps, color palettes, or colorblind-friendly schemes
31
+ - Figuring out figure sizes or font sizes for journal submission
32
+ - Placing colorbars without overlapping axes
33
+ - Formatting axis labels, tick labels, or legends
34
+ - Plotting heatmaps, bar charts, scatter plots, violin plots, line plots
35
+ - Rendering brain surface maps (cortical/subcortical) from parcellated data
36
+ - Plotting transition matrices, state occupancies, dwell times, or dynamic FC results
37
+ - Working with Yeo-7 network labels, Schaefer parcellations, or similar brain atlases
38
+ - Debugging figure rendering issues on HPC/SLURM (headless, memory leaks, segfaults)
39
+
40
+ ---
41
+
42
+ ## Publication Style Setup
43
+
44
+ ### The `apply_publication_style()` Pattern
45
+
46
+ Every project should have a single style-configuration function that sets all rcParams
47
+ in one place. This prevents font-size drift across scripts and ensures every figure
48
+ in the paper looks like it belongs together.
49
+
50
+ Font sizes should scale with figure size. Smaller figures need smaller fonts to avoid
51
+ text dominating the plot. Pick a tier and stay consistent across the project:
52
+
53
+ | Figure width | Title | Axis labels | Tick labels | Annotations |
54
+ |-------------|-------|-------------|-------------|-------------|
55
+ | <= 3.5 in (single-col) | 7 | 6 | 5 | 5 |
56
+ | 3.5-5.0 in (1.5-col) | 8 | 7 | 6 | 5-6 |
57
+ | 5.0-7.0 in (double-col) | 9 | 8 | 7 | 6 |
58
+ | > 7.0 in (wide panel) | 10 | 9 | 8 | 7 |
59
+
60
+ ```python
61
+ import matplotlib
62
+ matplotlib.use("Agg") # must come before pyplot import for headless environments
63
+ import matplotlib.pyplot as plt
64
+
65
+ def apply_publication_style(figwidth="double"):
66
+ """Call once at script start, before any plt.figure().
67
+
68
+ figwidth: "single" (<=3.5in), "1.5" (3.5-5in), "double" (5-7in), "wide" (>7in)
69
+ """
70
+ font_tiers = {
71
+ "single": {"font.size": 6, "figure.titlesize": 7, "axes.titlesize": 7,
72
+ "axes.labelsize": 6, "xtick.labelsize": 5, "ytick.labelsize": 5},
73
+ "1.5": {"font.size": 7, "figure.titlesize": 8, "axes.titlesize": 8,
74
+ "axes.labelsize": 7, "xtick.labelsize": 6, "ytick.labelsize": 6},
75
+ "double": {"font.size": 8, "figure.titlesize": 9, "axes.titlesize": 9,
76
+ "axes.labelsize": 8, "xtick.labelsize": 7, "ytick.labelsize": 7},
77
+ "wide": {"font.size": 9, "figure.titlesize": 10, "axes.titlesize": 10,
78
+ "axes.labelsize": 9, "xtick.labelsize": 8, "ytick.labelsize": 8},
79
+ }
80
+ params = {
81
+ "figure.dpi": 300,
82
+ "axes.facecolor": "white",
83
+ "figure.facecolor": "white",
84
+ "svg.fonttype": "none", # editable text in Illustrator/Inkscape
85
+ }
86
+ params.update(font_tiers.get(figwidth, font_tiers["double"]))
87
+ plt.rcParams.update(params)
88
+ ```
89
+
90
+ **Why these values:**
91
+ - **300 DPI** is the minimum most journals accept; setting it globally means you never forget.
92
+ - **Scaled font tiers** keep text proportional to figure area — a 3.5-inch figure with 10 pt labels looks cramped; 5-6 pt breathes.
93
+ - **`svg.fonttype: "none"`** keeps text as real glyphs (not paths) so collaborators can edit labels in vector editors.
94
+ - **White backgrounds** prevent the grey canvas that matplotlib defaults can produce.
95
+
96
+ If using seaborn, call `apply_publication_style()` **after** `sns.set_theme()` so your
97
+ rcParams take precedence.
98
+
99
+ ### Saving Figures
100
+
101
+ ```python
102
+ def savefig(fig, path, dpi=300):
103
+ """Save and close — prevents memory leaks in batch/SLURM jobs."""
104
+ fig.savefig(path, dpi=dpi, bbox_inches="tight")
105
+ plt.close(fig)
106
+ ```
107
+
108
+ Always close figures after saving. On HPC jobs that loop over subjects/states, unclosed
109
+ figures accumulate and eventually OOM-kill the process. The `savefig` wrapper makes this
110
+ automatic.
111
+
112
+ - Use `.png` for raster, `.svg` for vector.
113
+ - Always call `fig.tight_layout()` before saving to avoid clipped labels.
114
+
115
+ ---
116
+
117
+ ## Figure Sizes
118
+
119
+ Prefer compact figures. Journals shrink large figures to fit columns anyway, so a
120
+ figure designed at its final printed size will look sharper than one designed large
121
+ and scaled down (where fonts become illegibly small). Start small and only go bigger
122
+ if the data genuinely needs space.
123
+
124
+ | Layout | Size (w x h inches) | When to use |
125
+ |--------|---------------------|-------------|
126
+ | Single-column | `(3.5, 2.5)` | Most inline figures |
127
+ | 1.5-column | `(5.0, 3.0)` | Medium panels, grouped bar charts |
128
+ | Double-column / full-width | `(7.0, 4.0)` | Multi-panel composites |
129
+ | Wide heatmap | `(10, max(3, K*0.3+1))` | Transition matrices, large grids |
130
+ | Brain gallery (cortical + subcortical) | `(8, 3)` per state | Side-by-side brain views |
131
+ | Dwell-time grid | `(3*ncols, 2.5*nrows)` | Per-state distribution panels |
132
+
133
+ Scale height dynamically when the number of rows depends on data (e.g., K states, N networks).
134
+
135
+ ---
136
+
137
+ ## X-Axis Labels: Never Rotate
138
+
139
+ Rotated x-axis labels are hard to read and look messy. If labels are too long to fit
140
+ horizontally, wrap them onto two lines instead:
141
+
142
+ ```python
143
+ # Bad: rotated labels
144
+ ax.set_xticklabels(labels, rotation=45, ha="right")
145
+
146
+ # Good: wrap long labels with newlines
147
+ wrapped = [lab.replace(" ", "\n") if len(lab) > 10 else lab for lab in labels]
148
+ ax.set_xticklabels(wrapped)
149
+ ```
150
+
151
+ For network names or condition labels, abbreviate rather than rotate:
152
+ - `"SalVentAttn"` -> `"Sal/\nVentAttn"` or just `"SVA"`
153
+ - `"Default Mode"` -> `"Default\nMode"` or `"DMN"`
154
+
155
+ If even wrapping doesn't fit, the figure is probably too narrow — widen it or use
156
+ a horizontal bar chart instead.
157
+
158
+ ---
159
+
160
+ ## Colorbars: Always Separate
161
+
162
+ Never let matplotlib auto-place a colorbar with `plt.colorbar()` — it steals space
163
+ from the axes and causes misalignment in multi-panel figures. Instead, create a
164
+ dedicated axes for the colorbar:
165
+
166
+ ```python
167
+ from mpl_toolkits.axes_grid1 import make_axes_locatable
168
+
169
+ # Option 1: Adjacent colorbar via axes_grid1
170
+ divider = make_axes_locatable(ax)
171
+ cax = divider.append_axes("right", size="3%", pad=0.08)
172
+ fig.colorbar(im, cax=cax)
173
+
174
+ # Option 2: Explicit axes in gridspec (best for multi-panel)
175
+ fig, axes = plt.subplots(1, 3, figsize=(7, 3),
176
+ gridspec_kw={"width_ratios": [1, 1, 0.05]})
177
+ im = axes[0].imshow(data, cmap="RdBu_r", vmin=-vmax, vmax=vmax)
178
+ axes[1].imshow(data2, cmap="RdBu_r", vmin=-vmax, vmax=vmax)
179
+ fig.colorbar(im, cax=axes[2])
180
+
181
+ # Option 3: Single colorbar for an entire row
182
+ fig.colorbar(im, ax=axes[:2], location="right", shrink=0.8, pad=0.02)
183
+ ```
184
+
185
+ The key idea: the colorbar gets its own axes with explicit size/position, so it
186
+ never overlaps or squeezes the main plot. For multi-panel figures that share a
187
+ colormap, one shared colorbar is cleaner than one per panel.
188
+
189
+ ---
190
+
191
+ ## Color Palettes for Neuroscience
192
+
193
+ ### Default: Colorblind-Friendly
194
+
195
+ About 8% of men and 0.5% of women have some form of colour vision deficiency. Every
196
+ figure should be readable by everyone. Use colorblind-safe palettes by default, not
197
+ as an afterthought.
198
+
199
+ **For categorical data** (conditions, groups, states), use one of these:
200
+
201
+ ```python
202
+ import seaborn as sns
203
+
204
+ # Best default — 10 distinct, colorblind-safe colors
205
+ CB_PALETTE = sns.color_palette("colorblind")
206
+
207
+ # Alternative: Wong (2011) palette — 8 colors, widely used in science
208
+ WONG_PALETTE = [
209
+ "#000000", # black
210
+ "#E69F00", # orange
211
+ "#56B4E9", # sky blue
212
+ "#009E73", # bluish green
213
+ "#F0E442", # yellow
214
+ "#0072B2", # blue
215
+ "#D55E00", # vermilion
216
+ "#CC79A7", # reddish purple
217
+ ]
218
+
219
+ # Use directly
220
+ ax.bar(x, y, color=CB_PALETTE[:len(x)])
221
+ ```
222
+
223
+ Only fall back to non-colorblind palettes when there's a strong domain reason (e.g.,
224
+ Yeo-7 network colors, which are field-standard).
225
+
226
+ ### Yeo-7 Network Colors (+ Subcortical)
227
+
228
+ The Yeo 2011 7-network parcellation has canonical colors used across the field. These
229
+ are NOT fully colorblind-safe, but they're the field standard for brain network
230
+ visualizations. Use them only for network-labeled data; for everything else, prefer
231
+ the colorblind palettes above.
232
+
233
+ ```python
234
+ NETWORK_COLORS = {
235
+ "Vis": "#781286",
236
+ "SomMot": "#4682B4",
237
+ "DorsAttn": "#00760E",
238
+ "SalVentAttn": "#C43AFA",
239
+ "Limbic": "#DCF8A4",
240
+ "Cont": "#E69422",
241
+ "Default": "#CD3E4E",
242
+ "Subcortical": "#808080",
243
+ }
244
+
245
+ NETWORK_ORDER = [
246
+ "Vis", "SomMot", "DorsAttn", "SalVentAttn",
247
+ "Limbic", "Cont", "Default", "Subcortical",
248
+ ]
249
+ ```
250
+
251
+ Define these once in a shared module and import everywhere. Never redefine in individual
252
+ scripts — that's how colors silently diverge between figures.
253
+
254
+ When using Yeo colors, always add a second visual channel (hatching, markers, linestyle)
255
+ to compensate for the colorblind-unfriendly palette.
256
+
257
+ ### State Role Colors (for Transition Topology)
258
+
259
+ When labeling states by their role in a transition graph (gateway, sink, source):
260
+
261
+ ```python
262
+ STATE_ROLE_COLORS = {
263
+ "gateway": "#E69F00", # orange (Wong)
264
+ "sink": "#D55E00", # vermilion (Wong)
265
+ "source": "#0072B2", # blue (Wong)
266
+ "intermediate": "#999999", # grey
267
+ }
268
+ ```
269
+
270
+ ### Legend Helpers
271
+
272
+ Build legend handles programmatically rather than relying on plot order:
273
+
274
+ ```python
275
+ from matplotlib.patches import Patch
276
+
277
+ def make_network_legend_handles(networks, colors=NETWORK_COLORS):
278
+ return [Patch(facecolor=colors[n], label=n) for n in networks]
279
+
280
+ def make_role_legend_handles(roles=STATE_ROLE_COLORS):
281
+ return [Patch(facecolor=c, label=r) for r, c in roles.items()]
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Colormap Conventions
287
+
288
+ Choosing the right colormap for your data type matters for interpretability. Prefer
289
+ colormaps that are **perceptually uniform** and **colorblind-safe**. All recommendations
290
+ below meet both criteria.
291
+
292
+ | Data type | Colormap | CB-safe? | Notes |
293
+ |-----------|----------|----------|-------|
294
+ | Brain activation (diverging) | `"RdBu_r"` | yes | Centre at 0; symmetric `vmin/vmax` |
295
+ | Brain activation (positive-only) | `"YlOrRd"` | yes | |
296
+ | Transition probability (off-diag) | `"YlOrRd"` | yes | |
297
+ | Transition probability (full) | `"viridis"` + `LogNorm` | yes | Log scale for sparse matrices |
298
+ | Occupancy / recurrence | `"cividis"` | yes | Better than `YlGnBu` for CVD |
299
+ | Cosine similarity | `"RdBu_r"` | yes | Centre at 0 |
300
+ | Count / confusion matrix | `"viridis"` or `"cividis"` | yes | Normalise rows first |
301
+
302
+ **Avoid:** `"jet"`, `"rainbow"`, `"hot"`, `"hsv"` — these are not perceptually uniform
303
+ and fail badly for colorblind viewers. If you see `jet` in existing code, replace it.
304
+
305
+ **Key principle:** diverging data (positive and negative values around zero) needs a
306
+ diverging colormap centered at zero. Sequential data (counts, probabilities) needs a
307
+ sequential colormap. Getting this wrong misleads readers.
308
+
309
+ For diverging colormaps, compute symmetric limits:
310
+ ```python
311
+ vmax = np.percentile(np.abs(data), 95)
312
+ im = ax.imshow(data, cmap="RdBu_r", vmin=-vmax, vmax=vmax)
313
+ ```
314
+
315
+ The 95th percentile avoids letting outliers wash out the color range.
316
+
317
+ ---
318
+
319
+ ## Typography in Figures
320
+
321
+ - **Axes titles**: sentence case, no trailing period (`"Recurrence score"`)
322
+ - **State labels**: `k=N` inline; `State N` in axis labels
323
+ - **Units**: always include — `"Dwell time (s)"`, `"Time (TRs)"`, `"Z-score"`
324
+
325
+ ---
326
+
327
+ ## Accessibility
328
+
329
+ 1. **Colorblind-safe palettes first.** (See Color Palettes section above.)
330
+ 2. **Dual encoding**: colour AND shape/linestyle for every categorical distinction.
331
+ Scatter: vary marker shape. Lines: vary linestyle. Readable in greyscale.
332
+ 3. **Minimum font sizes**: 5 pt absolute minimum; scale with figure size per font tier table.
333
+
334
+ ---
335
+
336
+ ## Dynamic FC Plots (Transition Matrices, Occupancy, Dwell Times)
337
+
338
+ For dynamic functional connectivity analyses, see `references/dynamic-fc-plots.md` for
339
+ complete plotting functions. Quick patterns:
340
+
341
+ - **Transition matrices**: Use `YlOrRd` for probabilities, `LogNorm` for sparse matrices,
342
+ `RdBu_r` centered at 0 for difference matrices. Always create a separate colorbar axes.
343
+ - **State occupancy**: Grouped bar charts with error bars. Use Wong palette colors for groups.
344
+ Add significance brackets with `add_significance_bracket()`.
345
+ - **Dwell times**: Violin or raincloud plots per state. Grid layout with `ncols=4`.
346
+ - **State sequences**: Color-coded strips via `imshow` with `ListedColormap`. Carpet plots
347
+ for multi-subject comparisons.
348
+
349
+ ---
350
+
351
+ ## Heatmaps and Connectivity Matrices
352
+
353
+ For parcellation-level heatmaps and FC matrices, see `references/heatmaps-matrices.md`.
354
+ Key principles:
355
+
356
+ - Sort parcels by network so block structure is visible
357
+ - Draw thin black lines at network boundaries (`ax.axhline`, `ax.axvline`)
358
+ - Use symmetric color range for correlation-based data (`RdBu_r`, centered at 0)
359
+ - Overlay significance with FDR correction for statistical maps
360
+ - Use `sns.clustermap` only for exploration — switch to manual ordering for publication
361
+
362
+ ---
363
+
364
+ ## Multi-Panel Figure Composition
365
+
366
+ For complex multi-panel layouts, see `references/multi-panel-composition.md`. Key patterns:
367
+
368
+ - Use `gridspec` (not `plt.subplots`) when panels have different sizes
369
+ - Use `subgridspec` for nested layouts (e.g., brain maps row + metrics row)
370
+ - Panel labels: bold uppercase **A**, **B**, **C** at top-left, 1-2 pt larger than axis labels
371
+ - Shared colorbars: always via explicit `cax` in gridspec, never auto-placed
372
+
373
+ ---
374
+
375
+ ## Brain Surface Rendering
376
+
377
+ For detailed guidance on rendering cortical and subcortical brain maps (using surfplot,
378
+ yabplot, nilearn, or pyvista), including headless/HPC setup and atlas management, see
379
+ `references/brain-rendering.md`.
380
+
381
+ Quick pattern for surfplot (recommended for publication-quality cortical surfaces):
382
+
383
+ ```python
384
+ from surfplot import Plot
385
+ from neuromaps.datasets import fetch_fslr
386
+
387
+ surfaces = fetch_fslr()
388
+ p = Plot(surfaces["inflated"], views=["lateral", "medial"],
389
+ size=(400, 200), zoom=1.2)
390
+ p.add_layer(stat_map_lh, stat_map_rh, cmap="RdBu_r",
391
+ color_range=(-vmax, vmax))
392
+ fig = p.build()
393
+ fig.savefig("brain_surface.png", dpi=300, bbox_inches="tight")
394
+ ```
395
+
396
+ Quick pattern for yabplot:
397
+
398
+ ```python
399
+ # Headless setup — MUST come before any pyvista/yabplot import
400
+ from utils.viz_yabplot import setup_yabplot_headless
401
+ setup_yabplot_headless()
402
+
403
+ from utils.viz_yabplot import load_parcel_labels, render_brain_pattern
404
+
405
+ labels_df = load_parcel_labels("atlas-4S156Parcels")
406
+ vmax = np.percentile(np.abs(all_patterns), 95)
407
+ cortical_img, subcortical_img = render_brain_pattern(
408
+ pattern, labels_df, "atlas-4S156Parcels", (-vmax, vmax), cmap="RdBu_r"
409
+ )
410
+ ```
411
+
412
+ Then composite into matplotlib:
413
+ ```python
414
+ fig, axes = plt.subplots(1, 2, figsize=(10, 4),
415
+ gridspec_kw={"width_ratios": [2.5, 1.5]})
416
+ axes[0].imshow(cortical_img); axes[0].axis("off")
417
+ axes[1].imshow(subcortical_img); axes[1].axis("off")
418
+ savefig(fig, out_path)
419
+ ```
420
+
421
+ ---
422
+
423
+ ## Atlas Metadata and Network Sorting
424
+
425
+ When plotting parcellation-level data (heatmaps, connectivity matrices), sort parcels
426
+ by network so the block structure is visible:
427
+
428
+ ```python
429
+ from utils.atlas import load_atlas_metadata, sort_parcels_by_network
430
+
431
+ atlas_df = load_atlas_metadata("atlas-4S156Parcels")
432
+ sort_idx, boundaries = sort_parcels_by_network(atlas_df)
433
+
434
+ # Reorder matrix rows/columns by network
435
+ sorted_matrix = matrix[np.ix_(sort_idx, sort_idx)]
436
+
437
+ # Draw network boundaries
438
+ for b in boundaries:
439
+ ax.axhline(b, color="k", linewidth=0.5)
440
+ ax.axvline(b, color="k", linewidth=0.5)
441
+ ```
442
+
443
+ ---
444
+
445
+ ## Common Pitfalls
446
+
447
+ | Problem | Solution |
448
+ |---------|----------|
449
+ | Blank figure on SLURM / HPC | `matplotlib.use("Agg")` before importing pyplot (or use `apply_publication_style()`) |
450
+ | Memory leak in batch jobs | Always `plt.close(fig)` after saving — use the `savefig` wrapper |
451
+ | Network colors differ between scripts | Import from one shared module; never redefine |
452
+ | Font sizes inconsistent | All sizes from `apply_publication_style()`; don't override per-script |
453
+ | Wrong DPI in saved file | Set DPI in `savefig()`, not in `plt.show()` |
454
+ | Seaborn theme overrides your style | Call `apply_publication_style()` after `sns.set_theme()` |
455
+ | SVG text rendered as paths | Set `svg.fonttype: "none"` (the style function handles this) |
456
+ | Brain rendering segfaults on HPC | Call headless setup before any pyvista/vtk import; or use surfplot (no VTK needed) |
457
+ | Color range differs between panels | Compute `vmin/vmax` once from all data, pass to every panel |
458
+ | Axis labels clipped in saved file | Use `bbox_inches="tight"` in `savefig` |
459
+ | Rotated x-axis labels | Never rotate — wrap text to two lines or abbreviate instead |
460
+ | Colorbar overlapping axes | Create a separate `cax` for the colorbar; never use bare `plt.colorbar()` |
461
+ | Colorblind-unfriendly palette | Use `seaborn "colorblind"` or Wong palette; reserve Yeo-7 for network plots only |
462
+ | `jet` or `rainbow` colormap | Replace with `viridis`, `cividis`, or `RdBu_r` — always perceptually uniform |
463
+
464
+ ---
465
+
466
+ ## Project Setup Checklist
467
+
468
+ When setting up plotting for a new neuroscience project:
469
+
470
+ 1. Create `utils/plot_style.py` with `apply_publication_style()`, color constants, and `savefig()`
471
+ 2. Define project-specific constants (TR, atlas name, condition colors) in the same module
472
+ 3. If rendering brain surfaces, create `utils/viz_brain.py` with headless setup and rendering functions
473
+ 4. If using parcellated data, create `utils/atlas.py` for metadata loading and network sorting
474
+ 5. Every plotting script imports from these modules — no local redefinitions
475
+
476
+ This structure ensures that changing a color, font size, or DPI propagates to every
477
+ figure in the project automatically.
478
+
479
+ ---
480
+
481
+ ## Quick Reference: Imports Template
482
+
483
+ ```python
484
+ import numpy as np
485
+ import matplotlib
486
+ matplotlib.use("Agg")
487
+ import matplotlib.pyplot as plt
488
+
489
+ from utils.plot_style import (
490
+ apply_publication_style, savefig,
491
+ NETWORK_COLORS, NETWORK_ORDER,
492
+ )
493
+
494
+ apply_publication_style()
495
+
496
+ # ... your plotting code ...
497
+ # fig = plt.figure(figsize=(7.0, 5.0))
498
+ # ...
499
+ # savefig(fig, "figures/my_figure.png")
500
+ ```