psyclaw 0.28.1 → 0.28.2

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.
Files changed (128) hide show
  1. package/README.md +5 -5
  2. package/agents/recommended/catalog.json +1 -1
  3. package/dist/src/adapters/pi/extension.js +43 -9
  4. package/dist/src/adapters/pi/extension.js.map +1 -1
  5. package/dist/src/ars/mode-editor.d.ts +20 -0
  6. package/dist/src/ars/mode-editor.js +57 -0
  7. package/dist/src/ars/mode-editor.js.map +1 -0
  8. package/dist/src/ars/profile.d.ts +4 -1
  9. package/dist/src/ars/profile.js +23 -6
  10. package/dist/src/ars/profile.js.map +1 -1
  11. package/package.json +10 -3
  12. package/vendor/academic-paper-skills/LICENSE +21 -0
  13. package/vendor/academic-paper-skills/NOTICE.md +7 -0
  14. package/vendor/academic-paper-skills/PSYCLAW_SOURCE.json +14 -0
  15. package/vendor/academic-paper-skills/academic-paper-composer/SKILL.md +840 -0
  16. package/vendor/academic-paper-skills/academic-paper-composer/references/section_guides.md +675 -0
  17. package/vendor/academic-paper-skills/academic-paper-composer/references/writing_standards.md +629 -0
  18. package/vendor/academic-paper-skills/academic-paper-composer/scripts/chapter_quality_check.py +470 -0
  19. package/vendor/academic-paper-skills/academic-paper-composer/scripts/final_evaluation.py +550 -0
  20. package/vendor/academic-paper-skills/academic-paper-strategist/SKILL.md +670 -0
  21. package/vendor/academic-paper-skills/academic-paper-strategist/references/quality_standards.md +336 -0
  22. package/vendor/academic-paper-skills/academic-paper-strategist/references/search_strategy.md +459 -0
  23. package/vendor/academic-paper-skills/academic-paper-strategist/scripts/evaluate_samples.py +300 -0
  24. package/vendor/academic-paper-skills/academic-paper-strategist/scripts/gap_analysis.py +399 -0
  25. package/vendor/ars/pi/wrapper.js +9 -0
  26. package/vendor/nature-skills/LICENSE +201 -0
  27. package/vendor/nature-skills/NOTICE.md +7 -0
  28. package/vendor/nature-skills/PSYCLAW_SOURCE.json +19 -0
  29. package/vendor/nature-skills/skills/nature-figure/README.md +107 -0
  30. package/vendor/nature-skills/skills/nature-figure/README_EN.md +107 -0
  31. package/vendor/nature-skills/skills/nature-figure/SKILL.md +154 -0
  32. package/vendor/nature-skills/skills/nature-figure/agents/openai.yaml +4 -0
  33. package/vendor/nature-skills/skills/nature-figure/evals/evals.json +325 -0
  34. package/vendor/nature-skills/skills/nature-figure/manifest.yaml +103 -0
  35. package/vendor/nature-skills/skills/nature-figure/references/ai-graphical-abstract-workflow.md +129 -0
  36. package/vendor/nature-skills/skills/nature-figure/references/api.md +576 -0
  37. package/vendor/nature-skills/skills/nature-figure/references/asset-adaptation.md +72 -0
  38. package/vendor/nature-skills/skills/nature-figure/references/backend-selection.md +118 -0
  39. package/vendor/nature-skills/skills/nature-figure/references/chart-types.md +348 -0
  40. package/vendor/nature-skills/skills/nature-figure/references/common-patterns.md +371 -0
  41. package/vendor/nature-skills/skills/nature-figure/references/demos.md +58 -0
  42. package/vendor/nature-skills/skills/nature-figure/references/design-theory.md +467 -0
  43. package/vendor/nature-skills/skills/nature-figure/references/figure-contract.md +122 -0
  44. package/vendor/nature-skills/skills/nature-figure/references/figure-legend-conventions.md +90 -0
  45. package/vendor/nature-skills/skills/nature-figure/references/multipanel-evidence-architecture.md +268 -0
  46. package/vendor/nature-skills/skills/nature-figure/references/nature-2026-observations.md +124 -0
  47. package/vendor/nature-skills/skills/nature-figure/references/nature-article-requirements.md +149 -0
  48. package/vendor/nature-skills/skills/nature-figure/references/openrouter-image-generation.md +141 -0
  49. package/vendor/nature-skills/skills/nature-figure/references/qa-contract.md +344 -0
  50. package/vendor/nature-skills/skills/nature-figure/references/r-template-index.md +66 -0
  51. package/vendor/nature-skills/skills/nature-figure/references/r-workflow.md +234 -0
  52. package/vendor/nature-skills/skills/nature-figure/references/template-catalog.md +33 -0
  53. package/vendor/nature-skills/skills/nature-figure/references/tutorials.md +260 -0
  54. package/vendor/nature-skills/skills/nature-figure/requirements.txt +1 -0
  55. package/vendor/nature-skills/skills/nature-figure/scripts/audit_figure_collisions.py +742 -0
  56. package/vendor/nature-skills/skills/nature-figure/scripts/audit_panel_alignment.py +933 -0
  57. package/vendor/nature-skills/skills/nature-figure/scripts/audit_pdf_text.py +152 -0
  58. package/vendor/nature-skills/skills/nature-figure/scripts/figure_safety.py +50 -0
  59. package/vendor/nature-skills/skills/nature-figure/scripts/generate_openrouter_schematic.py +260 -0
  60. package/vendor/nature-skills/skills/nature-figure/scripts/nature_figure_backend.py +93 -0
  61. package/vendor/nature-skills/skills/nature-figure/scripts/panel_alignment.R +188 -0
  62. package/vendor/nature-skills/skills/nature-figure/scripts/plot_templates.py +604 -0
  63. package/vendor/nature-skills/skills/nature-figure/scripts/validate_figure.py +835 -0
  64. package/vendor/nature-skills/skills/nature-figure/static/core/contract.md +55 -0
  65. package/vendor/nature-skills/skills/nature-figure/static/core/stance.md +41 -0
  66. package/vendor/nature-skills/skills/nature-figure/static/fragments/backend/python.md +56 -0
  67. package/vendor/nature-skills/skills/nature-figure/static/fragments/backend/r.md +63 -0
  68. package/vendor/nature-skills/skills/nature-figure/tests/test_figure_safety.py +209 -0
  69. package/vendor/nature-skills/skills/nature-polishing/README.md +58 -0
  70. package/vendor/nature-skills/skills/nature-polishing/README_EN.md +58 -0
  71. package/vendor/nature-skills/skills/nature-polishing/SKILL.md +111 -0
  72. package/vendor/nature-skills/skills/nature-polishing/agents/openai.yaml +4 -0
  73. package/vendor/nature-skills/skills/nature-polishing/manifest.yaml +101 -0
  74. package/vendor/nature-skills/skills/nature-polishing/references/latex-layout.md +211 -0
  75. package/vendor/nature-skills/skills/nature-polishing/references/nat-comms-2025-diction.md +73 -0
  76. package/vendor/nature-skills/skills/nature-polishing/references/phrasebank-playbook.md +175 -0
  77. package/vendor/nature-skills/skills/nature-polishing/references/published-article-patterns.md +126 -0
  78. package/vendor/nature-skills/skills/nature-polishing/references/section-moves.md +252 -0
  79. package/vendor/nature-skills/skills/nature-polishing/references/style-guardrails.md +94 -0
  80. package/vendor/nature-skills/skills/nature-polishing/references/writing-strategy.md +160 -0
  81. package/vendor/nature-skills/skills/nature-polishing/static/core/failure-modes.md +29 -0
  82. package/vendor/nature-skills/skills/nature-polishing/static/core/output-format.md +21 -0
  83. package/vendor/nature-skills/skills/nature-polishing/static/core/stance.md +26 -0
  84. package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/generic.md +17 -0
  85. package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nat-comms.md +38 -0
  86. package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nat-mach-intell.md +102 -0
  87. package/vendor/nature-skills/skills/nature-polishing/static/fragments/journal/nature.md +19 -0
  88. package/vendor/nature-skills/skills/nature-polishing/static/fragments/language/en.md +19 -0
  89. package/vendor/nature-skills/skills/nature-polishing/static/fragments/language/zh-to-en.md +19 -0
  90. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/algorithmic.md +18 -0
  91. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/hypothesis.md +17 -0
  92. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/methods.md +35 -0
  93. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/research.md +29 -0
  94. package/vendor/nature-skills/skills/nature-polishing/static/fragments/paper_type/review.md +17 -0
  95. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/abstract.md +26 -0
  96. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/conclusion.md +15 -0
  97. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/discussion.md +48 -0
  98. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/intro.md +33 -0
  99. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/methods.md +26 -0
  100. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/results.md +61 -0
  101. package/vendor/nature-skills/skills/nature-polishing/static/fragments/section/title.md +17 -0
  102. package/vendor/nature-skills/skills/nature-ref-verifier/README.md +44 -0
  103. package/vendor/nature-skills/skills/nature-ref-verifier/README_EN.md +44 -0
  104. package/vendor/nature-skills/skills/nature-ref-verifier/SKILL.md +196 -0
  105. package/vendor/nature-skills/skills/nature-ref-verifier/agents/openai.yaml +4 -0
  106. package/vendor/nature-skills/skills/nature-ref-verifier/manifest.yaml +19 -0
  107. package/vendor/nature-skills/skills/nature-ref-verifier/references/common-patterns.md +96 -0
  108. package/vendor/nature-skills/skills/nature-shared/README.md +43 -0
  109. package/vendor/nature-skills/skills/nature-shared/README_EN.md +43 -0
  110. package/vendor/nature-skills/skills/nature-shared/SKILL.md +33 -0
  111. package/vendor/nature-skills/skills/nature-shared/agents/openai.yaml +6 -0
  112. package/vendor/nature-skills/skills/nature-shared/core/consistency-sweep.md +122 -0
  113. package/vendor/nature-skills/skills/nature-shared/core/discussion-argument-language.md +192 -0
  114. package/vendor/nature-skills/skills/nature-shared/core/ethics.md +88 -0
  115. package/vendor/nature-skills/skills/nature-shared/core/main-text-discipline.md +179 -0
  116. package/vendor/nature-skills/skills/nature-shared/core/nature-abstract.md +171 -0
  117. package/vendor/nature-skills/skills/nature-shared/core/nature-introduction.md +164 -0
  118. package/vendor/nature-skills/skills/nature-shared/core/nature-results-discussion.md +215 -0
  119. package/vendor/nature-skills/skills/nature-shared/core/paper-type-taxonomy.md +41 -0
  120. package/vendor/nature-skills/skills/nature-shared/core/reader-workflow.md +21 -0
  121. package/vendor/nature-skills/skills/nature-shared/core/research-compliance.md +192 -0
  122. package/vendor/nature-skills/skills/nature-shared/core/terminology-ledger.md +58 -0
  123. package/vendor/nature-skills/skills/nature-shared/journal-formats/nat-comms.md +139 -0
  124. package/vendor/nature-skills/skills/nature-shared/journal-formats/nature-machine-intelligence.md +431 -0
  125. package/vendor/nature-skills/skills/nature-shared/journal-formats/nature.md +313 -0
  126. package/vendor/nature-skills/skills/nature-shared/manifest.yaml +52 -0
  127. package/vendor/nature-skills/skills/nature-shared/scripts/check_consistency.py +273 -0
  128. package/vendor/nature-skills/skills/nature-shared/tests/test_check_consistency.py +63 -0
@@ -0,0 +1,33 @@
1
+ # Validated Plot Template Catalog
2
+
3
+ Use this catalog after the backend is resolved to Python and after `asset-adaptation.md` confirms that the requested panel matches a template semantically. These templates use only NumPy and Matplotlib, require real CSV input for production, and produce SVG, PDF, 600 dpi TIFF, and a count-based QA JSON record.
4
+
5
+ Run `python scripts/plot_templates.py <subcommand> --help` for the complete interface.
6
+
7
+ | Subcommand | Required CSV shape | Main safeguards |
8
+ |---|---|---|
9
+ | `volcano` | gene, effect-size, adjusted-p columns | positive p-value check, explicit thresholds, all points retained, large-data rasterization |
10
+ | `roc` | one FPR column plus one or more TPR columns | range check in `[0,1]`, stable FPR sorting, trapezoidal AUC recorded |
11
+ | `dotplot` | row category, column category, size, color | complete category-order validation, no silent category removal |
12
+ | `marginal` | x, y, optional group | preserves all observations, separates 2D joint structure from 1D marginal distributions |
13
+ | `paired` | pair ID, condition, value | exactly two conditions, rejects duplicate or incomplete pairs unless exclusion is explicit |
14
+
15
+ ## Production examples
16
+
17
+ ```bash
18
+ python skills/nature-figure/scripts/plot_templates.py volcano \
19
+ --input results.csv --gene-col gene --effect-col log2fc --p-col padj \
20
+ --output figures/volcano
21
+
22
+ python skills/nature-figure/scripts/plot_templates.py roc \
23
+ --input roc.csv --fpr-col fpr --tpr-cols model_a,model_b \
24
+ --output figures/roc
25
+
26
+ python skills/nature-figure/scripts/plot_templates.py dotplot \
27
+ --input markers.csv --row-col cell_type --column-col gene \
28
+ --size-col pct_exp --color-col avg_exp_scaled --output figures/markers
29
+ ```
30
+
31
+ Never use `--demo` for a manuscript deliverable. It exists only for explicit smoke tests and is marked in the QA JSON. If required numeric data are missing or non-finite, the default is to stop. `--drop-incomplete` is an explicit exception that records exclusion counts; it is not permission to hide scientifically important missingness.
32
+
33
+ The templates do not compute hypothesis tests. Add statistics only after defining the replicate unit, test, assumptions, multiplicity correction, and legend reporting contract.
@@ -0,0 +1,260 @@
1
+ # Tutorials — Nature Figure Making
2
+
3
+ ## Contents
4
+
5
+ - [Tutorial 1: Grouped bar chart (multi-metric comparison)](#tutorial-1-grouped-bar-chart-multi-metric-comparison)
6
+ - [Tutorial 2: Ablation bar chart (alpha-graduated, horizontal)](#tutorial-2-ablation-bar-chart-alpha-graduated-horizontal)
7
+ - [Tutorial 3: Multi-panel trend with shared legend](#tutorial-3-multi-panel-trend-with-shared-legend)
8
+ - [Tutorial 4: Heatmap with dual colormaps (positive/negative columns)](#tutorial-4-heatmap-with-dual-colormaps-positivenegative-columns)
9
+ - [Related files](#related-files)
10
+
11
+
12
+ End-to-end walkthroughs for the most common publication figure types.
13
+ All examples use helpers from [api.md](api.md) and patterns from [common-patterns.md](common-patterns.md).
14
+ For the retained third-party demo map, copyright boundary, and original reimplementation guidance, open [demos.md](demos.md).
15
+
16
+ ---
17
+
18
+ ## Tutorial 1: Grouped bar chart (multi-metric comparison)
19
+
20
+ **Goal**: Several methods compared across multiple metrics. Legend in a dedicated panel.
21
+ When methods belong to related families, use one coherent baseline family plus one coherent hero family.
22
+
23
+ ```python
24
+ import os
25
+ import numpy as np
26
+ import matplotlib.pyplot as plt
27
+ from matplotlib import gridspec
28
+
29
+ # --- Style ---
30
+ plt.rcParams['font.family'] = 'sans-serif'
31
+ plt.rcParams['font.sans-serif'] = ['Arial']
32
+ plt.rcParams['svg.fonttype'] = 'none'
33
+ plt.rcParams['font.size'] = 24
34
+ plt.rcParams['axes.spines.right'] = False
35
+ plt.rcParams['axes.spines.top'] = False
36
+ plt.rcParams['axes.linewidth'] = 3
37
+
38
+ # --- Data ---
39
+ methods = ['ResNet1d18', 'ResNet1d34', 'ECGFounder', 'CSFM-Tiny', 'CSFM-Base', 'CSFM-Large']
40
+ colors = ['#484878', '#7884B4', '#B4C0E4', '#E4E4F0', '#E4CCD8', '#F0C0CC']
41
+ metrics = ['Metric 1', 'Metric 2', 'Metric 3']
42
+ mean = {
43
+ 'Metric 1': np.array([0.81, 0.83, 0.86, 0.89, 0.91, 0.92]),
44
+ 'Metric 2': np.array([0.63, 0.67, 0.71, 0.74, 0.77, 0.79]),
45
+ 'Metric 3': np.array([0.41, 0.45, 0.49, 0.53, 0.56, 0.58]),
46
+ }
47
+ std = {k: v * 0.03 for k, v in mean.items()} # placeholder
48
+
49
+ # --- Figure ---
50
+ fig = plt.figure(figsize=(28, 6))
51
+ gs = gridspec.GridSpec(1, len(metrics) + 1) # +1 for legend panel
52
+
53
+ handles, labels = None, None
54
+ for col, metric in enumerate(metrics):
55
+ ax = fig.add_subplot(gs[col])
56
+ bars = ax.bar(
57
+ range(len(methods)),
58
+ mean[metric],
59
+ yerr=std[metric],
60
+ capsize=5,
61
+ color=colors,
62
+ label=methods,
63
+ error_kw={'elinewidth': 2, 'capthick': 2},
64
+ )
65
+ if col == 0:
66
+ handles, labels = ax.get_legend_handles_labels()
67
+ ax.set_xticks([])
68
+ y_vals = mean[metric]
69
+ margin = (y_vals.max() - y_vals.min()) * 0.15
70
+ ax.set_ylim([y_vals.min() - margin, y_vals.max() + margin])
71
+ ax.set_ylabel(metric, fontsize=32)
72
+
73
+ # Legend-only panel
74
+ ax_leg = fig.add_subplot(gs[-1])
75
+ ax_leg.legend(handles, labels, fontsize=28, loc='center', frameon=False)
76
+ ax_leg.set_axis_off()
77
+
78
+ fig.tight_layout(pad=2)
79
+ os.makedirs('./figures', exist_ok=True)
80
+ fig.savefig('./figures/comparison.png', dpi=300)
81
+ fig.savefig('./figures/comparison.pdf', dpi=300)
82
+ plt.close(fig)
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Tutorial 2: Ablation bar chart (alpha-graduated, horizontal)
88
+
89
+ **Goal**: Same method with components progressively added; alpha encodes completeness.
90
+
91
+ ```python
92
+ import os
93
+ import numpy as np
94
+ import matplotlib.pyplot as plt
95
+
96
+ plt.rcParams['font.family'] = 'sans-serif'
97
+ plt.rcParams['font.sans-serif'] = ['Arial']
98
+ plt.rcParams['svg.fonttype'] = 'none'
99
+ plt.rcParams['font.size'] = 24
100
+ plt.rcParams['axes.spines.right'] = False
101
+ plt.rcParams['axes.spines.top'] = False
102
+ plt.rcParams['axes.linewidth'] = 3
103
+
104
+ configs = ['None', '+ Module A', '+ Module B', '+ Module C', 'Full']
105
+ values = np.array([0.72, 0.78, 0.81, 0.84, 0.88])
106
+ stds = np.array([0.02, 0.02, 0.01, 0.01, 0.01])
107
+
108
+ n = len(configs)
109
+ blue_rgb = (0.215686, 0.458824, 0.729412) # #3775BA
110
+ alphas = np.linspace(0.2, 1.0, n)
111
+ colors = [(blue_rgb[0], blue_rgb[1], blue_rgb[2], a) for a in alphas]
112
+
113
+ fig, ax = plt.subplots(figsize=(12, 6))
114
+ ax.barh(range(n), values, xerr=stds,
115
+ color=colors, ecolor='k', capsize=5)
116
+ ax.set_yticks(range(n))
117
+ ax.set_yticklabels(configs)
118
+ ax.set_xlim([values.min() - 0.05, values.max() + 0.03])
119
+ ax.set_xlabel('Score', fontsize=32)
120
+
121
+ fig.tight_layout(pad=2)
122
+ os.makedirs('./figures', exist_ok=True)
123
+ fig.savefig('./figures/ablation.png', dpi=300)
124
+ plt.close(fig)
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Tutorial 3: Multi-panel trend with shared legend
130
+
131
+ **Goal**: Two trend panels (e.g., train/val curves) and a legend-only third panel.
132
+
133
+ ```python
134
+ import os
135
+ import numpy as np
136
+ import matplotlib.pyplot as plt
137
+
138
+ plt.rcParams['font.family'] = 'sans-serif'
139
+ plt.rcParams['font.sans-serif'] = ['Arial']
140
+ plt.rcParams['svg.fonttype'] = 'none'
141
+ plt.rcParams['font.size'] = 15
142
+ plt.rcParams['axes.spines.right'] = False
143
+ plt.rcParams['axes.spines.top'] = False
144
+ plt.rcParams['axes.linewidth'] = 2
145
+
146
+ methods = ['Baseline', 'CSFM-Tiny', 'CSFM-Base', 'CSFM-Large']
147
+ colors = ['#7884B4', '#E4E4F0', '#E4CCD8', '#F0C0CC']
148
+ x = np.arange(0, 100, 5)
149
+
150
+ fig, axes = plt.subplots(1, 3, figsize=(18, 5))
151
+
152
+ for panel_idx, (ax, panel_name) in enumerate(zip(axes[:2], ['Training', 'Validation'])):
153
+ for method, color in zip(methods, colors):
154
+ y = 0.48 + 0.42 * (1 - np.exp(-x / 30)) + np.random.randn(len(x)) * 0.01
155
+ if method == 'Baseline':
156
+ y -= 0.03
157
+ elif method == 'CSFM-Tiny':
158
+ y += 0.00
159
+ elif method == 'CSFM-Base':
160
+ y += 0.02
161
+ elif method == 'CSFM-Large':
162
+ y += 0.03
163
+ ax.plot(x, y, color=color, lw=2.5, marker='o', markersize=6, label=method)
164
+ ax.set_title(panel_name, fontsize=18)
165
+ ax.set_xlabel('Epoch', fontsize=16)
166
+ ax.set_ylabel('Loss', fontsize=16)
167
+ if panel_idx == 0:
168
+ handles, labels = ax.get_legend_handles_labels()
169
+
170
+ # Legend-only panel
171
+ axes[2].legend(handles, labels, fontsize=14, loc='center', frameon=False)
172
+ axes[2].set_axis_off()
173
+
174
+ fig.tight_layout(pad=2)
175
+ os.makedirs('./figures', exist_ok=True)
176
+ fig.savefig('./figures/trends.png', dpi=300)
177
+ fig.savefig('./figures/trends.pdf', dpi=300)
178
+ plt.close(fig)
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Tutorial 4: Heatmap with dual colormaps (positive/negative columns)
184
+
185
+ **Goal**: Score matrix where positive = Reds, negative = Blues_r. Cell text auto-contrasted.
186
+
187
+ ```python
188
+ import os
189
+ import numpy as np
190
+ import matplotlib as mpl
191
+ import matplotlib.pyplot as plt
192
+
193
+ plt.rcParams['font.family'] = 'sans-serif'
194
+ plt.rcParams['font.sans-serif'] = ['Arial']
195
+ plt.rcParams['svg.fonttype'] = 'none'
196
+ plt.rcParams['font.size'] = 16
197
+ plt.rcParams['axes.spines.right'] = False
198
+ plt.rcParams['axes.spines.top'] = False
199
+ plt.rcParams['axes.linewidth'] = 2
200
+
201
+ # matrix: rows = methods, cols = metrics (alternating positive/negative directions)
202
+ methods = ['Method A', 'Method B', 'Method C', 'Method D']
203
+ metrics = ['Score (+)', 'Error (-)', 'F1 (+)', 'Loss (-)']
204
+ matrix = np.array([
205
+ [0.88, 0.12, 0.85, 0.20],
206
+ [0.81, 0.18, 0.78, 0.28],
207
+ [0.75, 0.25, 0.72, 0.35],
208
+ [0.70, 0.30, 0.68, 0.40],
209
+ ])
210
+
211
+ fig, ax = plt.subplots(figsize=(10, 6))
212
+ n_rows, n_cols = matrix.shape
213
+ vmin, vmax = matrix.min(0), matrix.max(0)
214
+
215
+ for j in range(n_cols):
216
+ is_positive = (j % 2 == 0)
217
+ cmap = plt.cm.Reds if is_positive else plt.cm.Blues_r
218
+ cmap = cmap.copy()
219
+ norm = mpl.colors.Normalize(
220
+ vmin=0 if is_positive else vmax[j],
221
+ vmax=vmax[j] if is_positive else 0
222
+ )
223
+ ax.imshow(matrix[:, j:j+1], cmap=cmap, norm=norm,
224
+ aspect='auto', extent=[j-0.5, j+0.5, 0, n_rows], origin='lower')
225
+
226
+ for (i, j), val in np.ndenumerate(matrix):
227
+ is_positive = (j % 2 == 0)
228
+ cmap = plt.cm.Reds if is_positive else plt.cm.Blues_r
229
+ norm = mpl.colors.Normalize(vmin=0 if is_positive else vmax[j],
230
+ vmax=vmax[j] if is_positive else 0)
231
+ r, g, b, _ = cmap(norm(val))
232
+ lum = 0.299*r + 0.587*g + 0.114*b
233
+ color = 'white' if lum < 0.5 else 'black'
234
+ ax.text(j, i + 0.5, f'{val:.2f}', ha='center', va='center',
235
+ fontsize=13, color=color)
236
+
237
+ ax.set_xlim(-0.5, n_cols - 0.5)
238
+ ax.set_xticks(np.arange(n_cols))
239
+ ax.set_xticklabels(metrics, rotation=30, ha='right', fontsize=14)
240
+ ax.tick_params(axis='x', bottom=False, top=False, length=0)
241
+ ax.set_yticks(np.arange(n_rows) + 0.5)
242
+ ax.set_yticklabels(methods, fontsize=14)
243
+ ax.set_frame_on(False)
244
+ ax.invert_yaxis()
245
+
246
+ fig.tight_layout(pad=2)
247
+ os.makedirs('./figures', exist_ok=True)
248
+ fig.savefig('./figures/heatmap.png', dpi=300)
249
+ plt.close(fig)
250
+ ```
251
+
252
+ ---
253
+
254
+ ## Related files
255
+
256
+ - [SKILL.md](../SKILL.md) — When to use this skill
257
+ - [api.md](api.md) — Reusable helper implementations
258
+ - [common-patterns.md](common-patterns.md) — Layout and encoding patterns used above
259
+ - [design-theory.md](design-theory.md) — Why these choices exist
260
+ - [chart-types.md](chart-types.md) — Radar, 3D sphere, scatter, fill_between
@@ -0,0 +1 @@
1
+ PyMuPDF>=1.24.0,<2.0.0