@brainpilot/skills 0.0.6 → 0.0.7

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,790 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Trajectory Heatmap Generator for Trajectory Velocity Heatmap Generate Skill
4
+
5
+ Generates 2D spatial trajectory heatmaps from 2D/3D tracking data (.h5 or .csv).
6
+ Supports flexible data formats, auto-detection of body parts, and robust error handling.
7
+ """
8
+
9
+ import argparse
10
+ import re
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Dict, List, Optional, Tuple, Any
14
+
15
+ import numpy as np
16
+ import pandas as pd
17
+ import matplotlib
18
+ matplotlib.use('Agg')
19
+ import matplotlib.pyplot as plt
20
+ from scipy.ndimage import gaussian_filter
21
+ from matplotlib.collections import LineCollection
22
+
23
+
24
+ def parse_args() -> argparse.Namespace:
25
+ parser = argparse.ArgumentParser(
26
+ description="Generate trajectory heatmaps from animal tracking data"
27
+ )
28
+ parser.add_argument(
29
+ "input_path",
30
+ type=str,
31
+ help="Input file (.h5 or .csv) or directory"
32
+ )
33
+ parser.add_argument(
34
+ "--body-part",
35
+ type=str,
36
+ default="auto",
37
+ help="Body part to analyze (default: auto-detect)"
38
+ )
39
+ parser.add_argument(
40
+ "--list-parts",
41
+ action="store_true",
42
+ help="List available body parts and exit"
43
+ )
44
+ parser.add_argument(
45
+ "--confidence-threshold",
46
+ type=float,
47
+ default=0.6,
48
+ help="Minimum confidence threshold (default: 0.6)"
49
+ )
50
+ parser.add_argument(
51
+ "--output-dir",
52
+ type=str,
53
+ default=None,
54
+ help="Output directory (default: auto)"
55
+ )
56
+ parser.add_argument(
57
+ "--cmap",
58
+ type=str,
59
+ default="viridis",
60
+ help="Matplotlib colormap (default: viridis)"
61
+ )
62
+ parser.add_argument(
63
+ "--bins",
64
+ type=int,
65
+ default=50,
66
+ help="Number of bins for 2D histogram (default: 50)"
67
+ )
68
+ parser.add_argument(
69
+ "--sigma",
70
+ type=float,
71
+ default=2.0,
72
+ help="Gaussian smoothing sigma (default: 2.0)"
73
+ )
74
+ parser.add_argument(
75
+ "--arena-size",
76
+ type=str,
77
+ default=None,
78
+ help="Arena size in pixels (format: width,height). Auto-detected if not specified."
79
+ )
80
+ parser.add_argument(
81
+ "--outlier-threshold",
82
+ type=float,
83
+ default=10.0,
84
+ help="Outlier IQR multiplier for coordinate filtering (0 to disable, default: 10)"
85
+ )
86
+ return parser.parse_args()
87
+
88
+
89
+ def detect_body_parts(df: pd.DataFrame) -> List[str]:
90
+ """
91
+ Auto-detect body parts from DataFrame columns.
92
+ Supports various naming patterns.
93
+ """
94
+ body_parts = set()
95
+ columns = [c.lower() for c in df.columns]
96
+
97
+ # Pattern 1: {part}_x, {part}_y, {part}_confidence
98
+ pattern1 = re.compile(r'^(.+)_(x|y|z|confidence|likelihood|score)$')
99
+
100
+ # Pattern 2: x_{part}, y_{part}, confidence_{part}
101
+ pattern2 = re.compile(r'^(x|y|z|confidence|likelihood|score)_(.+)$')
102
+
103
+ for col in columns:
104
+ # Pattern 1
105
+ match = pattern1.match(col)
106
+ if match:
107
+ part = match.group(1)
108
+ if part not in ['', 'x', 'y', 'z']:
109
+ body_parts.add(part)
110
+
111
+ # Pattern 2
112
+ match = pattern2.match(col)
113
+ if match:
114
+ part = match.group(2)
115
+ body_parts.add(part)
116
+
117
+ # Additional check: look for pairs of x,y columns
118
+ for col in columns:
119
+ if col.endswith('_x') or col.endswith('x'):
120
+ base = col[:-1] if col.endswith('x') else col[:-2]
121
+ y_col = base + '_y' if col.endswith('_x') else base + 'y'
122
+ if y_col in columns or (base + '_y') in columns:
123
+ body_parts.add(base.rstrip('_'))
124
+
125
+ return sorted(list(body_parts))
126
+
127
+
128
+ def get_column_names(df: pd.DataFrame, body_part: str) -> Dict[str, str]:
129
+ """
130
+ Get actual column names for a body part (handles case variations).
131
+ Returns dict with keys: x, y, confidence
132
+ """
133
+ columns = list(df.columns)
134
+ columns_lower = [c.lower() for c in columns]
135
+ result = {}
136
+
137
+ # Try various patterns
138
+ patterns = [
139
+ (f"{body_part}_x", f"{body_part}_y", f"{body_part}_confidence"),
140
+ (f"{body_part}_x", f"{body_part}_y", f"{body_part}_likelihood"),
141
+ (f"{body_part}_x", f"{body_part}_y", f"{body_part}_score"),
142
+ (f"x_{body_part}", f"y_{body_part}", f"confidence_{body_part}"),
143
+ (f"x_{body_part}", f"y_{body_part}", f"likelihood_{body_part}"),
144
+ (f"{body_part}x", f"{body_part}y", f"{body_part}confidence"),
145
+ (f"{body_part}X", f"{body_part}Y", f"{body_part}Confidence"),
146
+ ]
147
+
148
+ for x_pat, y_pat, conf_pat in patterns:
149
+ x_col = None
150
+ y_col = None
151
+ conf_col = None
152
+
153
+ for i, col_lower in enumerate(columns_lower):
154
+ if col_lower == x_pat.lower():
155
+ x_col = columns[i]
156
+ elif col_lower == y_pat.lower():
157
+ y_col = columns[i]
158
+ elif col_lower == conf_pat.lower():
159
+ conf_col = columns[i]
160
+
161
+ if x_col and y_col:
162
+ result['x'] = x_col
163
+ result['y'] = y_col
164
+ result['confidence'] = conf_col
165
+ return result
166
+
167
+ # Fallback: search for any column containing body_part and x/y
168
+ for i, col in enumerate(columns):
169
+ col_lower = col.lower()
170
+ if body_part.lower() in col_lower:
171
+ if 'x' in col_lower and 'y' not in col_lower and 'z' not in col_lower:
172
+ result['x'] = col
173
+ elif 'y' in col_lower and 'x' not in col_lower and 'z' not in col_lower:
174
+ result['y'] = col
175
+ elif any(c in col_lower for c in ['confidence', 'likelihood', 'score']):
176
+ result['confidence'] = col
177
+
178
+ return result if 'x' in result and 'y' in result else {}
179
+
180
+
181
+ def load_h5_data(h5_path: Path) -> pd.DataFrame:
182
+ """Load tracking data from HDF5 file with flexible structure detection."""
183
+ import h5py
184
+
185
+ with h5py.File(h5_path, 'r') as f:
186
+ # Try different common structures
187
+ structures = [
188
+ ('/2Dskeleton/BodyParts', '/2Dskeleton/data2D'),
189
+ ('/body_parts', '/coordinates'),
190
+ ('/keypoints', '/data'),
191
+ ('/bodyparts', '/positions'),
192
+ ]
193
+
194
+ body_parts = None
195
+ data = None
196
+
197
+ for parts_path, data_path in structures:
198
+ if parts_path in f and data_path in f:
199
+ body_parts = f[parts_path][:]
200
+ data = f[data_path][:]
201
+ break
202
+
203
+ # If not found, try to auto-detect
204
+ if body_parts is None:
205
+ for key in f.keys():
206
+ if isinstance(f[key], h5py.Group):
207
+ for subkey in f[key].keys():
208
+ if 'part' in subkey.lower() or 'bodypart' in subkey.lower():
209
+ parts_path = f"{key}/{subkey}"
210
+ if parts_path in f:
211
+ body_parts = f[parts_path][:]
212
+ for data_key in ['data2D', 'data', 'coordinates', 'positions']:
213
+ data_path = f"{key}/{data_key}"
214
+ if data_path in f:
215
+ data = f[data_path][:]
216
+ break
217
+ if data is not None:
218
+ break
219
+ if body_parts is not None:
220
+ break
221
+
222
+ if body_parts is None or data is None:
223
+ raise ValueError("Could not detect HDF5 structure. Expected body parts and coordinate datasets.")
224
+
225
+ # Decode body parts if needed
226
+ decoded_parts = []
227
+ for part in body_parts:
228
+ if isinstance(part, bytes):
229
+ decoded_parts.append(part.decode('utf-8'))
230
+ else:
231
+ decoded_parts.append(str(part))
232
+
233
+ # Build column names
234
+ column_names = []
235
+ for part in decoded_parts:
236
+ column_names.extend([f"{part}_x", f"{part}_y", f"{part}_confidence"])
237
+
238
+ # Ensure data shape matches
239
+ expected_cols = len(decoded_parts) * 3
240
+ if data.shape[1] != expected_cols:
241
+ if data.shape[1] == len(decoded_parts) * 2:
242
+ column_names = []
243
+ for part in decoded_parts:
244
+ column_names.extend([f"{part}_x", f"{part}_y"])
245
+ else:
246
+ column_names = [f"col_{i}" for i in range(data.shape[1])]
247
+
248
+ df = pd.DataFrame(data, columns=column_names)
249
+ return df
250
+
251
+
252
+ def load_csv_data(csv_path: Path) -> pd.DataFrame:
253
+ """Load tracking data from CSV file with flexible parsing."""
254
+ try:
255
+ df = pd.read_csv(csv_path)
256
+ except Exception:
257
+ try:
258
+ df = pd.read_csv(csv_path, sep=';')
259
+ except Exception:
260
+ df = pd.read_csv(csv_path, sep='\t')
261
+
262
+ df.columns = [str(c).strip() for c in df.columns]
263
+ return df
264
+
265
+
266
+ def load_tracking_data(input_path: Path) -> pd.DataFrame:
267
+ """Load tracking data from either .h5 or .csv file."""
268
+ suffix = input_path.suffix.lower()
269
+
270
+ if suffix == '.h5' or suffix == '.hdf5':
271
+ return load_h5_data(input_path)
272
+ elif suffix == '.csv' or suffix == '.txt':
273
+ return load_csv_data(input_path)
274
+ else:
275
+ raise ValueError(f"Unsupported file format: {suffix}")
276
+
277
+
278
+ def clean_data(df: pd.DataFrame, x_col: str, y_col: str, conf_col: Optional[str] = None, outlier_threshold: float = 10.0) -> Tuple[pd.Series, pd.Series, pd.Series]:
279
+ """
280
+ Clean data by handling missing values, outliers, and invalid coordinates.
281
+ """
282
+ x = pd.to_numeric(df[x_col], errors='coerce').copy()
283
+ y = pd.to_numeric(df[y_col], errors='coerce').copy()
284
+
285
+ # Handle confidence
286
+ if conf_col and conf_col in df.columns:
287
+ confidence = pd.to_numeric(df[conf_col], errors='coerce').fillna(0)
288
+ else:
289
+ confidence = pd.Series(np.ones(len(df)))
290
+
291
+ # Replace inf with nan
292
+ x = x.replace([np.inf, -np.inf], np.nan)
293
+ y = y.replace([np.inf, -np.inf], np.nan)
294
+
295
+ # Detect and mark extreme outliers
296
+ if outlier_threshold > 0:
297
+ for coord in [x, y]:
298
+ valid = coord.dropna()
299
+ if len(valid) > 10:
300
+ q1 = valid.quantile(0.25)
301
+ q3 = valid.quantile(0.75)
302
+ iqr = q3 - q1
303
+ lower = q1 - outlier_threshold * iqr
304
+ upper = q3 + outlier_threshold * iqr
305
+ coord[(coord < lower) | (coord > upper)] = np.nan
306
+
307
+ return x, y, confidence
308
+
309
+
310
+ def calculate_trajectory_heatmap(
311
+ x: np.ndarray,
312
+ y: np.ndarray,
313
+ confidence: np.ndarray,
314
+ confidence_threshold: float,
315
+ bins: int,
316
+ sigma: float,
317
+ arena_size: Optional[Tuple[int, int]] = None
318
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
319
+ """
320
+ Calculate 2D trajectory heatmap with robust error handling.
321
+ """
322
+ # Create valid mask
323
+ valid_mask = (~np.isnan(x)) & (~np.isnan(y)) & (confidence >= confidence_threshold)
324
+ x_valid = x[valid_mask]
325
+ y_valid = y[valid_mask]
326
+
327
+ if len(x_valid) < 10:
328
+ raise ValueError("Insufficient valid data points for heatmap generation")
329
+
330
+ # Determine range
331
+ if arena_size is not None:
332
+ x_range = (0, arena_size[0])
333
+ y_range = (0, arena_size[1])
334
+ else:
335
+ x_padding = (x_valid.max() - x_valid.min()) * 0.05
336
+ y_padding = (y_valid.max() - y_valid.min()) * 0.05
337
+ x_range = (x_valid.min() - x_padding, x_valid.max() + x_padding)
338
+ y_range = (y_valid.min() - y_padding, y_valid.max() + y_padding)
339
+
340
+ # Create 2D histogram
341
+ heatmap, xedges, yedges = np.histogram2d(
342
+ x_valid, y_valid,
343
+ bins=bins,
344
+ range=[x_range, y_range]
345
+ )
346
+
347
+ # Apply Gaussian smoothing
348
+ if sigma > 0:
349
+ heatmap = gaussian_filter(heatmap, sigma=sigma)
350
+
351
+ return heatmap, xedges, yedges
352
+
353
+
354
+ def generate_trajectory_heatmap_figure(
355
+ heatmap: np.ndarray,
356
+ xedges: np.ndarray,
357
+ yedges: np.ndarray,
358
+ x: np.ndarray,
359
+ y: np.ndarray,
360
+ confidence: np.ndarray,
361
+ confidence_threshold: float,
362
+ title: str,
363
+ cmap: str,
364
+ body_part: str,
365
+ vmax: Optional[float] = None
366
+ ) -> plt.Figure:
367
+ """Generate trajectory heatmap visualization."""
368
+
369
+ fig, axes = plt.subplots(1, 2, figsize=(16, 7))
370
+
371
+ extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
372
+
373
+ # Determine color scale
374
+ if vmax is None:
375
+ vmax = np.percentile(heatmap[heatmap > 0], 99) if np.any(heatmap > 0) else 1.0
376
+ vmax = max(vmax, 0.001)
377
+
378
+ # Left panel: Heatmap only
379
+ ax1 = axes[0]
380
+
381
+ im1 = ax1.imshow(
382
+ heatmap.T,
383
+ origin='lower',
384
+ extent=extent,
385
+ cmap=cmap,
386
+ aspect='auto',
387
+ vmin=0,
388
+ vmax=vmax
389
+ )
390
+ ax1.set_xlabel('X Position (pixels)')
391
+ ax1.set_ylabel('Y Position (pixels)')
392
+ ax1.set_title(f'{title}\nTrajectory Density Heatmap ({body_part})')
393
+
394
+ cbar1 = plt.colorbar(im1, ax=ax1)
395
+ cbar1.set_label('Time spent (density)')
396
+
397
+ # Right panel: Heatmap with trajectory overlay
398
+ ax2 = axes[1]
399
+
400
+ im2 = ax2.imshow(
401
+ heatmap.T,
402
+ origin='lower',
403
+ extent=extent,
404
+ cmap=cmap,
405
+ aspect='auto',
406
+ alpha=0.7,
407
+ vmin=0,
408
+ vmax=vmax
409
+ )
410
+
411
+ # Overlay trajectory line
412
+ valid_mask = (~np.isnan(x)) & (~np.isnan(y)) & (confidence >= confidence_threshold)
413
+ x_valid = x[valid_mask]
414
+ y_valid = y[valid_mask]
415
+
416
+ # Downsample for visualization if too many points
417
+ if len(x_valid) > 5000:
418
+ step = len(x_valid) // 5000
419
+ indices = np.arange(0, len(x_valid), step)
420
+ x_plot = x_valid[indices]
421
+ y_plot = y_valid[indices]
422
+ else:
423
+ x_plot = x_valid
424
+ y_plot = y_valid
425
+
426
+ # Plot trajectory line
427
+ if len(x_plot) > 1:
428
+ ax2.plot(x_plot, y_plot, color='cyan', alpha=0.6, linewidth=1)
429
+
430
+ ax2.set_xlim(extent[0], extent[1])
431
+ ax2.set_ylim(extent[2], extent[3])
432
+ ax2.set_xlabel('X Position (pixels)')
433
+ ax2.set_ylabel('Y Position (pixels)')
434
+ ax2.set_title(f'{title}\nHeatmap + Trajectory Overlay ({body_part})')
435
+
436
+ cbar2 = plt.colorbar(im2, ax=ax2)
437
+ cbar2.set_label('Time spent (density)')
438
+
439
+ plt.tight_layout()
440
+ return fig
441
+
442
+
443
+ def detect_group(filename: str) -> str:
444
+ """Detect group from filename."""
445
+ parts = filename.replace('.h5', '').replace('.csv', '').replace('.hdf5', '').split('_')
446
+ return parts[0] if parts else "unknown"
447
+
448
+
449
+ def parse_arena_size(arena_size_str: Optional[str]) -> Optional[Tuple[int, int]]:
450
+ """Parse arena size string into (width, height) tuple."""
451
+ if arena_size_str is None:
452
+ return None
453
+ try:
454
+ width, height = map(int, arena_size_str.split(','))
455
+ return (width, height)
456
+ except ValueError:
457
+ print(f"Warning: Invalid arena size format: {arena_size_str}. Using auto-detection.")
458
+ return None
459
+
460
+
461
+ def get_output_dir(input_path: Path, custom_output_dir: Optional[str] = None) -> Path:
462
+ """Determine output directory."""
463
+ if custom_output_dir:
464
+ return Path(custom_output_dir)
465
+
466
+ if input_path.is_dir():
467
+ data_dir = input_path
468
+ else:
469
+ data_dir = input_path.parent
470
+
471
+ if data_dir.name == "1_2Dskeleton":
472
+ project_root = data_dir.parent
473
+ results_dir = project_root / "2_results" / "heatmap_trajectory"
474
+ else:
475
+ results_dir = data_dir / "results" / "heatmap_trajectory"
476
+
477
+ return results_dir
478
+
479
+
480
+ def calculate_total_distance(x: np.ndarray, y: np.ndarray, confidence: np.ndarray, threshold: float) -> float:
481
+ """Calculate total trajectory distance."""
482
+ valid_mask = (~np.isnan(x)) & (~np.isnan(y)) & (confidence >= threshold)
483
+ x_valid = x[valid_mask]
484
+ y_valid = y[valid_mask]
485
+
486
+ if len(x_valid) < 2:
487
+ return 0.0
488
+
489
+ dx = np.diff(x_valid)
490
+ dy = np.diff(y_valid)
491
+ distances = np.sqrt(dx**2 + dy**2)
492
+ return np.sum(distances)
493
+
494
+
495
+ def process_single_file(
496
+ input_file: Path,
497
+ body_part: str,
498
+ confidence_threshold: float,
499
+ output_dir: Path,
500
+ cmap: str,
501
+ bins: int,
502
+ sigma: float,
503
+ arena_size: Optional[Tuple[int, int]],
504
+ outlier_threshold: float,
505
+ list_only: bool = False,
506
+ vmax: Optional[float] = None
507
+ ) -> Optional[List[str]]:
508
+ """Process a single tracking file."""
509
+
510
+ print(f"Processing: {input_file.name}")
511
+
512
+ # Load data
513
+ try:
514
+ df = load_tracking_data(input_file)
515
+ except Exception as e:
516
+ print(f" Error loading file: {e}")
517
+ return None
518
+
519
+ print(f" Loaded {len(df)} frames, {len(df.columns)} columns")
520
+
521
+ # Detect body parts
522
+ available_parts = detect_body_parts(df)
523
+
524
+ if list_only:
525
+ return available_parts
526
+
527
+ if not available_parts:
528
+ print(f" Error: No body parts detected in columns: {list(df.columns)}")
529
+ return None
530
+
531
+ print(f" Detected body parts: {', '.join(available_parts)}")
532
+
533
+ # Select body part
534
+ if body_part == "auto":
535
+ preferred = ['center', 'body', 'back', 'midpoint', 'nose', 'head']
536
+ selected = None
537
+ for pref in preferred:
538
+ matches = [p for p in available_parts if pref in p.lower()]
539
+ if matches:
540
+ selected = matches[0]
541
+ break
542
+ if not selected:
543
+ selected = available_parts[0]
544
+ body_part = selected
545
+ print(f" Auto-selected body part: {body_part}")
546
+ else:
547
+ matches = [p for p in available_parts if p.lower() == body_part.lower()]
548
+ if not matches:
549
+ print(f" Warning: Body part '{body_part}' not found. Using: {available_parts[0]}")
550
+ body_part = available_parts[0]
551
+ else:
552
+ body_part = matches[0]
553
+
554
+ # Get column names
555
+ col_names = get_column_names(df, body_part)
556
+ if not col_names or 'x' not in col_names or 'y' not in col_names:
557
+ print(f" Error: Could not find coordinate columns for '{body_part}'")
558
+ print(f" Available columns: {list(df.columns)}")
559
+ return None
560
+
561
+ print(f" Using columns: x='{col_names['x']}', y='{col_names['y']}'", end="")
562
+ if 'confidence' in col_names:
563
+ print(f", confidence='{col_names['confidence']}'")
564
+ else:
565
+ print(" (no confidence column)")
566
+
567
+ # Clean data
568
+ x, y, confidence = clean_data(df, col_names['x'], col_names['y'], col_names.get('confidence'), outlier_threshold)
569
+
570
+ valid_count = (~x.isna() & ~y.isna() & (confidence >= confidence_threshold)).sum()
571
+ print(f" Valid frames: {valid_count}/{len(df)} ({100*valid_count/len(df):.1f}%)")
572
+
573
+ if valid_count < 10:
574
+ print(f" Skipping: insufficient valid data")
575
+ return None
576
+
577
+ # Calculate heatmap
578
+ try:
579
+ heatmap, xedges, yedges = calculate_trajectory_heatmap(
580
+ x.values, y.values, confidence.values,
581
+ confidence_threshold, bins, sigma, arena_size
582
+ )
583
+ except ValueError as e:
584
+ print(f" Error: {e}")
585
+ return None
586
+ except Exception as e:
587
+ print(f" Error calculating heatmap: {e}")
588
+ return None
589
+
590
+ # Generate visualization
591
+ group = detect_group(input_file.stem)
592
+ title = f"{input_file.stem} ({group})"
593
+
594
+ try:
595
+ fig = generate_trajectory_heatmap_figure(
596
+ heatmap, xedges, yedges, x.values, y.values, confidence.values,
597
+ confidence_threshold, title, cmap, body_part, vmax
598
+ )
599
+ except Exception as e:
600
+ print(f" Error generating plot: {e}")
601
+ return None
602
+
603
+ # Save output
604
+ output_dir.mkdir(parents=True, exist_ok=True)
605
+ output_file = output_dir / f"{input_file.stem}_trajectory.png"
606
+
607
+ try:
608
+ fig.savefig(output_file, dpi=150, bbox_inches='tight')
609
+ plt.close(fig)
610
+ print(f" Saved: {output_file}")
611
+ except Exception as e:
612
+ print(f" Error saving file: {e}")
613
+ plt.close(fig)
614
+ return None
615
+
616
+ # Statistics
617
+ total_distance = calculate_total_distance(x.values, y.values, confidence.values, confidence_threshold)
618
+ coverage = np.count_nonzero(heatmap) / heatmap.size * 100
619
+ print(f" Trajectory stats:")
620
+ print(f" Total distance: {total_distance:.2f} pixels")
621
+ print(f" Arena coverage: {coverage:.1f}%")
622
+
623
+ return available_parts
624
+
625
+
626
+ def calculate_file_heatmap(
627
+ input_file: Path,
628
+ body_part: str,
629
+ confidence_threshold: float,
630
+ bins: int,
631
+ sigma: float,
632
+ arena_size: Optional[Tuple[int, int]],
633
+ outlier_threshold: float
634
+ ) -> Tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray], Optional[str]]:
635
+ """Calculate heatmap for a single file without generating visualization.
636
+ Returns (heatmap, xedges, yedges, selected_body_part) or (None, None, None, None) on error.
637
+ """
638
+ try:
639
+ df = load_tracking_data(input_file)
640
+ except Exception as e:
641
+ print(f" Error loading file: {e}")
642
+ return None, None, None, None
643
+
644
+ # Detect body parts
645
+ available_parts = detect_body_parts(df)
646
+ if not available_parts:
647
+ return None, None, None, None
648
+
649
+ # Select body part
650
+ if body_part == "auto":
651
+ preferred = ['center', 'body', 'back', 'midpoint', 'nose', 'head']
652
+ selected = None
653
+ for pref in preferred:
654
+ matches = [p for p in available_parts if pref in p.lower()]
655
+ if matches:
656
+ selected = matches[0]
657
+ break
658
+ if not selected:
659
+ selected = available_parts[0]
660
+ selected_body_part = selected
661
+ else:
662
+ matches = [p for p in available_parts if p.lower() == body_part.lower()]
663
+ if not matches:
664
+ selected_body_part = available_parts[0]
665
+ else:
666
+ selected_body_part = matches[0]
667
+
668
+ # Get column names
669
+ col_names = get_column_names(df, selected_body_part)
670
+ if not col_names or 'x' not in col_names or 'y' not in col_names:
671
+ return None, None, None, None
672
+
673
+ # Clean data
674
+ x, y, confidence = clean_data(df, col_names['x'], col_names['y'], col_names.get('confidence'), outlier_threshold)
675
+
676
+ valid_count = (~x.isna() & ~y.isna() & (confidence >= confidence_threshold)).sum()
677
+ if valid_count < 10:
678
+ return None, None, None, None
679
+
680
+ # Calculate heatmap
681
+ try:
682
+ heatmap, xedges, yedges = calculate_trajectory_heatmap(
683
+ x.values, y.values, confidence.values,
684
+ confidence_threshold, bins, sigma, arena_size
685
+ )
686
+ return heatmap, xedges, yedges, selected_body_part
687
+ except Exception as e:
688
+ return None, None, None, None
689
+
690
+
691
+ def main():
692
+ args = parse_args()
693
+ input_path = Path(args.input_path)
694
+
695
+ if not input_path.exists():
696
+ print(f"Error: Input path does not exist: {input_path}")
697
+ sys.exit(1)
698
+
699
+ arena_size = parse_arena_size(args.arena_size)
700
+ output_dir = get_output_dir(input_path, args.output_dir)
701
+ print(f"Output directory: {output_dir}")
702
+
703
+ if input_path.is_file():
704
+ if input_path.suffix.lower() not in ['.h5', '.hdf5', '.csv', '.txt']:
705
+ print(f"Error: Unsupported file format: {input_path.suffix}")
706
+ sys.exit(1)
707
+
708
+ result = process_single_file(
709
+ input_path, args.body_part, args.confidence_threshold,
710
+ output_dir, args.cmap, args.bins, args.sigma,
711
+ arena_size, args.outlier_threshold, args.list_parts
712
+ )
713
+
714
+ if args.list_parts and result:
715
+ print(f"\nAvailable body parts: {', '.join(result)}")
716
+
717
+ elif input_path.is_dir():
718
+ files = sorted(input_path.glob("*.h5")) + sorted(input_path.glob("*.csv")) + sorted(input_path.glob("*.hdf5"))
719
+
720
+ if not files:
721
+ print(f"No .h5 or .csv files found in: {input_path}")
722
+ sys.exit(1)
723
+
724
+ print(f"Found {len(files)} files to process")
725
+ print("-" * 50)
726
+
727
+ # First pass: calculate all heatmaps and find global max for unified colorbar
728
+ print("\n[Pass 1/2] Calculating heatmaps to determine unified colorbar range...")
729
+ all_heatmaps = []
730
+ global_max = 0.0
731
+
732
+ for i, file_path in enumerate(files, 1):
733
+ print(f" [{i}/{len(files)}] {file_path.name}")
734
+ heatmap, xedges, yedges, selected_body_part = calculate_file_heatmap(
735
+ file_path, args.body_part, args.confidence_threshold,
736
+ args.bins, args.sigma, arena_size, args.outlier_threshold
737
+ )
738
+ if heatmap is not None:
739
+ all_heatmaps.append((file_path, heatmap, xedges, yedges, selected_body_part))
740
+ if np.any(heatmap > 0):
741
+ file_max = np.percentile(heatmap[heatmap > 0], 99)
742
+ global_max = max(global_max, file_max)
743
+
744
+ if not all_heatmaps:
745
+ print("No valid heatmaps could be calculated.")
746
+ sys.exit(1)
747
+
748
+ global_max = max(global_max, 0.001)
749
+ print(f"\nUnified colorbar range: 0 to {global_max:.4f}")
750
+ print("-" * 50)
751
+
752
+ # Second pass: generate visualizations with unified colorbar
753
+ print("\n[Pass 2/2] Generating visualizations with unified colorbar...")
754
+
755
+ for i, (file_path, heatmap, xedges, yedges, selected_body_part) in enumerate(all_heatmaps, 1):
756
+ print(f"\n[{i}/{len(all_heatmaps)}] {file_path.name}")
757
+
758
+ # Reload data for visualization
759
+ try:
760
+ df = load_tracking_data(file_path)
761
+ col_names = get_column_names(df, selected_body_part)
762
+ if not col_names:
763
+ continue
764
+ x, y, confidence = clean_data(df, col_names['x'], col_names['y'],
765
+ col_names.get('confidence'), args.outlier_threshold)
766
+
767
+ group = detect_group(file_path.stem)
768
+ title = f"{file_path.stem} ({group})"
769
+
770
+ fig = generate_trajectory_heatmap_figure(
771
+ heatmap, xedges, yedges, x.values, y.values, confidence.values,
772
+ args.confidence_threshold, title, args.cmap, selected_body_part, global_max
773
+ )
774
+
775
+ output_dir.mkdir(parents=True, exist_ok=True)
776
+ output_file = output_dir / f"{file_path.stem}_trajectory.png"
777
+ fig.savefig(output_file, dpi=150, bbox_inches='tight')
778
+ plt.close(fig)
779
+ print(f" Saved: {output_file}")
780
+
781
+ except Exception as e:
782
+ print(f" Error: {e}")
783
+ continue
784
+
785
+ print("\n" + "=" * 50)
786
+ print(f"All done! Results saved to: {output_dir}")
787
+
788
+
789
+ if __name__ == "__main__":
790
+ main()