@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,292 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Extract and merge ABCD Study phenotype tables for downstream analysis.
|
|
3
|
+
|
|
4
|
+
Reads ABCD tab-delimited phenotype files, selects columns, aligns visits,
|
|
5
|
+
and optionally cross-references with imaging subject lists.
|
|
6
|
+
"""
|
|
7
|
+
import argparse
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, List, Optional, Set
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import pandas as pd
|
|
14
|
+
except ImportError:
|
|
15
|
+
print("Error: pandas is required. Install with: pip install pandas", file=sys.stderr)
|
|
16
|
+
sys.exit(1)
|
|
17
|
+
|
|
18
|
+
# Standard ABCD event names in temporal order
|
|
19
|
+
ABCD_EVENTS = [
|
|
20
|
+
"baselineYear1Arm1",
|
|
21
|
+
"1YearFollowUpYArm1",
|
|
22
|
+
"2YearFollowUpYArm1",
|
|
23
|
+
"3YearFollowUpYArm1",
|
|
24
|
+
"4YearFollowUpYArm1",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
# Default phenotype files to look for
|
|
28
|
+
DEFAULT_PHENOTYPE_FILES = [
|
|
29
|
+
"abcd_p_tab.csv",
|
|
30
|
+
"abcd_p_tab.tsv",
|
|
31
|
+
"mental_health.csv",
|
|
32
|
+
"mental_health.tsv",
|
|
33
|
+
"cbcl.csv",
|
|
34
|
+
"cbcl.tsv",
|
|
35
|
+
"ksads.csv",
|
|
36
|
+
"ksads.tsv",
|
|
37
|
+
"nihtb.csv",
|
|
38
|
+
"nihtb.tsv",
|
|
39
|
+
"abcd_imgincl01.csv",
|
|
40
|
+
"abcd_imgincl01.tsv",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def detect_delimiter(file_path: Path) -> str:
|
|
45
|
+
"""Detect whether file uses tab or comma delimiter."""
|
|
46
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
47
|
+
first_line = f.readline()
|
|
48
|
+
if "\t" in first_line:
|
|
49
|
+
return "\t"
|
|
50
|
+
return ","
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def read_phenotype_file(file_path: Path) -> Optional["pd.DataFrame"]:
|
|
54
|
+
"""Read a single ABCD phenotype file."""
|
|
55
|
+
delimiter = detect_delimiter(file_path)
|
|
56
|
+
try:
|
|
57
|
+
df = pd.read_csv(file_path, sep=delimiter, low_memory=False)
|
|
58
|
+
return df
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"[WARN] Failed to read {file_path}: {e}", file=sys.stderr)
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def find_common_columns(dataframes: List["pd.DataFrame"]) -> List[str]:
|
|
65
|
+
"""Find columns present in all dataframes."""
|
|
66
|
+
if not dataframes:
|
|
67
|
+
return []
|
|
68
|
+
common = set(dataframes[0].columns)
|
|
69
|
+
for df in dataframes[1:]:
|
|
70
|
+
common &= set(df.columns)
|
|
71
|
+
return sorted(common)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def merge_phenotype_tables(
|
|
75
|
+
phenotype_dir: Path,
|
|
76
|
+
columns: Optional[List[str]] = None,
|
|
77
|
+
events: Optional[List[str]] = None,
|
|
78
|
+
imaging_ids: Optional[Set[str]] = None,
|
|
79
|
+
drop_missing_threshold: float = 0.5,
|
|
80
|
+
) -> "pd.DataFrame":
|
|
81
|
+
"""Merge multiple ABCD phenotype tables.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
phenotype_dir: Directory containing phenotype CSV/TSV files.
|
|
85
|
+
columns: Specific columns to select (None = all).
|
|
86
|
+
events: ABCD event names to include (None = all).
|
|
87
|
+
imaging_ids: Set of subject IDs from imaging data to filter by.
|
|
88
|
+
drop_missing_threshold: Drop columns with > this fraction of missing values.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Merged DataFrame.
|
|
92
|
+
"""
|
|
93
|
+
phenotype_files = []
|
|
94
|
+
for f in sorted(phenotype_dir.iterdir()):
|
|
95
|
+
if f.is_file() and f.suffix in (".csv", ".tsv"):
|
|
96
|
+
phenotype_files.append(f)
|
|
97
|
+
|
|
98
|
+
if not phenotype_files:
|
|
99
|
+
# Try default filenames
|
|
100
|
+
for name in DEFAULT_PHENOTYPE_FILES:
|
|
101
|
+
candidate = phenotype_dir / name
|
|
102
|
+
if candidate.exists():
|
|
103
|
+
phenotype_files.append(candidate)
|
|
104
|
+
|
|
105
|
+
if not phenotype_files:
|
|
106
|
+
print(f"[ERROR] No phenotype files found in {phenotype_dir}", file=sys.stderr)
|
|
107
|
+
return pd.DataFrame()
|
|
108
|
+
|
|
109
|
+
print(f"Found {len(phenotype_files)} phenotype files:")
|
|
110
|
+
for f in phenotype_files:
|
|
111
|
+
print(f" - {f.name}")
|
|
112
|
+
|
|
113
|
+
# Read all files
|
|
114
|
+
dataframes = []
|
|
115
|
+
for f in phenotype_files:
|
|
116
|
+
df = read_phenotype_file(f)
|
|
117
|
+
if df is not None and len(df) > 0:
|
|
118
|
+
# Standardize ID column name
|
|
119
|
+
if "src_subject_id" in df.columns:
|
|
120
|
+
df = df.rename(columns={"src_subject_id": "subject_id"})
|
|
121
|
+
elif "SUBJECTKEY" in df.columns:
|
|
122
|
+
df = df.rename(columns={"SUBJECTKEY": "subject_id"})
|
|
123
|
+
dataframes.append(df)
|
|
124
|
+
|
|
125
|
+
if not dataframes:
|
|
126
|
+
return pd.DataFrame()
|
|
127
|
+
|
|
128
|
+
# Find common columns across all tables
|
|
129
|
+
common_cols = find_common_columns(dataframes)
|
|
130
|
+
print(f"Common columns across all tables: {len(common_cols)}")
|
|
131
|
+
|
|
132
|
+
# Merge on subject_id + eventname
|
|
133
|
+
merge_keys = []
|
|
134
|
+
if "subject_id" in common_cols:
|
|
135
|
+
merge_keys.append("subject_id")
|
|
136
|
+
if "eventname" in common_cols:
|
|
137
|
+
merge_keys.append("eventname")
|
|
138
|
+
|
|
139
|
+
if not merge_keys:
|
|
140
|
+
print("[WARN] No merge keys found (subject_id/eventname). Concatenating instead.")
|
|
141
|
+
merged = pd.concat(dataframes, ignore_index=True)
|
|
142
|
+
else:
|
|
143
|
+
merged = dataframes[0]
|
|
144
|
+
for df in dataframes[1:]:
|
|
145
|
+
# Columns to merge (excluding merge keys and duplicates)
|
|
146
|
+
new_cols = [c for c in df.columns if c not in merged.columns or c in merge_keys]
|
|
147
|
+
df_subset = df[new_cols]
|
|
148
|
+
merged = pd.merge(merged, df_subset, on=merge_keys, how="outer", suffixes=("", "_dup"))
|
|
149
|
+
|
|
150
|
+
# Drop duplicate columns
|
|
151
|
+
dup_cols = [c for c in merged.columns if c.endswith("_dup")]
|
|
152
|
+
if dup_cols:
|
|
153
|
+
merged = merged.drop(columns=dup_cols)
|
|
154
|
+
|
|
155
|
+
# Filter by events
|
|
156
|
+
if events and "eventname" in merged.columns:
|
|
157
|
+
merged = merged[merged["eventname"].isin(events)]
|
|
158
|
+
print(f"Filtered to events: {events} -> {len(merged)} rows")
|
|
159
|
+
|
|
160
|
+
# Filter by imaging IDs
|
|
161
|
+
if imaging_ids and "subject_id" in merged.columns:
|
|
162
|
+
# Normalize imaging IDs for matching
|
|
163
|
+
normalized_imaging = set()
|
|
164
|
+
for sid in imaging_ids:
|
|
165
|
+
clean = sid.replace("sub-", "")
|
|
166
|
+
normalized_imaging.add(clean)
|
|
167
|
+
normalized_imaging.add(sid)
|
|
168
|
+
|
|
169
|
+
before_count = len(merged)
|
|
170
|
+
mask = merged["subject_id"].apply(
|
|
171
|
+
lambda x: str(x).strip() in normalized_imaging
|
|
172
|
+
or f"sub-{str(x).strip()}" in normalized_imaging
|
|
173
|
+
)
|
|
174
|
+
merged = merged[mask]
|
|
175
|
+
print(f"Filtered to imaging subjects: {before_count} -> {len(merged)} rows")
|
|
176
|
+
|
|
177
|
+
# Select specific columns
|
|
178
|
+
if columns:
|
|
179
|
+
available = [c for c in columns if c in merged.columns]
|
|
180
|
+
missing = [c for c in columns if c not in merged.columns]
|
|
181
|
+
if missing:
|
|
182
|
+
print(f"[WARN] Columns not found (skipped): {missing}")
|
|
183
|
+
if available:
|
|
184
|
+
merged = merged[available]
|
|
185
|
+
|
|
186
|
+
# Drop columns with too many missing values
|
|
187
|
+
if drop_missing_threshold < 1.0:
|
|
188
|
+
missing_frac = merged.isnull().mean()
|
|
189
|
+
cols_to_drop = missing_frac[missing_frac > drop_missing_threshold].index.tolist()
|
|
190
|
+
if cols_to_drop:
|
|
191
|
+
print(f"Dropping {len(cols_to_drop)} columns with >{drop_missing_threshold*100}% missing")
|
|
192
|
+
merged = merged.drop(columns=cols_to_drop)
|
|
193
|
+
|
|
194
|
+
return merged
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def load_imaging_ids(imaging_ids_file: Path) -> Set[str]:
|
|
198
|
+
"""Load subject IDs from a BIDS participants.tsv or similar file."""
|
|
199
|
+
ids = set()
|
|
200
|
+
delimiter = detect_delimiter(imaging_ids_file)
|
|
201
|
+
try:
|
|
202
|
+
df = pd.read_csv(imaging_ids_file, sep=delimiter)
|
|
203
|
+
id_col = None
|
|
204
|
+
for col_name in ["participant_id", "subject_id", "SUBJECTKEY", "src_subject_id"]:
|
|
205
|
+
if col_name in df.columns:
|
|
206
|
+
id_col = col_name
|
|
207
|
+
break
|
|
208
|
+
if id_col:
|
|
209
|
+
ids = set(df[id_col].astype(str).str.strip())
|
|
210
|
+
except Exception as e:
|
|
211
|
+
print(f"[WARN] Failed to read imaging IDs: {e}", file=sys.stderr)
|
|
212
|
+
return ids
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def main() -> int:
|
|
216
|
+
parser = argparse.ArgumentParser(
|
|
217
|
+
description="Extract and merge ABCD Study phenotype tables."
|
|
218
|
+
)
|
|
219
|
+
parser.add_argument(
|
|
220
|
+
"--phenotype-dir",
|
|
221
|
+
required=True,
|
|
222
|
+
help="Directory containing ABCD phenotype CSV/TSV files",
|
|
223
|
+
)
|
|
224
|
+
parser.add_argument(
|
|
225
|
+
"--output",
|
|
226
|
+
required=True,
|
|
227
|
+
help="Output path for merged phenotype CSV",
|
|
228
|
+
)
|
|
229
|
+
parser.add_argument(
|
|
230
|
+
"--columns",
|
|
231
|
+
help="Comma-separated list of columns to select (default: all)",
|
|
232
|
+
)
|
|
233
|
+
parser.add_argument(
|
|
234
|
+
"--events",
|
|
235
|
+
help=f"Comma-separated ABCD event names to include. Options: {', '.join(ABCD_EVENTS)}",
|
|
236
|
+
)
|
|
237
|
+
parser.add_argument(
|
|
238
|
+
"--imaging-ids",
|
|
239
|
+
help="Path to BIDS participants.tsv or file with imaging subject IDs",
|
|
240
|
+
)
|
|
241
|
+
parser.add_argument(
|
|
242
|
+
"--missing-threshold",
|
|
243
|
+
type=float,
|
|
244
|
+
default=0.5,
|
|
245
|
+
help="Drop columns with more than this fraction of missing values (default: 0.5)",
|
|
246
|
+
)
|
|
247
|
+
args = parser.parse_args()
|
|
248
|
+
|
|
249
|
+
phenotype_dir = Path(args.phenotype_dir).resolve()
|
|
250
|
+
if not phenotype_dir.exists() or not phenotype_dir.is_dir():
|
|
251
|
+
print(f"Phenotype directory does not exist: {phenotype_dir}", file=sys.stderr)
|
|
252
|
+
return 1
|
|
253
|
+
|
|
254
|
+
# Parse options
|
|
255
|
+
columns = None
|
|
256
|
+
if args.columns:
|
|
257
|
+
columns = [c.strip() for c in args.columns.split(",")]
|
|
258
|
+
|
|
259
|
+
events = None
|
|
260
|
+
if args.events:
|
|
261
|
+
events = [e.strip() for e in args.events.split(",")]
|
|
262
|
+
|
|
263
|
+
imaging_ids = None
|
|
264
|
+
if args.imaging_ids:
|
|
265
|
+
imaging_ids_path = Path(args.imaging_ids).resolve()
|
|
266
|
+
if imaging_ids_path.exists():
|
|
267
|
+
imaging_ids = load_imaging_ids(imaging_ids_path)
|
|
268
|
+
print(f"Loaded {len(imaging_ids)} imaging subject IDs")
|
|
269
|
+
|
|
270
|
+
# Merge phenotype tables
|
|
271
|
+
merged = merge_phenotype_tables(
|
|
272
|
+
phenotype_dir=phenotype_dir,
|
|
273
|
+
columns=columns,
|
|
274
|
+
events=events,
|
|
275
|
+
imaging_ids=imaging_ids,
|
|
276
|
+
drop_missing_threshold=args.missing_threshold,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
if merged.empty:
|
|
280
|
+
print("[ERROR] No data after merging. Check input files.", file=sys.stderr)
|
|
281
|
+
return 1
|
|
282
|
+
|
|
283
|
+
# Write output
|
|
284
|
+
output_path = Path(args.output).resolve()
|
|
285
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
286
|
+
merged.to_csv(output_path, index=False)
|
|
287
|
+
print(f"\nWrote {len(merged)} rows x {len(merged.columns)} columns to {output_path}")
|
|
288
|
+
return 0
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
if __name__ == "__main__":
|
|
292
|
+
sys.exit(main())
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Reorganize ABCD Study raw data into BIDS-compliant directory structure.
|
|
3
|
+
|
|
4
|
+
Handles NDAR-style subject IDs, ABCD event names, and multimodal routing
|
|
5
|
+
for T1w, T2w, dMRI, rs-fMRI, and task-fMRI.
|
|
6
|
+
"""
|
|
7
|
+
import argparse
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import shutil
|
|
12
|
+
import sys
|
|
13
|
+
from collections import defaultdict
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Dict, List, Optional, Set, Tuple
|
|
16
|
+
|
|
17
|
+
# ABCD event name -> BIDS session label mapping
|
|
18
|
+
ABCD_SESSION_MAP = {
|
|
19
|
+
"baselineYear1Arm1": "baselineYear1Arm1",
|
|
20
|
+
"1YearFollowUpYArm1": "1YearFollowUpYArm1",
|
|
21
|
+
"2YearFollowUpYArm1": "2YearFollowUpYArm1",
|
|
22
|
+
"3YearFollowUpYArm1": "3YearFollowUpYArm1",
|
|
23
|
+
"4YearFollowUpYArm1": "4YearFollowUpYArm1",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Modality keywords -> BIDS modality mapping
|
|
27
|
+
MODALITY_PATTERNS = [
|
|
28
|
+
(re.compile(r"T1w|t1w|T1|mprage", re.IGNORECASE), "T1w", "anat"),
|
|
29
|
+
(re.compile(r"T2w|t2w|T2", re.IGNORECASE), "T2w", "anat"),
|
|
30
|
+
(re.compile(r"dMRI|dmri|DWI|dwi|DTI|dti", re.IGNORECASE), "dwi", "dwi"),
|
|
31
|
+
(re.compile(r"rsfMRI|rs-fMRI|rest_bold|task-rest", re.IGNORECASE), "task-rest_bold", "func"),
|
|
32
|
+
(re.compile(r"task-fMRI|tfMRI|task-[a-zA-Z]+_bold", re.IGNORECASE), None, "func"), # dynamic task name
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
SIDECAR_EXTENSIONS = [".json", ".bval", ".bvec", ".tsv"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def normalize_ndar_id(raw_id: str) -> str:
|
|
39
|
+
"""Convert NDAR subject ID to BIDS-compatible label.
|
|
40
|
+
|
|
41
|
+
NDAR_INV000ABC0 -> sub-NDARINV000ABC0
|
|
42
|
+
"""
|
|
43
|
+
clean = raw_id.strip()
|
|
44
|
+
if clean.startswith("sub-"):
|
|
45
|
+
clean = clean[4:]
|
|
46
|
+
# Remove any non-alphanumeric characters except underscore
|
|
47
|
+
clean = re.sub(r"[^a-zA-Z0-9_]", "", clean)
|
|
48
|
+
return f"sub-{clean}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def normalize_session(event_name: str) -> str:
|
|
52
|
+
"""Convert ABCD event name to BIDS session label."""
|
|
53
|
+
event_name = event_name.strip()
|
|
54
|
+
if event_name.startswith("ses-"):
|
|
55
|
+
event_name = event_name[4:]
|
|
56
|
+
if event_name in ABCD_SESSION_MAP:
|
|
57
|
+
return f"ses-{ABCD_SESSION_MAP[event_name]}"
|
|
58
|
+
# Fallback: sanitize
|
|
59
|
+
clean = re.sub(r"[^a-zA-Z0-9]", "", event_name)
|
|
60
|
+
return f"ses-{clean}"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def detect_modality(filename: str) -> Optional[Tuple[str, str, str]]:
|
|
64
|
+
"""Detect BIDS modality suffix and folder from filename.
|
|
65
|
+
|
|
66
|
+
Returns (bids_suffix, bids_folder, task_name) or None.
|
|
67
|
+
"""
|
|
68
|
+
for pattern, suffix, folder in MODALITY_PATTERNS:
|
|
69
|
+
if pattern.search(filename):
|
|
70
|
+
# For task-fMRI, extract task name if present
|
|
71
|
+
if folder == "func" and suffix is None:
|
|
72
|
+
task_match = re.search(r"task-([a-zA-Z]+)", filename)
|
|
73
|
+
if task_match:
|
|
74
|
+
task_name = task_match.group(1)
|
|
75
|
+
return (f"task-{task_name}_bold", "func", task_name)
|
|
76
|
+
return ("task-unknown_bold", "func", "unknown")
|
|
77
|
+
return (suffix, folder, "")
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def find_nifti_files(directory: Path) -> List[Path]:
|
|
82
|
+
"""Find all NIfTI files in a directory."""
|
|
83
|
+
results = []
|
|
84
|
+
for f in directory.rglob("*"):
|
|
85
|
+
if f.is_file() and (f.name.endswith(".nii") or f.name.endswith(".nii.gz")):
|
|
86
|
+
results.append(f)
|
|
87
|
+
return results
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def copy_with_sidecars(src_nifti: Path, dst_dir: Path, dst_stem: str) -> List[Path]:
|
|
91
|
+
"""Copy NIfTI file and its sidecar files to destination."""
|
|
92
|
+
dst_dir.mkdir(parents=True, exist_ok=True)
|
|
93
|
+
copied = []
|
|
94
|
+
|
|
95
|
+
# Copy main NIfTI
|
|
96
|
+
ext = ".nii.gz" if src_nifti.name.endswith(".nii.gz") else ".nii"
|
|
97
|
+
dst_nifti = dst_dir / f"{dst_stem}{ext}"
|
|
98
|
+
if not dst_nifti.exists():
|
|
99
|
+
shutil.copy2(str(src_nifti), str(dst_nifti))
|
|
100
|
+
copied.append(dst_nifti)
|
|
101
|
+
|
|
102
|
+
# Copy sidecars
|
|
103
|
+
src_stem = src_nifti.name
|
|
104
|
+
if src_stem.endswith(".nii.gz"):
|
|
105
|
+
src_stem = src_stem[:-7]
|
|
106
|
+
elif src_stem.endswith(".nii"):
|
|
107
|
+
src_stem = src_stem[:-4]
|
|
108
|
+
|
|
109
|
+
for sidecar_ext in SIDECAR_EXTENSIONS:
|
|
110
|
+
src_sidecar = src_nifti.parent / f"{src_stem}{sidecar_ext}"
|
|
111
|
+
if src_sidecar.exists():
|
|
112
|
+
dst_sidecar = dst_dir / f"{dst_stem}{sidecar_ext}"
|
|
113
|
+
if not dst_sidecar.exists():
|
|
114
|
+
shutil.copy2(str(src_sidecar), str(dst_sidecar))
|
|
115
|
+
copied.append(dst_sidecar)
|
|
116
|
+
|
|
117
|
+
return copied
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def write_dataset_description(bids_root: Path, release_version: str = "5.1") -> None:
|
|
121
|
+
"""Write BIDS dataset_description.json."""
|
|
122
|
+
desc = {
|
|
123
|
+
"Name": f"ABCD Study {release_version}",
|
|
124
|
+
"BIDSVersion": "1.8.0",
|
|
125
|
+
"DatasetType": "raw",
|
|
126
|
+
"GeneratedBy": [
|
|
127
|
+
{
|
|
128
|
+
"Name": "NeuroClaw abcd-skill",
|
|
129
|
+
"Description": "ABCD raw data reorganized to BIDS structure",
|
|
130
|
+
"Version": "1.0.0",
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
}
|
|
134
|
+
desc_path = bids_root / "dataset_description.json"
|
|
135
|
+
with open(desc_path, "w", encoding="utf-8") as f:
|
|
136
|
+
json.dump(desc, f, indent=2, ensure_ascii=False)
|
|
137
|
+
print(f"[OK] Wrote {desc_path}")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def write_participants_tsv(bids_root: Path, subject_ids: List[str]) -> None:
|
|
141
|
+
"""Write BIDS participants.tsv header."""
|
|
142
|
+
tsv_path = bids_root / "participants.tsv"
|
|
143
|
+
with open(tsv_path, "w", encoding="utf-8") as f:
|
|
144
|
+
f.write("participant_id\n")
|
|
145
|
+
for sid in sorted(set(subject_ids)):
|
|
146
|
+
f.write(f"{sid}\n")
|
|
147
|
+
print(f"[OK] Wrote {tsv_path} ({len(set(subject_ids))} participants)")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def detect_subject_layout(input_dir: Path) -> str:
|
|
151
|
+
"""Detect whether input follows flat or nested ABCD layout.
|
|
152
|
+
|
|
153
|
+
Returns 'flat' if subject dirs are direct children,
|
|
154
|
+
'nested' if organized as subject/session/modality,
|
|
155
|
+
'ndar_package' if following NDA download package structure.
|
|
156
|
+
"""
|
|
157
|
+
children = list(input_dir.iterdir())
|
|
158
|
+
if not children:
|
|
159
|
+
return "flat"
|
|
160
|
+
|
|
161
|
+
# Check for NDA package structure (collection directories)
|
|
162
|
+
for child in children:
|
|
163
|
+
if child.is_dir() and child.name.startswith("collection_"):
|
|
164
|
+
return "ndar_package"
|
|
165
|
+
|
|
166
|
+
# Check for subject/session/modality nesting
|
|
167
|
+
first_child = children[0]
|
|
168
|
+
if first_child.is_dir():
|
|
169
|
+
sub_children = list(first_child.iterdir())
|
|
170
|
+
if sub_children:
|
|
171
|
+
# If children have modality-like names, it's flat
|
|
172
|
+
for sc in sub_children:
|
|
173
|
+
if sc.is_dir() and detect_modality(sc.name):
|
|
174
|
+
return "flat"
|
|
175
|
+
# If children look like session names, it's nested
|
|
176
|
+
for sc in sub_children:
|
|
177
|
+
if sc.is_dir() and any(
|
|
178
|
+
k in sc.name for k in ["baseline", "Year", "FollowUp"]
|
|
179
|
+
):
|
|
180
|
+
return "nested"
|
|
181
|
+
|
|
182
|
+
return "flat"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def process_flat_layout(
|
|
186
|
+
input_dir: Path,
|
|
187
|
+
bids_root: Path,
|
|
188
|
+
participants: List[str],
|
|
189
|
+
dry_run: bool = False,
|
|
190
|
+
) -> Tuple[int, int, int]:
|
|
191
|
+
"""Process flat ABCD layout: subject/modality/files."""
|
|
192
|
+
converted = 0
|
|
193
|
+
skipped = 0
|
|
194
|
+
failed = 0
|
|
195
|
+
|
|
196
|
+
subject_dirs = sorted(
|
|
197
|
+
[p for p in input_dir.iterdir() if p.is_dir() and not p.name.startswith(".")]
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
for subject_dir in subject_dirs:
|
|
201
|
+
sub_label = normalize_ndar_id(subject_dir.name)
|
|
202
|
+
participants.append(sub_label)
|
|
203
|
+
|
|
204
|
+
for modality_dir in sorted(subject_dir.iterdir()):
|
|
205
|
+
if not modality_dir.is_dir():
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
nifti_files = find_nifti_files(modality_dir)
|
|
209
|
+
for nifti in nifti_files:
|
|
210
|
+
result = detect_modality(nifti.name)
|
|
211
|
+
if result is None:
|
|
212
|
+
# Try parent folder name
|
|
213
|
+
result = detect_modality(modality_dir.name)
|
|
214
|
+
if result is None:
|
|
215
|
+
print(f"[WARN] {sub_label} | cannot detect modality: {nifti.name}")
|
|
216
|
+
failed += 1
|
|
217
|
+
continue
|
|
218
|
+
|
|
219
|
+
bids_suffix, bids_folder, task_name = result
|
|
220
|
+
|
|
221
|
+
# Default session
|
|
222
|
+
ses_label = "ses-baselineYear1Arm1"
|
|
223
|
+
|
|
224
|
+
bids_sub_dir = bids_root / sub_label / ses_label / bids_folder
|
|
225
|
+
dst_stem = f"{sub_label}_{ses_label}_{bids_suffix}"
|
|
226
|
+
|
|
227
|
+
if (bids_sub_dir / f"{dst_stem}.nii").exists() or (
|
|
228
|
+
bids_sub_dir / f"{dst_stem}.nii.gz"
|
|
229
|
+
).exists():
|
|
230
|
+
skipped += 1
|
|
231
|
+
continue
|
|
232
|
+
|
|
233
|
+
if dry_run:
|
|
234
|
+
print(f"[DRY] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}")
|
|
235
|
+
converted += 1
|
|
236
|
+
else:
|
|
237
|
+
copy_with_sidecars(nifti, bids_sub_dir, dst_stem)
|
|
238
|
+
print(f"[OK] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}")
|
|
239
|
+
converted += 1
|
|
240
|
+
|
|
241
|
+
return converted, skipped, failed
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def process_nested_layout(
|
|
245
|
+
input_dir: Path,
|
|
246
|
+
bids_root: Path,
|
|
247
|
+
participants: List[str],
|
|
248
|
+
dry_run: bool = False,
|
|
249
|
+
) -> Tuple[int, int, int]:
|
|
250
|
+
"""Process nested ABCD layout: subject/session/modality/files."""
|
|
251
|
+
converted = 0
|
|
252
|
+
skipped = 0
|
|
253
|
+
failed = 0
|
|
254
|
+
|
|
255
|
+
subject_dirs = sorted(
|
|
256
|
+
[p for p in input_dir.iterdir() if p.is_dir() and not p.name.startswith(".")]
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
for subject_dir in subject_dirs:
|
|
260
|
+
sub_label = normalize_ndar_id(subject_dir.name)
|
|
261
|
+
participants.append(sub_label)
|
|
262
|
+
|
|
263
|
+
for session_dir in sorted(subject_dir.iterdir()):
|
|
264
|
+
if not session_dir.is_dir():
|
|
265
|
+
continue
|
|
266
|
+
|
|
267
|
+
ses_label = normalize_session(session_dir.name)
|
|
268
|
+
|
|
269
|
+
for modality_dir in sorted(session_dir.iterdir()):
|
|
270
|
+
if not modality_dir.is_dir():
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
nifti_files = find_nifti_files(modality_dir)
|
|
274
|
+
if not nifti_files:
|
|
275
|
+
# Try detecting modality from folder name
|
|
276
|
+
result = detect_modality(modality_dir.name)
|
|
277
|
+
if result:
|
|
278
|
+
nifti_files = find_nifti_files(session_dir)
|
|
279
|
+
nifti_files = [
|
|
280
|
+
f
|
|
281
|
+
for f in nifti_files
|
|
282
|
+
if f.parent == modality_dir or f.parent == session_dir
|
|
283
|
+
]
|
|
284
|
+
|
|
285
|
+
for nifti in nifti_files:
|
|
286
|
+
result = detect_modality(nifti.name)
|
|
287
|
+
if result is None:
|
|
288
|
+
result = detect_modality(modality_dir.name)
|
|
289
|
+
if result is None:
|
|
290
|
+
print(
|
|
291
|
+
f"[WARN] {sub_label}/{ses_label} | cannot detect modality: {nifti.name}"
|
|
292
|
+
)
|
|
293
|
+
failed += 1
|
|
294
|
+
continue
|
|
295
|
+
|
|
296
|
+
bids_suffix, bids_folder, task_name = result
|
|
297
|
+
bids_sub_dir = bids_root / sub_label / ses_label / bids_folder
|
|
298
|
+
dst_stem = f"{sub_label}_{ses_label}_{bids_suffix}"
|
|
299
|
+
|
|
300
|
+
if (bids_sub_dir / f"{dst_stem}.nii").exists() or (
|
|
301
|
+
bids_sub_dir / f"{dst_stem}.nii.gz"
|
|
302
|
+
).exists():
|
|
303
|
+
skipped += 1
|
|
304
|
+
continue
|
|
305
|
+
|
|
306
|
+
if dry_run:
|
|
307
|
+
print(
|
|
308
|
+
f"[DRY] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}"
|
|
309
|
+
)
|
|
310
|
+
converted += 1
|
|
311
|
+
else:
|
|
312
|
+
copy_with_sidecars(nifti, bids_sub_dir, dst_stem)
|
|
313
|
+
print(
|
|
314
|
+
f"[OK] {sub_label}/{ses_label}/{bids_folder}/{dst_stem}"
|
|
315
|
+
)
|
|
316
|
+
converted += 1
|
|
317
|
+
|
|
318
|
+
return converted, skipped, failed
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def main() -> int:
|
|
322
|
+
parser = argparse.ArgumentParser(
|
|
323
|
+
description="Reorganize ABCD Study raw data into BIDS-compliant structure."
|
|
324
|
+
)
|
|
325
|
+
parser.add_argument(
|
|
326
|
+
"--input",
|
|
327
|
+
required=True,
|
|
328
|
+
help="Path to ABCD raw data directory",
|
|
329
|
+
)
|
|
330
|
+
parser.add_argument(
|
|
331
|
+
"--output",
|
|
332
|
+
required=True,
|
|
333
|
+
help="Path to output BIDS directory",
|
|
334
|
+
)
|
|
335
|
+
parser.add_argument(
|
|
336
|
+
"--participants-file",
|
|
337
|
+
help="Optional: path to ABCD phenotype file for participant metadata",
|
|
338
|
+
)
|
|
339
|
+
parser.add_argument(
|
|
340
|
+
"--release-version",
|
|
341
|
+
default="5.1",
|
|
342
|
+
help="ABCD release version (default: 5.1)",
|
|
343
|
+
)
|
|
344
|
+
parser.add_argument(
|
|
345
|
+
"--dry-run",
|
|
346
|
+
action="store_true",
|
|
347
|
+
help="Preview without copying files",
|
|
348
|
+
)
|
|
349
|
+
args = parser.parse_args()
|
|
350
|
+
|
|
351
|
+
input_dir = Path(args.input).resolve()
|
|
352
|
+
output_dir = Path(args.output).resolve()
|
|
353
|
+
|
|
354
|
+
if not input_dir.exists() or not input_dir.is_dir():
|
|
355
|
+
print(f"Input directory does not exist: {input_dir}", file=sys.stderr)
|
|
356
|
+
return 1
|
|
357
|
+
|
|
358
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
359
|
+
participants: List[str] = []
|
|
360
|
+
|
|
361
|
+
# Detect layout
|
|
362
|
+
layout = detect_subject_layout(input_dir)
|
|
363
|
+
print(f"Detected layout: {layout}")
|
|
364
|
+
print(f"Input: {input_dir}")
|
|
365
|
+
print(f"Output: {output_dir}")
|
|
366
|
+
print(f"{'[DRY RUN] ' if args.dry_run else ''}Starting reorganization...\n")
|
|
367
|
+
|
|
368
|
+
if layout == "nested":
|
|
369
|
+
converted, skipped, failed = process_nested_layout(
|
|
370
|
+
input_dir, output_dir, participants, args.dry_run
|
|
371
|
+
)
|
|
372
|
+
else:
|
|
373
|
+
converted, skipped, failed = process_flat_layout(
|
|
374
|
+
input_dir, output_dir, participants, args.dry_run
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
# Write BIDS metadata
|
|
378
|
+
if not args.dry_run and converted > 0:
|
|
379
|
+
write_dataset_description(output_dir, args.release_version)
|
|
380
|
+
write_participants_tsv(output_dir, participants)
|
|
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())
|