@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.
- package/package.json +2 -2
- package/skills/01_Meta-Skills/academic-research-hub/SKILL.md +108 -0
- package/skills/01_Meta-Skills/academic-research-hub/scripts/requirements.txt +17 -0
- package/skills/01_Meta-Skills/academic-research-hub/scripts/research.py +781 -0
- package/skills/01_Meta-Skills/beautiful-log/SKILL.md +64 -0
- package/skills/01_Meta-Skills/beautiful-log/scripts/beautiful_log.py +274 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/SKILL.md +130 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/assets/config.template.yaml +54 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/assets/top5_digest_template.md +5 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/build_top5_digest.py +300 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/common.py +137 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/merge_results.py +106 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/run_pipeline.py +177 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/search_arxiv.py +162 -0
- package/skills/01_Meta-Skills/ethoclaw-daily-paper/scripts/search_pubmed.py +202 -0
- package/skills/01_Meta-Skills/ethoclaw-normalize-tabular/SKILL.md +173 -0
- package/skills/01_Meta-Skills/ethoclaw-normalize-tabular/scripts/normalize_data.py +874 -0
- package/skills/01_Meta-Skills/ethoclaw-pdf-research/SKILL.md +134 -0
- package/skills/01_Meta-Skills/ethoclaw-pdf-research/references/confirmation-prompts.md +31 -0
- package/skills/01_Meta-Skills/ethoclaw-pdf-research/references/output-patterns.md +45 -0
- package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_markdown_deliverables.py +41 -0
- package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_research_log.py +84 -0
- package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/build_summary_md.py +63 -0
- package/skills/01_Meta-Skills/ethoclaw-pdf-research/scripts/extract_pdf_bundle.py +140 -0
- package/skills/01_Meta-Skills/experiment-controller/SKILL.md +140 -0
- package/skills/01_Meta-Skills/knowledge-graph-builder/SKILL.md +366 -0
- package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/entity_resolution.py +120 -0
- package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/extraction_prompt_template.txt +19 -0
- package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/graph_query.py +106 -0
- package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/hypothesis_cli_reference.py +42 -0
- package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/new_data_source_template.py +116 -0
- package/skills/01_Meta-Skills/knowledge-graph-builder/scripts/requirements.txt +15 -0
- package/skills/01_Meta-Skills/method-design/SKILL.md +61 -0
- package/skills/01_Meta-Skills/multi-search-engine/SKILL.md +119 -0
- package/skills/01_Meta-Skills/research-idea/SKILL.md +65 -0
- package/skills/05_EEG_ERP/eeg-skill/SKILL.md +197 -0
- package/skills/05_EEG_ERP/meg-skill/SKILL.md +188 -0
- package/skills/05_EEG_ERP/meg-skill/scripts/time_frequency.py +223 -0
- package/skills/05_EEG_ERP/mne-eeg-tool/SKILL.md +165 -0
- package/skills/05_EEG_ERP/mne-eeg-tool/scripts/eeg_pipeline_reference.py +231 -0
- package/skills/05_EEG_ERP/seed-iv-skill/SKILL.md +184 -0
- package/skills/05_EEG_ERP/seed-iv-skill/scripts/classify_seed_iv.py +154 -0
- package/skills/05_EEG_ERP/seed-iv-skill/scripts/extract_seed_iv_features.py +190 -0
- package/skills/05_EEG_ERP/seed-iv-skill/scripts/validate_seed_iv.py +102 -0
- package/skills/05_EEG_ERP/seed-vig-skill/SKILL.md +182 -0
- package/skills/05_EEG_ERP/seed-vig-skill/scripts/classify_seed_vig.py +165 -0
- package/skills/05_EEG_ERP/seed-vig-skill/scripts/extract_seed_vig_features.py +185 -0
- package/skills/05_EEG_ERP/seed-vig-skill/scripts/validate_seed_vig.py +88 -0
- package/skills/06_fMRI_Neuroimaging/abcd-skill/SKILL.md +308 -0
- package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/abcd_qc_summary.py +449 -0
- package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/extract_abcd_phenotype.py +292 -0
- package/skills/06_fMRI_Neuroimaging/abcd-skill/scripts/reorganize_abcd.py +387 -0
- package/skills/06_fMRI_Neuroimaging/abide-skill/SKILL.md +302 -0
- package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/abide_qc_summary.py +317 -0
- package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/extract_abide_phenotype.py +267 -0
- package/skills/06_fMRI_Neuroimaging/abide-skill/scripts/reorganize_abide.py +387 -0
- package/skills/06_fMRI_Neuroimaging/adhd200-skill/SKILL.md +244 -0
- package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/adhd200_qc_summary.py +98 -0
- package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/extract_adhd200_phenotype.py +134 -0
- package/skills/06_fMRI_Neuroimaging/adhd200-skill/scripts/reorganize_adhd200.py +206 -0
- package/skills/06_fMRI_Neuroimaging/adni-skill/SKILL.md +358 -0
- package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/generate_adni_task_files.py +1305 -0
- package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/generate_vqa_from_tasks.py +766 -0
- package/skills/06_fMRI_Neuroimaging/adni-skill/scripts/reorganize_adni.py +491 -0
- package/skills/06_fMRI_Neuroimaging/aibl-skill/SKILL.md +295 -0
- package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/aibl_qc_summary.py +260 -0
- package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/extract_aibl_phenotype.py +365 -0
- package/skills/06_fMRI_Neuroimaging/aibl-skill/scripts/reorganize_aibl.py +394 -0
- package/skills/06_fMRI_Neuroimaging/aomic-skill/SKILL.md +292 -0
- package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/aomic_qc_summary.py +258 -0
- package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/extract_aomic_phenotype.py +284 -0
- package/skills/06_fMRI_Neuroimaging/aomic-skill/scripts/reorganize_aomic.py +322 -0
- package/skills/06_fMRI_Neuroimaging/asl-skill/SKILL.md +168 -0
- package/skills/06_fMRI_Neuroimaging/asl-skill/scripts/compute_cbf.py +224 -0
- package/skills/06_fMRI_Neuroimaging/bids-organizer/SKILL.md +241 -0
- package/skills/06_fMRI_Neuroimaging/bold5000-skill/SKILL.md +186 -0
- package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/bold5000_qc_summary.py +96 -0
- package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/extract_bold5000_stimulus.py +125 -0
- package/skills/06_fMRI_Neuroimaging/bold5000-skill/scripts/reorganize_bold5000.py +102 -0
- package/skills/06_fMRI_Neuroimaging/camcan-skill/SKILL.md +213 -0
- package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/camcan_qc_summary.py +131 -0
- package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/extract_camcan_phenotype.py +145 -0
- package/skills/06_fMRI_Neuroimaging/camcan-skill/scripts/validate_camcan.py +141 -0
- package/skills/06_fMRI_Neuroimaging/cobre-skill/SKILL.md +201 -0
- package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/cobre_qc_summary.py +95 -0
- package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/extract_cobre_phenotype.py +104 -0
- package/skills/06_fMRI_Neuroimaging/cobre-skill/scripts/reorganize_cobre.py +140 -0
- package/skills/06_fMRI_Neuroimaging/conn-tool/SKILL.md +180 -0
- package/skills/06_fMRI_Neuroimaging/dcm2nii/SKILL.md +189 -0
- package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/SKILL.md +183 -0
- package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/dmt_har_med_qc_summary.py +96 -0
- package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/extract_dmt_har_med_phenotype.py +121 -0
- package/skills/06_fMRI_Neuroimaging/dmt-har-med-skill/scripts/reorganize_dmt_har_med.py +125 -0
- package/skills/06_fMRI_Neuroimaging/dwi-skill/SKILL.md +359 -0
- package/skills/06_fMRI_Neuroimaging/fmri-skill/SKILL.md +371 -0
- package/skills/06_fMRI_Neuroimaging/fmriprep-tool/SKILL.md +228 -0
- package/skills/06_fMRI_Neuroimaging/freesurfer-tool/SKILL.md +286 -0
- package/skills/06_fMRI_Neuroimaging/freesurfer-tool/scripts/freesurfer_processor.py +145 -0
- package/skills/06_fMRI_Neuroimaging/fsl-tool/SKILL.md +208 -0
- package/skills/06_fMRI_Neuroimaging/hbn-skill/SKILL.md +271 -0
- package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/extract_hbn_phenotype.py +107 -0
- package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/hbn_qc_summary.py +96 -0
- package/skills/06_fMRI_Neuroimaging/hbn-skill/scripts/reorganize_hbn.py +150 -0
- package/skills/06_fMRI_Neuroimaging/hcpa-skill/SKILL.md +210 -0
- package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/extract_hcpa_phenotype.py +146 -0
- package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/hcpa_qc_summary.py +120 -0
- package/skills/06_fMRI_Neuroimaging/hcpa-skill/scripts/reorganize_hcpa.py +155 -0
- package/skills/06_fMRI_Neuroimaging/hcpd-skill/SKILL.md +210 -0
- package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/extract_hcpd_phenotype.py +148 -0
- package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/hcpd_qc_summary.py +125 -0
- package/skills/06_fMRI_Neuroimaging/hcpd-skill/scripts/reorganize_hcpd.py +146 -0
- package/skills/06_fMRI_Neuroimaging/hcpep-skill/SKILL.md +215 -0
- package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/extract_hcpep_phenotype.py +157 -0
- package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/hcpep_qc_summary.py +143 -0
- package/skills/06_fMRI_Neuroimaging/hcpep-skill/scripts/reorganize_hcpep.py +146 -0
- package/skills/06_fMRI_Neuroimaging/hcppipeline-tool/SKILL.md +217 -0
- package/skills/06_fMRI_Neuroimaging/hcpya-skill/SKILL.md +214 -0
- package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/extract_hcpya_phenotype.py +190 -0
- package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/hcpya_qc_summary.py +152 -0
- package/skills/06_fMRI_Neuroimaging/hcpya-skill/scripts/reorganize_hcpya.py +203 -0
- package/skills/06_fMRI_Neuroimaging/ixi-skill/SKILL.md +198 -0
- package/skills/06_fMRI_Neuroimaging/ixi-skill/scripts/ixi_qc_summary.py +137 -0
- package/skills/06_fMRI_Neuroimaging/ixi-skill/scripts/reorganize_ixi.py +190 -0
- package/skills/06_fMRI_Neuroimaging/mnd-skill/SKILL.md +191 -0
- package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/extract_mnd_phenotype.py +143 -0
- package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/mnd_qc_summary.py +120 -0
- package/skills/06_fMRI_Neuroimaging/mnd-skill/scripts/validate_mnd.py +107 -0
- package/skills/06_fMRI_Neuroimaging/mschallenge-skill/SKILL.md +203 -0
- package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/analyze_lesions.py +119 -0
- package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/longitudinal_lesion.py +148 -0
- package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/mschallenge_qc_summary.py +132 -0
- package/skills/06_fMRI_Neuroimaging/mschallenge-skill/scripts/validate_mschallenge.py +116 -0
- package/skills/06_fMRI_Neuroimaging/nibabel-skill/SKILL.md +184 -0
- package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/atlas_coordinate_reference.py +61 -0
- package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/freesurfer_io_reference.py +34 -0
- package/skills/06_fMRI_Neuroimaging/nibabel-skill/scripts/nifti_inspection_reference.py +35 -0
- package/skills/06_fMRI_Neuroimaging/nifd-skill/SKILL.md +205 -0
- package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/extract_nifd_phenotype.py +132 -0
- package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/nifd_qc_summary.py +111 -0
- package/skills/06_fMRI_Neuroimaging/nifd-skill/scripts/validate_nifd.py +111 -0
- package/skills/06_fMRI_Neuroimaging/nii2dcm/SKILL.md +143 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/SKILL.md +266 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/connectome_reference.py +65 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/denoise_timeseries_reference.py +58 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/hierarchical_parcellation_reference.py +53 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/kmeans_parcellation_reference.py +53 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/preprocess_bold_reference.py +76 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/rest_dictlearning_reference.py +56 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/rest_ica_reference.py +59 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/second_level_glm_reference.py +58 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/spacenet_classifier_reference.py +59 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/svm_classifier_reference.py +60 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/task_glm_reference.py +63 -0
- package/skills/06_fMRI_Neuroimaging/nilearn-tool/scripts/zalff_summary_reference.py +109 -0
- package/skills/06_fMRI_Neuroimaging/nsd-skill/SKILL.md +210 -0
- package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/extract_nsd_stimulus.py +171 -0
- package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/nsd_qc_summary.py +142 -0
- package/skills/06_fMRI_Neuroimaging/nsd-skill/scripts/validate_nsd.py +142 -0
- package/skills/06_fMRI_Neuroimaging/oasis-skill/SKILL.md +205 -0
- package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/extract_oasis_phenotype.py +126 -0
- package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/oasis_qc_summary.py +115 -0
- package/skills/06_fMRI_Neuroimaging/oasis-skill/scripts/validate_oasis.py +119 -0
- package/skills/06_fMRI_Neuroimaging/pet-skill/SKILL.md +173 -0
- package/skills/06_fMRI_Neuroimaging/pet-skill/scripts/compute_suvr.py +202 -0
- package/skills/06_fMRI_Neuroimaging/pnc-skill/SKILL.md +206 -0
- package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/extract_pnc_phenotype.py +136 -0
- package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/pnc_qc_summary.py +116 -0
- package/skills/06_fMRI_Neuroimaging/pnc-skill/scripts/validate_pnc.py +120 -0
- package/skills/06_fMRI_Neuroimaging/ppmi-skill/SKILL.md +209 -0
- package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/extract_ppmi_phenotype.py +138 -0
- package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/ppmi_qc_summary.py +111 -0
- package/skills/06_fMRI_Neuroimaging/ppmi-skill/scripts/validate_ppmi.py +117 -0
- package/skills/06_fMRI_Neuroimaging/qsiprep-tool/SKILL.md +320 -0
- package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/SKILL.md +215 -0
- package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/extract_rest_mdd_phenotype.py +132 -0
- package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/harmonize_sites.py +152 -0
- package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/rest_mdd_qc_summary.py +124 -0
- package/skills/06_fMRI_Neuroimaging/rest-mneta-mdd-skill/scripts/validate_rest_mdd.py +103 -0
- package/skills/06_fMRI_Neuroimaging/smri-skill/SKILL.md +302 -0
- package/skills/06_fMRI_Neuroimaging/tcp-skill/SKILL.md +204 -0
- package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/extract_tcp_phenotype.py +139 -0
- package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/tcp_qc_summary.py +111 -0
- package/skills/06_fMRI_Neuroimaging/tcp-skill/scripts/validate_tcp.py +99 -0
- package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/SKILL.md +217 -0
- package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/extract_ucla_cnp_phenotype.py +145 -0
- package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/ucla_cnp_qc_summary.py +111 -0
- package/skills/06_fMRI_Neuroimaging/ucla-cnp-skill/scripts/validate_ucla_cnp.py +113 -0
- package/skills/06_fMRI_Neuroimaging/ukb-skill/SKILL.md +310 -0
- package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/build_ukb_survival.py +210 -0
- package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/extract_ukb_cases.py +308 -0
- package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/extract_ukb_phenotype.py +232 -0
- package/skills/06_fMRI_Neuroimaging/ukb-skill/scripts/ukb_qc_summary.py +158 -0
- package/skills/06_fMRI_Neuroimaging/wmh-segmentation/SKILL.md +133 -0
- package/skills/07_Computational_Modeling/detrending/SKILL.md +118 -0
- package/skills/07_Computational_Modeling/dictlearning/SKILL.md +122 -0
- package/skills/07_Computational_Modeling/filtering/SKILL.md +121 -0
- package/skills/07_Computational_Modeling/glm/SKILL.md +153 -0
- package/skills/07_Computational_Modeling/hierarchical/SKILL.md +121 -0
- package/skills/07_Computational_Modeling/ica/SKILL.md +122 -0
- package/skills/07_Computational_Modeling/kmeans/SKILL.md +119 -0
- package/skills/07_Computational_Modeling/run_models/SKILL.md +427 -0
- package/skills/07_Computational_Modeling/spacenet/SKILL.md +122 -0
- package/skills/07_Computational_Modeling/svm/SKILL.md +120 -0
- package/skills/08_Computational_Neuroscience/brain_gnn/SKILL.md +183 -0
- package/skills/08_Computational_Neuroscience/dipy-tool/SKILL.md +239 -0
- package/skills/08_Computational_Neuroscience/dipy-tool/scripts/dti_metrics_reference.py +70 -0
- package/skills/08_Computational_Neuroscience/dipy-tool/scripts/load_and_mask_reference.py +76 -0
- package/skills/08_Computational_Neuroscience/dipy-tool/scripts/roi_stats_reference.py +59 -0
- package/skills/08_Computational_Neuroscience/fm_app/SKILL.md +195 -0
- package/skills/08_Computational_Neuroscience/neurostorm/SKILL.md +151 -0
- package/skills/13_Visualization/brain-visualization/SKILL.md +191 -0
- package/skills/13_Visualization/brain-visualization/scripts/connectome_reference.py +108 -0
- package/skills/13_Visualization/brain-visualization/scripts/freesurfer_ply_reference.py +54 -0
- package/skills/13_Visualization/brain-visualization/scripts/zalff_summary_reference.py +116 -0
- package/skills/13_Visualization/ethoclaw-paper-figure-layout/SKILL.md +78 -0
- package/skills/13_Visualization/ethoclaw-paper-figure-layout/assets/naturecomm_figures.tex +74 -0
- package/skills/13_Visualization/ethoclaw-paper-figure-layout/scripts/layout_results_foldered.py +579 -0
- package/skills/14_Writing/overleaf-skill/SKILL.md +184 -0
- package/skills/14_Writing/overleaf-skill/scripts/install.sh +30 -0
- package/skills/14_Writing/paper-writing/SKILL.md +146 -0
- package/skills/14_Writing/paper-writing/scripts/data_statement_templates.py +164 -0
- package/skills/14_Writing/paper-writing/scripts/figure_templates.py +315 -0
- package/skills/14_Writing/paper-writing/scripts/nature_figure_style.py +214 -0
- package/skills/14_Writing/paper-writing/scripts/section_phrasebank.py +246 -0
- package/skills/16_Animal_Behavior/deeplabcut/SKILL.md +154 -0
- package/skills/16_Animal_Behavior/deeplabcut/references/3d-pose.md +89 -0
- package/skills/16_Animal_Behavior/deeplabcut/references/maDLC.md +123 -0
- package/skills/16_Animal_Behavior/deeplabcut/references/modelzoo.md +98 -0
- package/skills/16_Animal_Behavior/deeplabcut/references/standard-pipeline.md +165 -0
- package/skills/16_Animal_Behavior/deeplabcut/references/utilities.md +146 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/SKILL.md +274 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/report_template_en.html +112 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/report_template_en.md +21 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/cluster-section.md +5 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/heatmap-section.md +5 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/integrated-interpretation.md +3 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/overview.md +3 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/project-summary.md +3 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/radar-section.md +5 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/raw-trajectory.md +3 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/sample-check.md +3 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/single-subject-section.md +3 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/assets/section_templates/stats-section.md +5 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/epm.md +52 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/fst.md +37 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/nor.md +39 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/oft.md +43 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/tcst.md +45 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/experiment-types/tst.md +36 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/input-types.md +59 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/interpretation-guardrails.md +45 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/metadata-schema.md +57 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/report-sections.md +86 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/references/section-selection-rules.md +169 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/build_report_manifest.py +27 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/render_report.py +34 -0
- package/skills/16_Animal_Behavior/ethoclaw-analysis-report/scripts/report_utils.py +1121 -0
- package/skills/16_Animal_Behavior/ethoclaw-animal-grounding/SKILL.md +390 -0
- package/skills/16_Animal_Behavior/ethoclaw-animal-grounding/reference_code.py +98 -0
- package/skills/16_Animal_Behavior/ethoclaw-animal-pose-estimation/SKILL.md +336 -0
- package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/README.md +21 -0
- package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/SKILL.md +41 -0
- package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/batch_kinematic_generator.py +663 -0
- package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/config.json +19 -0
- package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/generate_kinematic_parameter.py +401 -0
- package/skills/16_Animal_Behavior/ethoclaw-kinematic-parameter-generator/kinematic_generator.py +265 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/SKILL.md +72 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/references/config.example.toml +56 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/scripts/cluster_all_params.py +232 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-clustermap-generate/scripts/cluster_all_params_from_config.py +236 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/SKILL.md +68 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/references/notes.md +5 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-radar-generate/scripts/plot_h5_radar.py +513 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/SKILL.md +52 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/config.toml +81 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/references/stats-rule.md +18 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_inspect.py +79 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_violin_batch.py +624 -0
- package/skills/16_Animal_Behavior/ethoclaw-multiparameter-violin-stats-generate/scripts/h5_violin_stats.py +438 -0
- package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/SKILL.md +280 -0
- package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/core_scripts/heatmap_trajectory.py +790 -0
- package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/core_scripts/heatmap_velocity.py +855 -0
- package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/reference_data/reference_2d.csv +101 -0
- package/skills/16_Animal_Behavior/ethoclaw-trajectory-velocity-heatmap-generate/reference_data/reference_2d.h5 +0 -0
- 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())
|