@brainpilot/skills 0.0.6 → 0.0.8

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 (285) hide show
  1. package/package.json +2 -2
  2. package/skills/01_Meta-Skills/academic-research-hub/SKILL.md +108 -0
  3. package/skills/01_Meta-Skills/academic-research-hub/scripts/requirements.txt +17 -0
  4. package/skills/01_Meta-Skills/academic-research-hub/scripts/research.py +781 -0
  5. package/skills/01_Meta-Skills/beautiful-log/SKILL.md +64 -0
  6. package/skills/01_Meta-Skills/beautiful-log/scripts/beautiful_log.py +274 -0
  7. package/skills/01_Meta-Skills/ethoclaw-daily-paper/SKILL.md +130 -0
  8. package/skills/01_Meta-Skills/ethoclaw-daily-paper/assets/config.template.yaml +54 -0
  9. package/skills/01_Meta-Skills/ethoclaw-daily-paper/assets/top5_digest_template.md +5 -0
  10. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/build_top5_digest.py +300 -0
  11. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/common.py +137 -0
  12. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/merge_results.py +106 -0
  13. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/run_pipeline.py +177 -0
  14. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/search_arxiv.py +162 -0
  15. package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/search_pubmed.py +202 -0
  16. package/skills/01_Meta-Skills/ethoclaw-normalize-tabular/SKILL.md +173 -0
  17. package/skills/01_Meta-Skills/ethoclaw-normalize-tabular/scripts/normalize_data.py +874 -0
  18. package/skills/01_Meta-Skills/ethoclaw-pdf-research/SKILL.md +134 -0
  19. package/skills/01_Meta-Skills/ethoclaw-pdf-research/references/confirmation-prompts.md +31 -0
  20. package/skills/01_Meta-Skills/ethoclaw-pdf-research/references/output-patterns.md +45 -0
  21. package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_markdown_deliverables.py +41 -0
  22. package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_research_log.py +84 -0
  23. package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_summary_md.py +63 -0
  24. package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/extract_pdf_bundle.py +140 -0
  25. package/skills/01_Meta-Skills/experiment-controller/SKILL.md +140 -0
  26. package/skills/01_Meta-Skills/knowledge-graph-builder/SKILL.md +366 -0
  27. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/entity_resolution.py +120 -0
  28. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/extraction_prompt_template.txt +19 -0
  29. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/graph_query.py +106 -0
  30. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/hypothesis_cli_reference.py +42 -0
  31. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/new_data_source_template.py +116 -0
  32. package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/requirements.txt +15 -0
  33. package/skills/01_Meta-Skills/method-design/SKILL.md +61 -0
  34. package/skills/01_Meta-Skills/multi-search-engine/SKILL.md +119 -0
  35. package/skills/01_Meta-Skills/research-idea/SKILL.md +65 -0
  36. package/skills/05_EEG_ERP/eeg-skill/SKILL.md +197 -0
  37. package/skills/05_EEG_ERP/meg-skill/SKILL.md +188 -0
  38. package/skills/05_EEG_ERP/meg-skill/scripts/time_frequency.py +223 -0
  39. package/skills/05_EEG_ERP/mne-eeg-tool/SKILL.md +165 -0
  40. package/skills/05_EEG_ERP/mne-eeg-tool/scripts/eeg_pipeline_reference.py +231 -0
  41. package/skills/05_EEG_ERP/seed-iv-skill/SKILL.md +184 -0
  42. package/skills/05_EEG_ERP/seed-iv-skill/scripts/classify_seed_iv.py +154 -0
  43. package/skills/05_EEG_ERP/seed-iv-skill/scripts/extract_seed_iv_features.py +190 -0
  44. package/skills/05_EEG_ERP/seed-iv-skill/scripts/validate_seed_iv.py +102 -0
  45. package/skills/05_EEG_ERP/seed-vig-skill/SKILL.md +182 -0
  46. package/skills/05_EEG_ERP/seed-vig-skill/scripts/classify_seed_vig.py +165 -0
  47. package/skills/05_EEG_ERP/seed-vig-skill/scripts/extract_seed_vig_features.py +185 -0
  48. package/skills/05_EEG_ERP/seed-vig-skill/scripts/validate_seed_vig.py +88 -0
  49. package/skills/06_fMRI_Neuroimaging/abcd-skill/SKILL.md +308 -0
  50. package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/abcd_qc_summary.py +449 -0
  51. package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/extract_abcd_phenotype.py +292 -0
  52. package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/reorganize_abcd.py +387 -0
  53. package/skills/06_fMRI_Neuroimaging/abide-skill/SKILL.md +302 -0
  54. package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/abide_qc_summary.py +317 -0
  55. package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/extract_abide_phenotype.py +267 -0
  56. package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/reorganize_abide.py +387 -0
  57. package/skills/06_fMRI_Neuroimaging/adhd200-skill/SKILL.md +244 -0
  58. package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/adhd200_qc_summary.py +98 -0
  59. package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/extract_adhd200_phenotype.py +134 -0
  60. package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/reorganize_adhd200.py +206 -0
  61. package/skills/06_fMRI_Neuroimaging/adni-skill/SKILL.md +358 -0
  62. package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/generate_adni_task_files.py +1305 -0
  63. package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/generate_vqa_from_tasks.py +766 -0
  64. package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/reorganize_adni.py +491 -0
  65. package/skills/06_fMRI_Neuroimaging/aibl-skill/SKILL.md +295 -0
  66. package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/aibl_qc_summary.py +260 -0
  67. package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/extract_aibl_phenotype.py +365 -0
  68. package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/reorganize_aibl.py +394 -0
  69. package/skills/06_fMRI_Neuroimaging/aomic-skill/SKILL.md +292 -0
  70. package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/aomic_qc_summary.py +258 -0
  71. package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/extract_aomic_phenotype.py +284 -0
  72. package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/reorganize_aomic.py +322 -0
  73. package/skills/06_fMRI_Neuroimaging/asl-skill/SKILL.md +168 -0
  74. package/skills/06_fMRI_Neuroimaging/asl-skill/scripts/compute_cbf.py +224 -0
  75. package/skills/06_fMRI_Neuroimaging/bids-organizer/SKILL.md +241 -0
  76. package/skills/06_fMRI_Neuroimaging/bold5000-skill/SKILL.md +186 -0
  77. package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/bold5000_qc_summary.py +96 -0
  78. package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/extract_bold5000_stimulus.py +125 -0
  79. package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/reorganize_bold5000.py +102 -0
  80. package/skills/06_fMRI_Neuroimaging/camcan-skill/SKILL.md +213 -0
  81. package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/camcan_qc_summary.py +131 -0
  82. package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/extract_camcan_phenotype.py +145 -0
  83. package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/validate_camcan.py +141 -0
  84. package/skills/06_fMRI_Neuroimaging/cobre-skill/SKILL.md +201 -0
  85. package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/cobre_qc_summary.py +95 -0
  86. package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/extract_cobre_phenotype.py +104 -0
  87. package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/reorganize_cobre.py +140 -0
  88. package/skills/06_fMRI_Neuroimaging/conn-tool/SKILL.md +180 -0
  89. package/skills/06_fMRI_Neuroimaging/dcm2nii/SKILL.md +189 -0
  90. package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/SKILL.md +183 -0
  91. package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/dmt_har_med_qc_summary.py +96 -0
  92. package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/extract_dmt_har_med_phenotype.py +121 -0
  93. package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/reorganize_dmt_har_med.py +125 -0
  94. package/skills/06_fMRI_Neuroimaging/dwi-skill/SKILL.md +359 -0
  95. package/skills/06_fMRI_Neuroimaging/fmri-skill/SKILL.md +371 -0
  96. package/skills/06_fMRI_Neuroimaging/fmriprep-tool/SKILL.md +228 -0
  97. package/skills/06_fMRI_Neuroimaging/freesurfer-tool/SKILL.md +286 -0
  98. package/skills/06_fMRI_Neuroimaging/freesurfer-tool/scripts/freesurfer_processor.py +145 -0
  99. package/skills/06_fMRI_Neuroimaging/fsl-tool/SKILL.md +208 -0
  100. package/skills/06_fMRI_Neuroimaging/hbn-skill/SKILL.md +271 -0
  101. package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/extract_hbn_phenotype.py +107 -0
  102. package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/hbn_qc_summary.py +96 -0
  103. package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/reorganize_hbn.py +150 -0
  104. package/skills/06_fMRI_Neuroimaging/hcpa-skill/SKILL.md +210 -0
  105. package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/extract_hcpa_phenotype.py +146 -0
  106. package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/hcpa_qc_summary.py +120 -0
  107. package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/reorganize_hcpa.py +155 -0
  108. package/skills/06_fMRI_Neuroimaging/hcpd-skill/SKILL.md +210 -0
  109. package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/extract_hcpd_phenotype.py +148 -0
  110. package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/hcpd_qc_summary.py +125 -0
  111. package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/reorganize_hcpd.py +146 -0
  112. package/skills/06_fMRI_Neuroimaging/hcpep-skill/SKILL.md +215 -0
  113. package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/extract_hcpep_phenotype.py +157 -0
  114. package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/hcpep_qc_summary.py +143 -0
  115. package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/reorganize_hcpep.py +146 -0
  116. package/skills/06_fMRI_Neuroimaging/hcppipeline-tool/SKILL.md +217 -0
  117. package/skills/06_fMRI_Neuroimaging/hcpya-skill/SKILL.md +214 -0
  118. package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/extract_hcpya_phenotype.py +190 -0
  119. package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/hcpya_qc_summary.py +152 -0
  120. package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/reorganize_hcpya.py +203 -0
  121. package/skills/06_fMRI_Neuroimaging/ixi-skill/SKILL.md +198 -0
  122. package/skills/06_fMRI_Neuroimaging/ixi-skill/scripts/ixi_qc_summary.py +137 -0
  123. package/skills/06_fMRI_Neuroimaging/ixi-skill/scripts/reorganize_ixi.py +190 -0
  124. package/skills/06_fMRI_Neuroimaging/mnd-skill/SKILL.md +191 -0
  125. package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/extract_mnd_phenotype.py +143 -0
  126. package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/mnd_qc_summary.py +120 -0
  127. package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/validate_mnd.py +107 -0
  128. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/SKILL.md +203 -0
  129. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/analyze_lesions.py +119 -0
  130. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/longitudinal_lesion.py +148 -0
  131. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/mschallenge_qc_summary.py +132 -0
  132. package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/validate_mschallenge.py +116 -0
  133. package/skills/06_fMRI_Neuroimaging/nibabel-skill/SKILL.md +184 -0
  134. package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/atlas_coordinate_reference.py +61 -0
  135. package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/freesurfer_io_reference.py +34 -0
  136. package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/nifti_inspection_reference.py +35 -0
  137. package/skills/06_fMRI_Neuroimaging/nifd-skill/SKILL.md +205 -0
  138. package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/extract_nifd_phenotype.py +132 -0
  139. package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/nifd_qc_summary.py +111 -0
  140. package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/validate_nifd.py +111 -0
  141. package/skills/06_fMRI_Neuroimaging/nii2dcm/SKILL.md +143 -0
  142. package/skills/06_fMRI_Neuroimaging/nilearn-tool/SKILL.md +266 -0
  143. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/connectome_reference.py +65 -0
  144. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/denoise_timeseries_reference.py +58 -0
  145. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/hierarchical_parcellation_reference.py +53 -0
  146. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/kmeans_parcellation_reference.py +53 -0
  147. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/preprocess_bold_reference.py +76 -0
  148. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/rest_dictlearning_reference.py +56 -0
  149. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/rest_ica_reference.py +59 -0
  150. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/second_level_glm_reference.py +58 -0
  151. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/spacenet_classifier_reference.py +59 -0
  152. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/svm_classifier_reference.py +60 -0
  153. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/task_glm_reference.py +63 -0
  154. package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/zalff_summary_reference.py +109 -0
  155. package/skills/06_fMRI_Neuroimaging/nsd-skill/SKILL.md +210 -0
  156. package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/extract_nsd_stimulus.py +171 -0
  157. package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/nsd_qc_summary.py +142 -0
  158. package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/validate_nsd.py +142 -0
  159. package/skills/06_fMRI_Neuroimaging/oasis-skill/SKILL.md +205 -0
  160. package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/extract_oasis_phenotype.py +126 -0
  161. package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/oasis_qc_summary.py +115 -0
  162. package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/validate_oasis.py +119 -0
  163. package/skills/06_fMRI_Neuroimaging/pet-skill/SKILL.md +173 -0
  164. package/skills/06_fMRI_Neuroimaging/pet-skill/scripts/compute_suvr.py +202 -0
  165. package/skills/06_fMRI_Neuroimaging/pnc-skill/SKILL.md +206 -0
  166. package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/extract_pnc_phenotype.py +136 -0
  167. package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/pnc_qc_summary.py +116 -0
  168. package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/validate_pnc.py +120 -0
  169. package/skills/06_fMRI_Neuroimaging/ppmi-skill/SKILL.md +209 -0
  170. package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/extract_ppmi_phenotype.py +138 -0
  171. package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/ppmi_qc_summary.py +111 -0
  172. package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/validate_ppmi.py +117 -0
  173. package/skills/06_fMRI_Neuroimaging/qsiprep-tool/SKILL.md +320 -0
  174. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/SKILL.md +215 -0
  175. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/extract_rest_mdd_phenotype.py +132 -0
  176. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/harmonize_sites.py +152 -0
  177. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/rest_mdd_qc_summary.py +124 -0
  178. package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/validate_rest_mdd.py +103 -0
  179. package/skills/06_fMRI_Neuroimaging/smri-skill/SKILL.md +302 -0
  180. package/skills/06_fMRI_Neuroimaging/tcp-skill/SKILL.md +204 -0
  181. package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/extract_tcp_phenotype.py +139 -0
  182. package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/tcp_qc_summary.py +111 -0
  183. package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/validate_tcp.py +99 -0
  184. package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/SKILL.md +217 -0
  185. package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/extract_ucla_cnp_phenotype.py +145 -0
  186. package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/ucla_cnp_qc_summary.py +111 -0
  187. package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/validate_ucla_cnp.py +113 -0
  188. package/skills/06_fMRI_Neuroimaging/ukb-skill/SKILL.md +310 -0
  189. package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/build_ukb_survival.py +210 -0
  190. package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/extract_ukb_cases.py +308 -0
  191. package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/extract_ukb_phenotype.py +232 -0
  192. package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/ukb_qc_summary.py +158 -0
  193. package/skills/06_fMRI_Neuroimaging/wmh-segmentation/SKILL.md +133 -0
  194. package/skills/07_Computational_Modeling/detrending/SKILL.md +118 -0
  195. package/skills/07_Computational_Modeling/dictlearning/SKILL.md +122 -0
  196. package/skills/07_Computational_Modeling/filtering/SKILL.md +121 -0
  197. package/skills/07_Computational_Modeling/glm/SKILL.md +153 -0
  198. package/skills/07_Computational_Modeling/hierarchical/SKILL.md +121 -0
  199. package/skills/07_Computational_Modeling/ica/SKILL.md +122 -0
  200. package/skills/07_Computational_Modeling/kmeans/SKILL.md +119 -0
  201. package/skills/07_Computational_Modeling/run_models/SKILL.md +427 -0
  202. package/skills/07_Computational_Modeling/spacenet/SKILL.md +122 -0
  203. package/skills/07_Computational_Modeling/svm/SKILL.md +120 -0
  204. package/skills/08_Computational_Neuroscience/brain_gnn/SKILL.md +183 -0
  205. package/skills/08_Computational_Neuroscience/dipy-tool/SKILL.md +239 -0
  206. package/skills/08_Computational_Neuroscience/dipy-tool/scripts/dti_metrics_reference.py +70 -0
  207. package/skills/08_Computational_Neuroscience/dipy-tool/scripts/load_and_mask_reference.py +76 -0
  208. package/skills/08_Computational_Neuroscience/dipy-tool/scripts/roi_stats_reference.py +59 -0
  209. package/skills/08_Computational_Neuroscience/fm_app/SKILL.md +195 -0
  210. package/skills/08_Computational_Neuroscience/neurostorm/SKILL.md +151 -0
  211. package/skills/13_Visualization/brain-visualization/SKILL.md +191 -0
  212. package/skills/13_Visualization/brain-visualization/scripts/connectome_reference.py +108 -0
  213. package/skills/13_Visualization/brain-visualization/scripts/freesurfer_ply_reference.py +54 -0
  214. package/skills/13_Visualization/brain-visualization/scripts/zalff_summary_reference.py +116 -0
  215. package/skills/13_Visualization/ethoclaw-paper-figure-layout/SKILL.md +78 -0
  216. package/skills/13_Visualization/ethoclaw-paper-figure-layout/assets/naturecomm_figures.tex +74 -0
  217. package/skills/13_Visualization/ethoclaw-paper-figure-layout/scripts/layout_results_foldered.py +579 -0
  218. package/skills/14_Writing/overleaf-skill/SKILL.md +184 -0
  219. package/skills/14_Writing/overleaf-skill/scripts/install.sh +30 -0
  220. package/skills/14_Writing/paper-writing/SKILL.md +146 -0
  221. package/skills/14_Writing/paper-writing/scripts/data_statement_templates.py +164 -0
  222. package/skills/14_Writing/paper-writing/scripts/figure_templates.py +315 -0
  223. package/skills/14_Writing/paper-writing/scripts/nature_figure_style.py +214 -0
  224. package/skills/14_Writing/paper-writing/scripts/section_phrasebank.py +246 -0
  225. package/skills/16_Animal_Behavior/deeplabcut/SKILL.md +154 -0
  226. package/skills/16_Animal_Behavior/deeplabcut/references/3d-pose.md +89 -0
  227. package/skills/16_Animal_Behavior/deeplabcut/references/maDLC.md +123 -0
  228. package/skills/16_Animal_Behavior/deeplabcut/references/modelzoo.md +98 -0
  229. package/skills/16_Animal_Behavior/deeplabcut/references/standard-pipeline.md +165 -0
  230. package/skills/16_Animal_Behavior/deeplabcut/references/utilities.md +146 -0
  231. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/SKILL.md +274 -0
  232. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/report_template_en.html +112 -0
  233. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/report_template_en.md +21 -0
  234. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/cluster-section.md +5 -0
  235. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/heatmap-section.md +5 -0
  236. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/integrated-interpretation.md +3 -0
  237. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/overview.md +3 -0
  238. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/project-summary.md +3 -0
  239. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/radar-section.md +5 -0
  240. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/raw-trajectory.md +3 -0
  241. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/sample-check.md +3 -0
  242. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/single-subject-section.md +3 -0
  243. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/stats-section.md +5 -0
  244. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/epm.md +52 -0
  245. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/fst.md +37 -0
  246. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/nor.md +39 -0
  247. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/oft.md +43 -0
  248. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/tcst.md +45 -0
  249. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/tst.md +36 -0
  250. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/input-types.md +59 -0
  251. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/interpretation-guardrails.md +45 -0
  252. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/metadata-schema.md +57 -0
  253. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/report-sections.md +86 -0
  254. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/section-selection-rules.md +169 -0
  255. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/build_report_manifest.py +27 -0
  256. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/render_report.py +34 -0
  257. package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/report_utils.py +1121 -0
  258. package/skills/16_Animal_Behavior/ethoclaw-animal-grounding/SKILL.md +390 -0
  259. package/skills/16_Animal_Behavior/ethoclaw-animal-grounding/reference_code.py +98 -0
  260. package/skills/16_Animal_Behavior/ethoclaw-animal-pose-estimation/SKILL.md +336 -0
  261. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/README.md +21 -0
  262. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/SKILL.md +41 -0
  263. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/batch_kinematic_generator.py +663 -0
  264. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/config.json +19 -0
  265. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/generate_kinematic_parameter.py +401 -0
  266. package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/kinematic_generator.py +265 -0
  267. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/SKILL.md +72 -0
  268. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/references/config.example.toml +56 -0
  269. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/scripts/cluster_all_params.py +232 -0
  270. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/scripts/cluster_all_params_from_config.py +236 -0
  271. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/SKILL.md +68 -0
  272. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/references/notes.md +5 -0
  273. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/scripts/plot_h5_radar.py +513 -0
  274. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/SKILL.md +52 -0
  275. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/config.toml +81 -0
  276. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/references/stats-rule.md +18 -0
  277. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_inspect.py +79 -0
  278. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_violin_batch.py +624 -0
  279. package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_violin_stats.py +438 -0
  280. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/SKILL.md +280 -0
  281. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/core_scripts/heatmap_trajectory.py +790 -0
  282. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/core_scripts/heatmap_velocity.py +855 -0
  283. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/reference_data/reference_2d.csv +101 -0
  284. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/reference_data/reference_2d.h5 +0 -0
  285. package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/reference_data/reference_data_readme.md +126 -0
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env python3
2
+ """Inspect an HDF5 (.h5/.hdf5) file and print a tree of groups/datasets.
3
+
4
+ Usage:
5
+ python h5_inspect.py path/to/file.h5
6
+
7
+ Notes:
8
+ - Requires: h5py
9
+ - Prints dataset shapes/dtypes and a preview for small 1D datasets.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import sys
15
+ from typing import Any
16
+
17
+
18
+ def _fmt(obj: Any) -> str:
19
+ try:
20
+ return str(obj)
21
+ except Exception:
22
+ return repr(obj)
23
+
24
+
25
+ def _walk(h5, path: str = "/", indent: int = 0) -> None:
26
+ import h5py # local import for nicer dependency error
27
+
28
+ item = h5[path]
29
+ pad = " " * indent
30
+
31
+ if isinstance(item, h5py.Dataset):
32
+ shape = item.shape
33
+ dtype = item.dtype
34
+ print(f"{pad}{path} [Dataset] shape={shape} dtype={dtype}")
35
+ # small preview
36
+ try:
37
+ if shape is not None and len(shape) == 1 and shape[0] <= 10:
38
+ data = item[...]
39
+ print(f"{pad} preview: {_fmt(data)}")
40
+ except Exception:
41
+ pass
42
+ return
43
+
44
+ print(f"{pad}{path} [Group]")
45
+ for key in item.keys():
46
+ child_path = (path.rstrip("/") + "/" + key) if path != "/" else "/" + key
47
+ _walk(h5, child_path, indent + 1)
48
+
49
+
50
+ def main(argv: list[str]) -> int:
51
+ if len(argv) != 2 or argv[1] in {"-h", "--help"}:
52
+ print(__doc__.strip())
53
+ return 0
54
+
55
+ h5_path = argv[1]
56
+
57
+ try:
58
+ import h5py # noqa: F401
59
+ except Exception as e:
60
+ print(
61
+ "ERROR: missing dependency 'h5py'.\n"
62
+ "Install options:\n"
63
+ " - Ubuntu/Debian: apt install python3-pip python3-venv && pip install h5py\n"
64
+ " - Or use a dedicated venv/conda env.\n"
65
+ f"Details: {e}",
66
+ file=sys.stderr,
67
+ )
68
+ return 2
69
+
70
+ import h5py
71
+
72
+ with h5py.File(h5_path, "r") as h5:
73
+ _walk(h5, "/", 0)
74
+
75
+ return 0
76
+
77
+
78
+ if __name__ == "__main__":
79
+ raise SystemExit(main(sys.argv))
@@ -0,0 +1,624 @@
1
+ #!/usr/bin/env python3
2
+ """Batch-generate violin plots for *all parameters* from a folder of .h5 files.
3
+
4
+ Designed to match `h5-multiparameter-violin-stats` rules + config, but works in batch mode:
5
+ - Reads every *.h5/*.hdf5 under --input-dir (non-recursive by default)
6
+ - Infers group from filename: rec-<id>-<group>-<timestamp>.h5
7
+ - Extracts:
8
+ - AdvancedParameters/* (scalar per file)
9
+ - KinematicParameter/* (per-file mean across frames)
10
+ - Writes one violin plot per parameter to --out-dir.
11
+
12
+ Config:
13
+ - Default config: ../config.toml (in this skill folder)
14
+ - Supports Python 3.11+ tomllib, and Python<3.11 via `toml` package.
15
+
16
+ Usage:
17
+ python h5_violin_batch.py --input-dir /path/to/project/random_project
18
+
19
+ Windows example (conda python):
20
+ D:\\Anaconda3\\python.exe h5_violin_batch.py \\
21
+ --input-dir C:\\Users\\ASUS\\Desktop\\2Dxy_test_project\\random_project
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ import math
28
+ import os
29
+ import re
30
+ from dataclasses import dataclass
31
+ from pathlib import Path
32
+ from typing import Dict, List, Literal, Sequence, Tuple
33
+
34
+
35
+ Method = Literal["auto", "ttest", "mannwhitney", "anova", "kruskal"]
36
+
37
+ FNAME_RE = re.compile(r"rec-(?P<rec>\d+)-(?P<group>[A-Za-z]+)-(?P<ts>\d+)\.h5$", re.IGNORECASE)
38
+
39
+
40
+ def _die(msg: str, code: int = 2) -> "NoReturn":
41
+ raise SystemExit(f"ERROR: {msg}")
42
+
43
+
44
+ def _deep_get(d: dict, keys: list[str], default=None):
45
+ cur = d
46
+ for k in keys:
47
+ if not isinstance(cur, dict) or k not in cur:
48
+ return default
49
+ cur = cur[k]
50
+ return cur
51
+
52
+
53
+ def _load_toml(path: str) -> dict:
54
+ # Prefer tomllib (py3.11+). For older Python, avoid extra deps by parsing with stdlib.
55
+ try:
56
+ import tomllib # type: ignore
57
+
58
+ with open(path, "rb") as f:
59
+ return tomllib.load(f)
60
+ except Exception:
61
+ pass
62
+
63
+ # Minimal TOML parser for this skill's simple config (sections + key = value).
64
+ # Supports: strings, ints, floats, booleans.
65
+ cfg: dict = {}
66
+ cur: dict = cfg
67
+ section: list[str] = []
68
+
69
+ def set_in(d: dict, keys: list[str], k: str, v):
70
+ node = d
71
+ for kk in keys:
72
+ node = node.setdefault(kk, {})
73
+ node[k] = v
74
+
75
+ def parse_value(s: str):
76
+ s = s.strip()
77
+ if not s:
78
+ return ""
79
+ if s.lower() in {"true", "false"}:
80
+ return s.lower() == "true"
81
+ if (s.startswith('"') and s.endswith('"')) or (s.startswith("'") and s.endswith("'")):
82
+ return s[1:-1]
83
+ # number
84
+ try:
85
+ if "." in s or "e" in s.lower():
86
+ return float(s)
87
+ return int(s)
88
+ except Exception:
89
+ return s
90
+
91
+ with open(path, "r", encoding="utf-8") as f:
92
+ for raw in f:
93
+ line = raw.strip()
94
+ if not line or line.startswith("#"):
95
+ continue
96
+ if line.startswith("[") and line.endswith("]"):
97
+ sec = line[1:-1].strip()
98
+ section = [x.strip() for x in sec.split(".") if x.strip()]
99
+ continue
100
+ if "=" not in line:
101
+ continue
102
+ k, v = line.split("=", 1)
103
+ k = k.strip()
104
+ # strip inline comment
105
+ v = v.split("#", 1)[0].strip()
106
+ set_in(cfg, section, k, parse_value(v))
107
+
108
+ return cfg
109
+
110
+
111
+ def _default_config_path() -> str:
112
+ here = os.path.dirname(os.path.abspath(__file__))
113
+ return os.path.join(os.path.dirname(here), "config.toml")
114
+
115
+
116
+ def _parse_group(p: Path) -> str:
117
+ m = FNAME_RE.search(p.name)
118
+ if not m:
119
+ return "unknown"
120
+ return m.group("group")
121
+
122
+
123
+ @dataclass
124
+ class TestResult:
125
+ test: str
126
+ p_value: float
127
+ statistic: float | None = None
128
+
129
+
130
+ def _holm_bonferroni(p_values: Sequence[float]) -> list[float]:
131
+ m = len(p_values)
132
+ order = sorted(range(m), key=lambda i: p_values[i])
133
+ adj = [0.0] * m
134
+ prev = 0.0
135
+ for k, i in enumerate(order):
136
+ raw = p_values[i]
137
+ val = (m - k) * raw
138
+ val = max(val, prev)
139
+ prev = val
140
+ adj[i] = min(val, 1.0)
141
+ return adj
142
+
143
+
144
+ def _choose_method(n_groups: int, method: Method, cfg: dict) -> str:
145
+ if method != "auto":
146
+ return method
147
+ m2 = _deep_get(cfg, ["stats", "default_2_groups"], "ttest")
148
+ m3 = _deep_get(cfg, ["stats", "default_3plus_groups"], "kruskal")
149
+ return str(m2) if n_groups == 2 else str(m3)
150
+
151
+
152
+ def _run_overall(groups: List[str], values: List[float], method: str) -> TestResult:
153
+ import numpy as np
154
+ from scipy import stats
155
+
156
+ uniq = sorted(set(groups))
157
+ arrays = [np.asarray([v for g, v in zip(groups, values) if g == u], dtype=float) for u in uniq]
158
+
159
+ if len(uniq) == 2:
160
+ a, b = arrays
161
+ if method == "ttest":
162
+ stat, p = stats.ttest_ind(a, b, equal_var=False, nan_policy="omit")
163
+ return TestResult("Welch t-test", float(p), None if stat is None else float(stat))
164
+ if method == "mannwhitney":
165
+ stat, p = stats.mannwhitneyu(a, b, alternative="two-sided")
166
+ return TestResult("Mann–Whitney U", float(p), float(stat))
167
+ _die(f"method '{method}' incompatible with 2 groups")
168
+
169
+ if method == "anova":
170
+ stat, p = stats.f_oneway(*arrays)
171
+ return TestResult("One-way ANOVA", float(p), float(stat))
172
+ if method == "kruskal":
173
+ stat, p = stats.kruskal(*arrays)
174
+ return TestResult("Kruskal–Wallis", float(p), float(stat))
175
+
176
+ _die(f"method '{method}' incompatible with {len(uniq)} groups")
177
+
178
+
179
+ def _run_pairwise(groups: List[str], values: List[float], base_method: str, correction: str) -> List[dict]:
180
+ import numpy as np
181
+ from scipy import stats
182
+
183
+ uniq = sorted(set(groups))
184
+ by = {u: np.asarray([v for g, v in zip(groups, values) if g == u], dtype=float) for u in uniq}
185
+
186
+ rows = []
187
+ pvals = []
188
+ for i in range(len(uniq)):
189
+ for j in range(i + 1, len(uniq)):
190
+ a_name, b_name = uniq[i], uniq[j]
191
+ a, b = by[a_name], by[b_name]
192
+ if base_method in {"anova", "ttest"}:
193
+ stat, p = stats.ttest_ind(a, b, equal_var=False, nan_policy="omit")
194
+ test = "Welch t-test (pairwise)"
195
+ statv = None if stat is None else float(stat)
196
+ else:
197
+ stat, p = stats.mannwhitneyu(a, b, alternative="two-sided")
198
+ test = "Mann–Whitney U (pairwise)"
199
+ statv = float(stat)
200
+ pvals.append(float(p))
201
+ rows.append({"a": a_name, "b": b_name, "test": test, "p": float(p), "stat": statv})
202
+
203
+ corr = (correction or "holm").lower()
204
+ if corr == "holm":
205
+ adj = _holm_bonferroni(pvals)
206
+ for row, ap in zip(rows, adj):
207
+ row["p_adj"] = float(ap)
208
+ row["p_adj_method"] = "holm"
209
+ elif corr == "bonferroni":
210
+ m = len(pvals)
211
+ for row in rows:
212
+ row["p_adj"] = min(float(row["p"]) * m, 1.0)
213
+ row["p_adj_method"] = "bonferroni"
214
+ else:
215
+ _die(f"unsupported pairwise_correction: {correction}")
216
+
217
+ return rows
218
+
219
+
220
+ def _mean_sem(arr) -> Tuple[float, float]:
221
+ import numpy as np
222
+
223
+ x = np.asarray(arr, dtype=float)
224
+ x = x[np.isfinite(x)]
225
+ if x.size == 0:
226
+ return (math.nan, math.nan)
227
+ mean = float(np.mean(x))
228
+ sem = float(np.std(x, ddof=1) / math.sqrt(x.size)) if x.size > 1 else 0.0
229
+ return mean, sem
230
+
231
+
232
+ def _p_to_stars(p: float) -> str:
233
+ if p <= 1e-4:
234
+ return "****"
235
+ if p <= 1e-3:
236
+ return "***"
237
+ if p <= 1e-2:
238
+ return "**"
239
+ if p <= 5e-2:
240
+ return "*"
241
+ return "ns"
242
+
243
+
244
+ def _add_sig_brackets(
245
+ ax,
246
+ order: list[str],
247
+ d,
248
+ y_col: str,
249
+ comparisons: list[dict],
250
+ cfg: dict,
251
+ alpha: float,
252
+ ) -> None:
253
+ """Add pairwise significance brackets to an existing Axes.
254
+
255
+ comparisons: list of dicts with keys: a, b, p (and optional p_adj).
256
+ """
257
+
258
+ import numpy as np
259
+
260
+ if not comparisons:
261
+ return
262
+
263
+ show = bool(_deep_get(cfg, ["annotate", "show"], True))
264
+ if not show:
265
+ return
266
+
267
+ use_adj = bool(_deep_get(cfg, ["annotate", "use_p_adj"], True))
268
+ show_ns = bool(_deep_get(cfg, ["annotate", "show_ns"], False))
269
+ only_sig = bool(_deep_get(cfg, ["annotate", "only_sig"], True))
270
+ max_pairs = int(_deep_get(cfg, ["annotate", "max_pairs"], 10))
271
+ fmt = str(_deep_get(cfg, ["annotate", "label_format"], "stars+p")).lower()
272
+
273
+ # choose which p to display
274
+ def pick_p(row: dict) -> float:
275
+ if use_adj and ("p_adj" in row) and (row["p_adj"] is not None):
276
+ return float(row["p_adj"])
277
+ return float(row["p"])
278
+
279
+ rows = []
280
+ for r in comparisons:
281
+ p = pick_p(r)
282
+ if only_sig and p > alpha:
283
+ continue
284
+ if (not show_ns) and _p_to_stars(p) == "ns":
285
+ continue
286
+ rows.append((r["a"], r["b"], p))
287
+
288
+ if not rows:
289
+ return
290
+
291
+ rows = rows[:max_pairs]
292
+
293
+ # map group -> x position
294
+ x_of = {g: i for i, g in enumerate(order)}
295
+
296
+ # compute baseline height
297
+ y = d[y_col].astype(float).to_numpy()
298
+ y = y[np.isfinite(y)]
299
+ if y.size == 0:
300
+ return
301
+ y_min = float(np.min(y))
302
+ y_max = float(np.max(y))
303
+ yr = y_max - y_min
304
+ if yr <= 0:
305
+ yr = 1.0
306
+
307
+ base = y_max + 0.05 * yr
308
+ step = float(_deep_get(cfg, ["annotate", "step"], 0.08)) * yr
309
+ h = float(_deep_get(cfg, ["annotate", "bracket_height"], 0.02)) * yr
310
+
311
+ for k, (a, b, p) in enumerate(rows):
312
+ if a not in x_of or b not in x_of:
313
+ continue
314
+ x1, x2 = x_of[a], x_of[b]
315
+ if x1 == x2:
316
+ continue
317
+ if x1 > x2:
318
+ x1, x2 = x2, x1
319
+
320
+ yk = base + k * step
321
+
322
+ # bracket
323
+ ax.plot([x1, x1, x2, x2], [yk, yk + h, yk + h, yk], lw=1.2, c="black")
324
+
325
+ stars = _p_to_stars(p)
326
+ ptxt = f"p={p:.3g}"
327
+ if fmt == "stars":
328
+ label = stars
329
+ elif fmt == "p":
330
+ label = ptxt
331
+ else:
332
+ label = f"{stars}\n{ptxt}" if stars != "ns" else ptxt
333
+
334
+ ax.text(
335
+ (x1 + x2) / 2,
336
+ yk + h,
337
+ label,
338
+ ha="center",
339
+ va="bottom",
340
+ fontsize=float(_deep_get(cfg, ["annotate", "fontsize"], 9)),
341
+ )
342
+
343
+ # make room on top
344
+ ax.set_ylim(top=base + (len(rows) + 1) * step)
345
+
346
+
347
+ def _make_violin(
348
+ df,
349
+ param: str,
350
+ out_path: Path,
351
+ cfg: dict,
352
+ title: str,
353
+ comparisons: list[dict] | None = None,
354
+ alpha: float = 0.05,
355
+ ) -> None:
356
+ import numpy as np
357
+ import matplotlib
358
+
359
+ matplotlib.use("Agg")
360
+ import matplotlib.pyplot as plt
361
+ import seaborn as sns
362
+
363
+ style = _deep_get(cfg, ["plot", "style"], "whitegrid")
364
+ context = _deep_get(cfg, ["plot", "context"], "notebook")
365
+ sns.set_theme(style=style, context=context)
366
+
367
+ palette = _deep_get(cfg, ["plot", "palette"], "Set2")
368
+
369
+ fig_w = float(_deep_get(cfg, ["plot", "fig_width"], 10))
370
+ fig_h = float(_deep_get(cfg, ["plot", "fig_height"], 5))
371
+ cut = float(_deep_get(cfg, ["plot", "cut"], 0))
372
+
373
+ summary = str(_deep_get(cfg, ["plot", "summary"], "mean_sem")).lower()
374
+ inner = None
375
+ if summary in {"box", "quartile"}:
376
+ inner = "box" if summary == "box" else "quartile"
377
+
378
+ d = df[["group", param]].dropna().copy()
379
+ d = d[np.isfinite(d[param].astype(float))]
380
+
381
+ # keep a stable group order (alphabetical) for consistent bracket placement
382
+ order = sorted(d["group"].astype(str).unique().tolist())
383
+
384
+ plt.figure(figsize=(fig_w, fig_h))
385
+ ax = sns.violinplot(
386
+ data=d,
387
+ x="group",
388
+ y=param,
389
+ inner=inner,
390
+ cut=cut,
391
+ palette=palette,
392
+ order=order,
393
+ )
394
+
395
+ if bool(_deep_get(cfg, ["plot", "show_points"], True)):
396
+ sns.stripplot(
397
+ data=d,
398
+ x="group",
399
+ y=param,
400
+ color=_deep_get(cfg, ["plot", "point_color"], "black"),
401
+ size=float(_deep_get(cfg, ["plot", "point_size"], 3)),
402
+ alpha=float(_deep_get(cfg, ["plot", "point_alpha"], 0.4)),
403
+ jitter=float(_deep_get(cfg, ["plot", "point_jitter"], 0.2)),
404
+ order=order,
405
+ )
406
+
407
+ if summary == "mean_sem":
408
+ uniq = order
409
+ xs = np.arange(len(uniq))
410
+ col = _deep_get(cfg, ["plot", "summary_color"], "black")
411
+ cap = float(_deep_get(cfg, ["plot", "summary_capsize"], 4))
412
+ lw = float(_deep_get(cfg, ["plot", "summary_linewidth"], 1.5))
413
+
414
+ centers = []
415
+ errs = []
416
+ for u in uniq:
417
+ arr = d.loc[d["group"].astype(str) == u, param].astype(float).to_numpy()
418
+ m, sem = _mean_sem(arr)
419
+ centers.append(m)
420
+ errs.append(sem)
421
+
422
+ ax.errorbar(xs, centers, yerr=errs, fmt="o", color=col, capsize=cap, lw=lw, ms=4, zorder=10)
423
+
424
+ # significance brackets (pairwise) — default uses adjusted p if available
425
+ if comparisons:
426
+ _add_sig_brackets(ax, order=order, d=d, y_col=param, comparisons=comparisons, cfg=cfg, alpha=alpha)
427
+
428
+ ax.set_xlabel("Group")
429
+ ax.set_ylabel(param)
430
+ ax.set_title(title)
431
+
432
+ out_path.parent.mkdir(parents=True, exist_ok=True)
433
+ dpi = int(_deep_get(cfg, ["dump", "png_dpi"], 200))
434
+ save_pdf = bool(_deep_get(cfg, ["dump", "save_pdf"], False))
435
+
436
+ plt.tight_layout()
437
+
438
+ # Always save PNG
439
+ png_path = out_path
440
+ if png_path.suffix.lower() != ".png":
441
+ png_path = png_path.with_suffix(".png")
442
+ plt.savefig(str(png_path), dpi=dpi)
443
+
444
+ # Optionally save PDF alongside
445
+ if save_pdf:
446
+ pdf_path = png_path.with_suffix(".pdf")
447
+ plt.savefig(str(pdf_path))
448
+
449
+ plt.close()
450
+
451
+
452
+ def _read_one_file(p: Path) -> Dict[str, float]:
453
+ import numpy as np
454
+ import h5py
455
+
456
+ row: Dict[str, float] = {}
457
+ with h5py.File(str(p), "r") as f:
458
+ # AdvancedParameters
459
+ if "AdvancedParameters" in f and "ParameterName" in f["AdvancedParameters"]:
460
+ names = [x.decode() if isinstance(x, (bytes, np.bytes_)) else str(x) for x in f["AdvancedParameters/ParameterName"][()]]
461
+ vals = np.asarray(f["AdvancedParameters/ParameterData"][()], dtype=float)
462
+ for n, v in zip(names, vals):
463
+ row[str(n)] = float(v)
464
+
465
+ # KinematicParameter means
466
+ if "KinematicParameter" in f and "ParameterName" in f["KinematicParameter"]:
467
+ names = [x.decode() if isinstance(x, (bytes, np.bytes_)) else str(x) for x in f["KinematicParameter/ParameterName"][()]]
468
+ data = np.asarray(f["KinematicParameter/ParameterData"], dtype=float) # (frames, params)
469
+ means = np.nanmean(data, axis=0)
470
+ for n, v in zip(names, means):
471
+ row[f"mean_{str(n)}"] = float(v)
472
+
473
+ return row
474
+
475
+
476
+ def build_parser() -> argparse.ArgumentParser:
477
+ p = argparse.ArgumentParser()
478
+ p.add_argument("--input-dir", required=True, help="Folder containing .h5 files")
479
+ p.add_argument(
480
+ "--out-dir",
481
+ default="",
482
+ help="Output folder (default: <input-dir>/results/violin_chart)",
483
+ )
484
+ p.add_argument(
485
+ "--config",
486
+ default="",
487
+ help="Path to config.toml (default: ../config.toml relative to this script)",
488
+ )
489
+ p.add_argument(
490
+ "--method",
491
+ default="auto",
492
+ choices=["auto", "ttest", "mannwhitney", "anova", "kruskal"],
493
+ help="Override stat method; auto uses config defaults",
494
+ )
495
+ p.add_argument("--alpha", type=float, default=0.05)
496
+ p.add_argument("--glob", default="*.h5", help="Glob pattern for input files")
497
+ return p
498
+
499
+
500
+ def main(argv: Sequence[str]) -> int:
501
+ args = build_parser().parse_args(list(argv))
502
+
503
+ cfg_path = args.config.strip() or _default_config_path()
504
+ cfg = _load_toml(cfg_path)
505
+
506
+ alpha = float(_deep_get(cfg, ["stats", "alpha"], args.alpha))
507
+ args.alpha = alpha
508
+
509
+ in_dir = Path(args.input_dir)
510
+ if not in_dir.exists():
511
+ _die(f"input-dir not found: {in_dir}")
512
+
513
+ out_dir = Path(args.out_dir) if args.out_dir.strip() else (in_dir / "results" / "violin_chart")
514
+ out_dir.mkdir(parents=True, exist_ok=True)
515
+
516
+ h5_paths = sorted(in_dir.glob(args.glob))
517
+ if not h5_paths:
518
+ _die(f"no files matched: {in_dir / args.glob}")
519
+
520
+ import pandas as pd
521
+
522
+ rows = []
523
+ for p in h5_paths:
524
+ group = _parse_group(p)
525
+ base = {"file": str(p), "record": p.stem, "group": group}
526
+ try:
527
+ base.update(_read_one_file(p))
528
+ except Exception as e:
529
+ # keep going; some files may be header-only
530
+ base["_error"] = str(e)
531
+ rows.append(base)
532
+
533
+ df = pd.DataFrame(rows)
534
+
535
+ # determine parameter columns
536
+ meta_cols = {"file", "record", "group", "_error"}
537
+ params = [c for c in df.columns if c not in meta_cols]
538
+ params = [c for c in params if df[c].dropna().shape[0] > 0]
539
+
540
+ if not params:
541
+ _die("no parameters found in H5 files (AdvancedParameters/KinematicParameter missing?)")
542
+
543
+ stats_rows = []
544
+ pair_rows = []
545
+
546
+ for param in params:
547
+ d = df[["group", param]].dropna().copy()
548
+ try:
549
+ d[param] = d[param].astype(float)
550
+ except Exception:
551
+ continue
552
+ d = d[~d[param].isna()]
553
+ if d.shape[0] < 2:
554
+ continue
555
+ groups = d["group"].astype(str).tolist()
556
+ vals = d[param].astype(float).tolist()
557
+ uniq = sorted(set(groups))
558
+ if len(uniq) < 2:
559
+ continue
560
+
561
+ method = _choose_method(len(uniq), args.method, cfg)
562
+ overall = _run_overall(groups, vals, method)
563
+
564
+ correction = str(_deep_get(cfg, ["stats", "pairwise_correction"], "holm"))
565
+ pairwise_only_if_sig = bool(_deep_get(cfg, ["stats", "pairwise_only_if_overall_sig"], True))
566
+
567
+ pairwise = []
568
+ if len(uniq) >= 3:
569
+ if (not pairwise_only_if_sig) or (overall.p_value <= args.alpha):
570
+ pairwise = _run_pairwise(groups, vals, method, correction=correction)
571
+
572
+ stats_rows.append(
573
+ {
574
+ "param": param,
575
+ "n": int(len(vals)),
576
+ "groups": ",".join(uniq),
577
+ "method": method,
578
+ "overall_test": overall.test,
579
+ "overall_p": overall.p_value,
580
+ "overall_stat": overall.statistic,
581
+ }
582
+ )
583
+ for r in pairwise:
584
+ r2 = dict(r)
585
+ r2["param"] = param
586
+ pair_rows.append(r2)
587
+
588
+ title = f"{param} | {overall.test} p={overall.p_value:.3g}"
589
+ out_path = out_dir / f"violin_{param}.png"
590
+
591
+ # choose comparisons to annotate on plot
592
+ # - if 2 groups: annotate the single overall test p
593
+ # - if 3+ groups: annotate pairwise results (adjusted p by default)
594
+ comparisons_for_plot: list[dict] = []
595
+ if len(uniq) == 2:
596
+ a, b = uniq[0], uniq[1]
597
+ comparisons_for_plot = [{"a": a, "b": b, "p": float(overall.p_value)}]
598
+ else:
599
+ comparisons_for_plot = list(pairwise)
600
+
601
+ _make_violin(
602
+ df,
603
+ param,
604
+ out_path,
605
+ cfg,
606
+ title,
607
+ comparisons=comparisons_for_plot,
608
+ alpha=args.alpha,
609
+ )
610
+
611
+ # dump stats
612
+ if stats_rows:
613
+ pd.DataFrame(stats_rows).to_csv(out_dir / "stats_overall.csv", index=False, encoding="utf-8-sig")
614
+ if pair_rows:
615
+ pd.DataFrame(pair_rows).to_csv(out_dir / "stats_pairwise.csv", index=False, encoding="utf-8-sig")
616
+
617
+ print(f"Done. Plots saved to: {out_dir}")
618
+ return 0
619
+
620
+
621
+ if __name__ == "__main__":
622
+ import sys
623
+
624
+ raise SystemExit(main(sys.argv[1:]))