@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,267 @@
1
+ #!/usr/bin/env python3
2
+ """Extract and merge ABIDE phenotype tables for downstream analysis.
3
+
4
+ Reads ABIDE phenotype CSV files, standardizes diagnosis labels,
5
+ selects columns, and optionally cross-references with imaging subject lists.
6
+ """
7
+ import argparse
8
+ import csv
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Dict, List, Optional, Set
12
+
13
+ try:
14
+ import pandas as pd
15
+ except ImportError:
16
+ print("Error: pandas is required. Install with: pip install pandas", file=sys.stderr)
17
+ sys.exit(1)
18
+
19
+
20
+ # ABIDE column name standardization
21
+ COLUMN_MAP = {
22
+ "SUBJECT_ID": "subject_id",
23
+ "subject": "subject_id",
24
+ "Subject": "subject_id",
25
+ "DX_GROUP": "diagnosis",
26
+ "DX": "diagnosis",
27
+ "AGE_AT_SCAN": "age",
28
+ "AGE": "age",
29
+ "SEX": "sex",
30
+ "FIQ": "fiq",
31
+ "VIQ": "viq",
32
+ "PIQ": "piq",
33
+ "SITE_ID": "site",
34
+ "SITE": "site",
35
+ "HANDEDNESS_CATEGORY": "handedness",
36
+ "HANDEDNESS": "handedness",
37
+ }
38
+
39
+ # Diagnosis label mapping
40
+ DIAGNOSIS_MAP = {
41
+ "1": "ASD",
42
+ "2": "control",
43
+ 1: "ASD",
44
+ 2: "control",
45
+ "ASD": "ASD",
46
+ "Control": "control",
47
+ "CONTROL": "control",
48
+ "TD": "control",
49
+ }
50
+
51
+
52
+ def read_phenotype_file(file_path: Path) -> Optional["pd.DataFrame"]:
53
+ """Read an ABIDE phenotype file."""
54
+ try:
55
+ # Try tab first, then comma
56
+ df = pd.read_csv(file_path, sep=None, engine="python", low_memory=False)
57
+ return df
58
+ except Exception as e:
59
+ print(f"[WARN] Failed to read {file_path}: {e}", file=sys.stderr)
60
+ return None
61
+
62
+
63
+ def standardize_columns(df: "pd.DataFrame") -> "pd.DataFrame":
64
+ """Standardize ABIDE column names."""
65
+ rename_map = {}
66
+ for old_name, new_name in COLUMN_MAP.items():
67
+ if old_name in df.columns and new_name not in df.columns:
68
+ rename_map[old_name] = new_name
69
+ if rename_map:
70
+ df = df.rename(columns=rename_map)
71
+ return df
72
+
73
+
74
+ def standardize_diagnosis(df: "pd.DataFrame") -> "pd.DataFrame":
75
+ """Standardize diagnosis labels."""
76
+ if "diagnosis" in df.columns:
77
+ df["diagnosis"] = df["diagnosis"].map(DIAGNOSIS_MAP).fillna(df["diagnosis"])
78
+ return df
79
+
80
+
81
+ def normalize_subject_id(raw_id) -> str:
82
+ """Normalize subject ID for matching."""
83
+ clean = str(raw_id).strip()
84
+ if clean.startswith("sub-"):
85
+ clean = clean[4:]
86
+ # Remove non-alphanumeric characters
87
+ import re
88
+ clean = re.sub(r"[^a-zA-Z0-9]", "", clean)
89
+ return f"sub-{clean}"
90
+
91
+
92
+ def main() -> int:
93
+ parser = argparse.ArgumentParser(
94
+ description="Extract and merge ABIDE phenotype tables."
95
+ )
96
+ parser.add_argument(
97
+ "--phenotype-dir",
98
+ help="Directory containing ABIDE phenotype CSV files",
99
+ )
100
+ parser.add_argument(
101
+ "--phenotype-file",
102
+ help="Direct path to ABIDE phenotype CSV file",
103
+ )
104
+ parser.add_argument(
105
+ "--output", required=True, help="Output path for merged phenotype CSV"
106
+ )
107
+ parser.add_argument(
108
+ "--columns",
109
+ help="Comma-separated list of columns to select (default: all common columns)",
110
+ )
111
+ parser.add_argument(
112
+ "--imaging-ids",
113
+ help="Path to BIDS participants.tsv or file with imaging subject IDs",
114
+ )
115
+ parser.add_argument(
116
+ "--missing-threshold",
117
+ type=float,
118
+ default=0.5,
119
+ help="Drop columns with more than this fraction of missing values (default: 0.5)",
120
+ )
121
+ parser.add_argument(
122
+ "--abide-version",
123
+ default="I",
124
+ help="ABIDE version: I or II (default: I)",
125
+ )
126
+ args = parser.parse_args()
127
+
128
+ # Find phenotype files
129
+ phenotype_files = []
130
+ if args.phenotype_file:
131
+ p = Path(args.phenotype_file).resolve()
132
+ if p.exists():
133
+ phenotype_files.append(p)
134
+ else:
135
+ print(f"Phenotype file not found: {p}", file=sys.stderr)
136
+ return 1
137
+ elif args.phenotype_dir:
138
+ phenotype_dir = Path(args.phenotype_dir).resolve()
139
+ if phenotype_dir.exists():
140
+ for f in sorted(phenotype_dir.iterdir()):
141
+ if f.is_file() and f.suffix in (".csv", ".tsv"):
142
+ phenotype_files.append(f)
143
+ else:
144
+ print(f"Phenotype directory not found: {phenotype_dir}", file=sys.stderr)
145
+ return 1
146
+ else:
147
+ print("Error: specify --phenotype-file or --phenotype-dir", file=sys.stderr)
148
+ return 1
149
+
150
+ if not phenotype_files:
151
+ print("[ERROR] No phenotype files found.", file=sys.stderr)
152
+ return 1
153
+
154
+ print(f"Found {len(phenotype_files)} phenotype file(s):")
155
+ for f in phenotype_files:
156
+ print(f" - {f.name}")
157
+
158
+ # Read and merge
159
+ dataframes = []
160
+ for f in phenotype_files:
161
+ df = read_phenotype_file(f)
162
+ if df is not None and len(df) > 0:
163
+ df = standardize_columns(df)
164
+ df = standardize_diagnosis(df)
165
+ dataframes.append(df)
166
+
167
+ if not dataframes:
168
+ print("[ERROR] No data loaded.", file=sys.stderr)
169
+ return 1
170
+
171
+ # Merge: concatenate if same columns, merge if different
172
+ if len(dataframes) == 1:
173
+ merged = dataframes[0]
174
+ else:
175
+ # Try merge on subject_id
176
+ common_cols = set(dataframes[0].columns)
177
+ for df in dataframes[1:]:
178
+ common_cols &= set(df.columns)
179
+
180
+ if "subject_id" in common_cols:
181
+ merged = dataframes[0]
182
+ for df in dataframes[1:]:
183
+ new_cols = [c for c in df.columns if c not in merged.columns or c == "subject_id"]
184
+ merged = pd.merge(merged, df[new_cols], on="subject_id", how="outer", suffixes=("", "_dup"))
185
+ # Drop duplicate columns
186
+ dup_cols = [c for c in merged.columns if c.endswith("_dup")]
187
+ if dup_cols:
188
+ merged = merged.drop(columns=dup_cols)
189
+ else:
190
+ merged = pd.concat(dataframes, ignore_index=True)
191
+
192
+ # Filter by imaging IDs
193
+ if args.imaging_ids:
194
+ imaging_path = Path(args.imaging_ids).resolve()
195
+ if imaging_path.exists():
196
+ imaging_ids = set()
197
+ try:
198
+ id_df = pd.read_csv(imaging_path, sep="\t")
199
+ for col in ["participant_id", "subject_id", "SUBJECT_ID"]:
200
+ if col in id_df.columns:
201
+ imaging_ids = set(id_df[col].astype(str).str.strip())
202
+ break
203
+ except Exception:
204
+ pass
205
+
206
+ if imaging_ids and "subject_id" in merged.columns:
207
+ # Normalize for matching
208
+ normalized = set()
209
+ for sid in imaging_ids:
210
+ normalized.add(normalize_subject_id(sid))
211
+ normalized.add(sid)
212
+
213
+ before_count = len(merged)
214
+ merged["_norm_id"] = merged["subject_id"].apply(normalize_subject_id)
215
+ mask = merged["_norm_id"].isin(normalized) | merged["subject_id"].astype(str).isin(normalized)
216
+ merged = merged[mask].drop(columns=["_norm_id"])
217
+ print(f"Filtered to imaging subjects: {before_count} -> {len(merged)} rows")
218
+
219
+ # Select specific columns
220
+ if args.columns:
221
+ requested = [c.strip() for c in args.columns.split(",")]
222
+ available = [c for c in requested if c in merged.columns]
223
+ missing = [c for c in requested if c not in merged.columns]
224
+ if missing:
225
+ print(f"[WARN] Columns not found (skipped): {missing}")
226
+ if available:
227
+ # Always include subject_id
228
+ if "subject_id" not in available and "subject_id" in merged.columns:
229
+ available = ["subject_id"] + available
230
+ merged = merged[available]
231
+
232
+ # Drop columns with too many missing values
233
+ if args.missing_threshold < 1.0:
234
+ missing_frac = merged.isnull().mean()
235
+ cols_to_drop = missing_frac[missing_frac > args.missing_threshold].index.tolist()
236
+ # Keep subject_id and diagnosis
237
+ cols_to_drop = [c for c in cols_to_drop if c not in ("subject_id", "diagnosis")]
238
+ if cols_to_drop:
239
+ print(f"Dropping {len(cols_to_drop)} columns with >{args.missing_threshold*100}% missing")
240
+ merged = merged.drop(columns=cols_to_drop)
241
+
242
+ # Write output
243
+ output_path = Path(args.output).resolve()
244
+ output_path.parent.mkdir(parents=True, exist_ok=True)
245
+ merged.to_csv(output_path, index=False)
246
+ print(f"\nWrote {len(merged)} rows x {len(merged.columns)} columns to {output_path}")
247
+
248
+ # Print summary stats
249
+ if "diagnosis" in merged.columns:
250
+ dx_counts = merged["diagnosis"].value_counts()
251
+ print(f"\nDiagnosis distribution:")
252
+ for dx, count in dx_counts.items():
253
+ print(f" {dx}: {count}")
254
+
255
+ if "site" in merged.columns:
256
+ site_counts = merged["site"].value_counts()
257
+ print(f"\nSite distribution ({len(site_counts)} sites):")
258
+ for site, count in site_counts.head(10).items():
259
+ print(f" {site}: {count}")
260
+ if len(site_counts) > 10:
261
+ print(f" ... and {len(site_counts) - 10} more sites")
262
+
263
+ return 0
264
+
265
+
266
+ if __name__ == "__main__":
267
+ sys.exit(main())
@@ -0,0 +1,387 @@
1
+ #!/usr/bin/env python3
2
+ """Reorganize ABIDE raw data into BIDS-compliant directory structure.
3
+
4
+ Handles FCP/INDI-style subject directories, site extraction, and modality
5
+ routing for T1w and rs-fMRI.
6
+ """
7
+ import argparse
8
+ import csv
9
+ import json
10
+ import os
11
+ import re
12
+ import shutil
13
+ import sys
14
+ from pathlib import Path
15
+ from typing import Dict, List, Optional, Set, Tuple
16
+
17
+ SIDECAR_EXTENSIONS = [".json", ".bval", ".bvec", ".tsv"]
18
+
19
+ # Known ABIDE site names
20
+ ABIDE_SITES = [
21
+ "Caltech", "CMU", "KKI", "Leuven", "MaxMun", "NYU", "OHSU",
22
+ "Olin", "Pitt", "SBL", "SDSU", "Stanford", "Trinity", "UCLA",
23
+ "UMich", "USM", "Yale", "ABIDEII-KKI-ABIDEII", "ABIDEII-NYU-ABIDEII",
24
+ ]
25
+
26
+
27
+ def normalize_subject_id(raw_id: str) -> str:
28
+ """Convert ABIDE subject ID to BIDS-compatible label."""
29
+ clean = raw_id.strip()
30
+ if clean.startswith("sub-"):
31
+ clean = clean[4:]
32
+ # Remove non-alphanumeric characters
33
+ clean = re.sub(r"[^a-zA-Z0-9]", "", clean)
34
+ return f"sub-{clean}"
35
+
36
+
37
+ def detect_site_from_path(file_path: Path, root_dir: Path) -> str:
38
+ """Extract site name from ABIDE directory structure."""
39
+ rel = file_path.relative_to(root_dir)
40
+ parts = rel.parts
41
+ for part in parts:
42
+ # Check if any known site name is in the path component
43
+ for site in ABIDE_SITES:
44
+ if site.lower() in part.lower():
45
+ return site
46
+ return "unknown"
47
+
48
+
49
+ def detect_modality(directory: Path, filename: str) -> Optional[Tuple[str, str]]:
50
+ """Detect BIDS modality suffix and folder from directory name or filename.
51
+
52
+ Returns (bids_suffix, bids_folder) or None.
53
+ """
54
+ name_lower = (directory.name + " " + filename).lower()
55
+
56
+ # T1w
57
+ if any(k in name_lower for k in ["t1w", "t1", "anat", "mprage", "spgr"]):
58
+ if "bold" not in name_lower and "fmri" not in name_lower:
59
+ return ("T1w", "anat")
60
+
61
+ # T2w
62
+ if any(k in name_lower for k in ["t2w", "t2"]):
63
+ if "bold" not in name_lower:
64
+ return ("T2w", "anat")
65
+
66
+ # FLAIR
67
+ if "flair" in name_lower:
68
+ return ("FLAIR", "anat")
69
+
70
+ # rs-fMRI / BOLD
71
+ if any(k in name_lower for k in ["rest", "bold", "fmri", "func", "rsfmri", "rfmri"]):
72
+ return ("task-rest_bold", "func")
73
+
74
+ return None
75
+
76
+
77
+ def find_nifti_files(directory: Path) -> List[Path]:
78
+ """Find all NIfTI files in a directory."""
79
+ results = []
80
+ for f in directory.rglob("*"):
81
+ if f.is_file() and (f.name.endswith(".nii") or f.name.endswith(".nii.gz")):
82
+ results.append(f)
83
+ return results
84
+
85
+
86
+ def copy_with_sidecars(src_nifti: Path, dst_dir: Path, dst_stem: str) -> List[Path]:
87
+ """Copy NIfTI file and its sidecar files to destination."""
88
+ dst_dir.mkdir(parents=True, exist_ok=True)
89
+ copied = []
90
+
91
+ # Copy main NIfTI
92
+ ext = ".nii.gz" if src_nifti.name.endswith(".nii.gz") else ".nii"
93
+ dst_nifti = dst_dir / f"{dst_stem}{ext}"
94
+ if not dst_nifti.exists():
95
+ shutil.copy2(str(src_nifti), str(dst_nifti))
96
+ copied.append(dst_nifti)
97
+
98
+ # Copy sidecars
99
+ src_stem = src_nifti.name
100
+ if src_stem.endswith(".nii.gz"):
101
+ src_stem = src_stem[:-7]
102
+ elif src_stem.endswith(".nii"):
103
+ src_stem = src_stem[:-4]
104
+
105
+ for sidecar_ext in SIDECAR_EXTENSIONS:
106
+ src_sidecar = src_nifti.parent / f"{src_stem}{sidecar_ext}"
107
+ if src_sidecar.exists():
108
+ dst_sidecar = dst_dir / f"{dst_stem}{sidecar_ext}"
109
+ if not dst_sidecar.exists():
110
+ shutil.copy2(str(src_sidecar), str(dst_sidecar))
111
+ copied.append(dst_sidecar)
112
+
113
+ return copied
114
+
115
+
116
+ def write_dataset_description(bids_root: Path, version: str = "I") -> None:
117
+ """Write BIDS dataset_description.json."""
118
+ desc = {
119
+ "Name": f"ABIDE {version}",
120
+ "BIDSVersion": "1.8.0",
121
+ "DatasetType": "raw",
122
+ "GeneratedBy": [
123
+ {
124
+ "Name": "NeuroClaw abide-skill",
125
+ "Description": "ABIDE raw data reorganized to BIDS structure",
126
+ "Version": "1.0.0",
127
+ }
128
+ ],
129
+ }
130
+ desc_path = bids_root / "dataset_description.json"
131
+ with open(desc_path, "w", encoding="utf-8") as f:
132
+ json.dump(desc, f, indent=2, ensure_ascii=False)
133
+ print(f"[OK] Wrote {desc_path}")
134
+
135
+
136
+ def write_participants_tsv(
137
+ bids_root: Path, participants: List[Dict[str, str]], phenotype_file: Optional[Path] = None
138
+ ) -> None:
139
+ """Write BIDS participants.tsv with optional phenotype metadata."""
140
+ # Load phenotype data if available
141
+ phenotype_map: Dict[str, Dict[str, str]] = {}
142
+ if phenotype_file and phenotype_file.exists():
143
+ try:
144
+ with open(phenotype_file, "r", encoding="utf-8") as f:
145
+ reader = csv.DictReader(f)
146
+ for row in reader:
147
+ # Standardize key columns
148
+ sid = row.get("SUBJECT_ID", row.get("subject", row.get("Subject", "")))
149
+ if sid:
150
+ clean_sid = normalize_subject_id(sid)
151
+ phenotype_map[clean_sid] = row
152
+ except Exception as e:
153
+ print(f"[WARN] Failed to read phenotype file: {e}")
154
+
155
+ tsv_path = bids_root / "participants.tsv"
156
+ # Collect all unique header columns
157
+ headers = ["participant_id", "site"]
158
+ if phenotype_map:
159
+ sample = next(iter(phenotype_map.values()))
160
+ for col in sample.keys():
161
+ if col not in headers and col not in ("SUBJECT_ID", "subject", "Subject"):
162
+ headers.append(col)
163
+
164
+ with open(tsv_path, "w", encoding="utf-8", newline="") as f:
165
+ writer = csv.writer(f, delimiter="\t")
166
+ writer.writerow(headers)
167
+ seen = set()
168
+ for p in participants:
169
+ pid = p["participant_id"]
170
+ if pid in seen:
171
+ continue
172
+ seen.add(pid)
173
+ row = [pid, p.get("site", "unknown")]
174
+ # Add phenotype columns
175
+ pheno = phenotype_map.get(pid, {})
176
+ for col in headers[2:]:
177
+ row.append(pheno.get(col, "n/a"))
178
+ writer.writerow(row)
179
+ print(f"[OK] Wrote {tsv_path} ({len(seen)} participants)")
180
+
181
+
182
+ def detect_subject_layout(input_dir: Path) -> str:
183
+ """Detect whether input follows site-based or flat layout.
184
+
185
+ Returns 'site_based' if organized as site/subject/...,
186
+ 'flat' if organized as subject/...,
187
+ 'bids_like' if already partially BIDS-formatted.
188
+ """
189
+ children = [p for p in input_dir.iterdir() if p.is_dir() and not p.name.startswith(".")]
190
+ if not children:
191
+ return "flat"
192
+
193
+ # Check if children are site directories
194
+ for child in children:
195
+ if child.name in ABIDE_SITES or any(s.lower() in child.name.lower() for s in ABIDE_SITES):
196
+ return "site_based"
197
+ # Check if children contain subject-like numeric IDs
198
+ sub_children = [p for p in child.iterdir() if p.is_dir()]
199
+ if sub_children:
200
+ for sc in sub_children:
201
+ if sc.name.startswith("sub-") or sc.name.replace("_", "").isdigit():
202
+ return "site_based"
203
+
204
+ # Check if top-level looks like subjects
205
+ for child in children:
206
+ if child.name.replace("_", "").isdigit() or child.name.startswith("sub-"):
207
+ return "flat"
208
+
209
+ # Check for BIDS-like structure
210
+ for child in children:
211
+ if (child / "anat").exists() or (child / "func").exists():
212
+ return "bids_like"
213
+
214
+ return "flat"
215
+
216
+
217
+ def process_site_based_layout(
218
+ input_dir: Path,
219
+ bids_root: Path,
220
+ participants: List[Dict[str, str]],
221
+ dry_run: bool = False,
222
+ ) -> Tuple[int, int, int]:
223
+ """Process site-based ABIDE layout: site/subject/session/modality/files."""
224
+ converted = 0
225
+ skipped = 0
226
+ failed = 0
227
+
228
+ site_dirs = sorted(
229
+ [p for p in input_dir.iterdir() if p.is_dir() and not p.name.startswith(".")]
230
+ )
231
+
232
+ for site_dir in site_dirs:
233
+ site_name = site_dir.name
234
+ subject_dirs = sorted([p for p in site_dir.iterdir() if p.is_dir()])
235
+
236
+ for subject_dir in subject_dirs:
237
+ sub_label = normalize_subject_id(subject_dir.name)
238
+ participants.append({"participant_id": sub_label, "site": site_name})
239
+
240
+ # Walk through session/modality directories
241
+ nifti_files = find_nifti_files(subject_dir)
242
+ for nifti in nifti_files:
243
+ # Try to detect modality from parent directory and filename
244
+ result = detect_modality(nifti.parent, nifti.name)
245
+ if result is None:
246
+ # Try grandparent
247
+ result = detect_modality(nifti.parent.parent, nifti.name)
248
+ if result is None:
249
+ print(f"[WARN] {sub_label} ({site_name}) | cannot detect modality: {nifti.name}")
250
+ failed += 1
251
+ continue
252
+
253
+ bids_suffix, bids_folder = result
254
+ ses_label = "ses-1"
255
+ bids_sub_dir = bids_root / sub_label / ses_label / bids_folder
256
+ dst_stem = f"{sub_label}_{ses_label}_{bids_suffix}"
257
+
258
+ if (bids_sub_dir / f"{dst_stem}.nii").exists() or (
259
+ bids_sub_dir / f"{dst_stem}.nii.gz"
260
+ ).exists():
261
+ skipped += 1
262
+ continue
263
+
264
+ if dry_run:
265
+ print(f"[DRY] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}")
266
+ converted += 1
267
+ else:
268
+ copy_with_sidecars(nifti, bids_sub_dir, dst_stem)
269
+ print(f"[OK] {sub_label} ({site_name}) / {bids_folder} / {dst_stem}")
270
+ converted += 1
271
+
272
+ return converted, skipped, failed
273
+
274
+
275
+ def process_flat_layout(
276
+ input_dir: Path,
277
+ bids_root: Path,
278
+ participants: List[Dict[str, str]],
279
+ dry_run: bool = False,
280
+ ) -> Tuple[int, int, int]:
281
+ """Process flat ABIDE layout: subject/modality/files."""
282
+ converted = 0
283
+ skipped = 0
284
+ failed = 0
285
+
286
+ subject_dirs = sorted(
287
+ [p for p in input_dir.iterdir() if p.is_dir() and not p.name.startswith(".")]
288
+ )
289
+
290
+ for subject_dir in subject_dirs:
291
+ sub_label = normalize_subject_id(subject_dir.name)
292
+ site = detect_site_from_path(subject_dir, input_dir)
293
+ participants.append({"participant_id": sub_label, "site": site})
294
+
295
+ nifti_files = find_nifti_files(subject_dir)
296
+ for nifti in nifti_files:
297
+ result = detect_modality(nifti.parent, nifti.name)
298
+ if result is None:
299
+ result = detect_modality(subject_dir, nifti.name)
300
+ if result is None:
301
+ print(f"[WARN] {sub_label} | cannot detect modality: {nifti.name}")
302
+ failed += 1
303
+ continue
304
+
305
+ bids_suffix, bids_folder = result
306
+ ses_label = "ses-1"
307
+ bids_sub_dir = bids_root / sub_label / ses_label / bids_folder
308
+ dst_stem = f"{sub_label}_{ses_label}_{bids_suffix}"
309
+
310
+ if (bids_sub_dir / f"{dst_stem}.nii").exists() or (
311
+ bids_sub_dir / f"{dst_stem}.nii.gz"
312
+ ).exists():
313
+ skipped += 1
314
+ continue
315
+
316
+ if dry_run:
317
+ print(f"[DRY] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}")
318
+ converted += 1
319
+ else:
320
+ copy_with_sidecars(nifti, bids_sub_dir, dst_stem)
321
+ print(f"[OK] {sub_label} / {bids_folder} / {dst_stem}")
322
+ converted += 1
323
+
324
+ return converted, skipped, failed
325
+
326
+
327
+ def main() -> int:
328
+ parser = argparse.ArgumentParser(
329
+ description="Reorganize ABIDE raw data into BIDS-compliant structure."
330
+ )
331
+ parser.add_argument(
332
+ "--input", required=True, help="Path to ABIDE raw data directory"
333
+ )
334
+ parser.add_argument(
335
+ "--output", required=True, help="Path to output BIDS directory"
336
+ )
337
+ parser.add_argument(
338
+ "--phenotype", help="Path to ABIDE phenotype CSV file"
339
+ )
340
+ parser.add_argument(
341
+ "--version",
342
+ default="I",
343
+ help="ABIDE version: I or II (default: I)",
344
+ )
345
+ parser.add_argument(
346
+ "--dry-run", action="store_true", help="Preview without copying files"
347
+ )
348
+ args = parser.parse_args()
349
+
350
+ input_dir = Path(args.input).resolve()
351
+ output_dir = Path(args.output).resolve()
352
+
353
+ if not input_dir.exists() or not input_dir.is_dir():
354
+ print(f"Input directory does not exist: {input_dir}", file=sys.stderr)
355
+ return 1
356
+
357
+ output_dir.mkdir(parents=True, exist_ok=True)
358
+ participants: List[Dict[str, str]] = []
359
+
360
+ # Detect layout
361
+ layout = detect_subject_layout(input_dir)
362
+ print(f"Detected layout: {layout}")
363
+ print(f"Input: {input_dir}")
364
+ print(f"Output: {output_dir}")
365
+ print(f"{'[DRY RUN] ' if args.dry_run else ''}Starting reorganization...\n")
366
+
367
+ if layout == "site_based":
368
+ converted, skipped, failed = process_site_based_layout(
369
+ input_dir, output_dir, participants, args.dry_run
370
+ )
371
+ else:
372
+ converted, skipped, failed = process_flat_layout(
373
+ input_dir, output_dir, participants, args.dry_run
374
+ )
375
+
376
+ # Write BIDS metadata
377
+ if not args.dry_run and converted > 0:
378
+ write_dataset_description(output_dir, args.version)
379
+ phenotype_path = Path(args.phenotype).resolve() if args.phenotype else None
380
+ write_participants_tsv(output_dir, participants, phenotype_path)
381
+
382
+ print(f"\nDone. Converted={converted}, Skipped={skipped}, Failed={failed}")
383
+ return 0 if failed == 0 else 2
384
+
385
+
386
+ if __name__ == "__main__":
387
+ sys.exit(main())