dfc-kit 1.0.2__tar.gz → 1.0.3__tar.gz
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.
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/CHANGELOG.md +13 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/CITATION.cff +2 -2
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/PKG-INFO +1 -1
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/api.md +4 -3
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/cli.md +23 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/state_alignment.md +2 -1
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/states.md +38 -14
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/storage.md +31 -16
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/pyproject.toml +1 -1
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfc_kit.egg-info/PKG-INFO +1 -1
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfc_kit.egg-info/SOURCES.txt +1 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/__init__.py +1 -1
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/_preprocessing.py +34 -3
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/commands/parser.py +4 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/commands/source.py +1 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/__init__.py +2 -1
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/alignment.py +2 -3
- dfc_kit-1.0.3/src/dfckit/states/cap.py +124 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/storage/builders.py +13 -7
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/test_xcpd_state_pipeline.py +59 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/regression/test_nbs_reference.py +5 -3
- dfc_kit-1.0.3/tests/unit/test_cap.py +230 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_states.py +4 -3
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_storage.py +25 -3
- dfc_kit-1.0.2/src/dfckit/states/cap.py +0 -50
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/CONTRIBUTING.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/LICENSE +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/MANIFEST.in +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/README.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/benchmarks/README.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/benchmarks/profile_core.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/benchmarks/profile_information.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/architecture.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/correlation.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/datasets.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/getting_started.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/granger.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/group_inference.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/hmm.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/index.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/inference.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/inference_matching.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/information.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/instantaneous_edges.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/leida.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/lowrank.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/method_inventory.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/model_artifacts.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/nbs.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/partition_graphs.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/reference.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/release.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/state_scoring.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/state_selection.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/state_stability.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/tutorial_xcpd_to_states.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/docs/xcpd_input.md +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/mkdocs.yml +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/setup.cfg +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfc_kit.egg-info/dependency_links.txt +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfc_kit.egg-info/entry_points.txt +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfc_kit.egg-info/requires.txt +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfc_kit.egg-info/top_level.txt +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/_arrays.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/_feature_selection.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/_validation.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/_fields.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/_json.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/_numpy.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/models.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/state_alignment.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/state_results.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/state_scoring.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/artifacts/state_stability.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/cli.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/commands/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/commands/reporting.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/commands/stability.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/commands/states.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/_edge_products.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/correlation.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/granger.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/instantaneous.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/leida.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/lowrank.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/partition.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/connectivity/windows.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/data.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/endpoints.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/hc3.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/independent_endpoints.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/matching.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/multiple_testing.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/nbs.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/nbs_endpoints.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/paired.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/inference/state_metrics.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/information/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/information/_artifact.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/information/estimators.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/information/fixed.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/information/summary.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/io/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/io/xcpd.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/reference.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/segments.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/cross_validation.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/data.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/hmm.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/interpretation.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/kmeans.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/metrics.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/scoring.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/selection.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/stability.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/streaming.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/states/streaming_hmm.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/storage/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/storage/_statistics.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/storage/store.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/src/dfckit/storage/summary.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/fixtures/audited_kernels.json +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/test_leida_state_pipeline.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/test_model_artifact_pipeline.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/test_xcpd_information.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/test_xcpd_lowrank.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/test_xcpd_sliding_window.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/test_xcpd_static_nbs.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/integration/test_xcpd_window_pattern.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/regression/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/regression/test_audited_kernels.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/regression/test_information_reference.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/regression/test_nbs_independent_reference.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/__init__.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_artifact_helpers.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_cli.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_correlation.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_cross_validation.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_data.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_endpoint_inference.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_ets.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_feature_selection.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_granger.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_granger_optional_dependency.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_hmm.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_independent_endpoint_inference.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_inference.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_inference_matching.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_information.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_information_io.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_information_parallel.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_information_summary.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_internal_helpers.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_leida.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_lowrank.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_model_io.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_mtd.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_nbs.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_nbs_endpoints.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_outofcore.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_outofcore_hmm.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_partition_graph.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_preprocessing.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_public_api.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_reference.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_sliding_window.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_stability.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_state_alignment_io.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_state_interpretation.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_state_metric_inference.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_state_results.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_state_scoring_io.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_state_selection.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_state_selection_io.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_state_stability_io.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_store_summary.py +0 -0
- {dfc_kit-1.0.2 → dfc_kit-1.0.3}/tests/unit/test_xcpd.py +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.3 - 2026-10-08
|
|
4
|
+
|
|
5
|
+
- CAP now defaults to within-run ROI z-scoring and full-data Lloyd KMeans;
|
|
6
|
+
previously the defaults were within-segment z-scoring and MiniBatchKMeans.
|
|
7
|
+
Isolated retained frames contribute when their run has at least two frames.
|
|
8
|
+
- Added `cap_state_maps` to average standardized ROI activity by assigned state.
|
|
9
|
+
To retain earlier CAP behavior, choose `standardization="segment"` and
|
|
10
|
+
`algorithm="minibatch"`; for stores, use
|
|
11
|
+
`--cap-standardization segment` and a materialized MiniBatch fit.
|
|
12
|
+
- Clarified that result storage is explicit and computations are not automatically
|
|
13
|
+
cached across calls. Memory-mapped reads can be disabled with `mmap=False`;
|
|
14
|
+
this changes file loading, not computation reuse or operating-system caching.
|
|
15
|
+
|
|
3
16
|
## 1.0.2 - 2026-09-11
|
|
4
17
|
|
|
5
18
|
- Added exact-feature selection for in-memory and disk-backed state models,
|
|
@@ -11,9 +11,10 @@ from dfckit.io import load_xcpd_run, discover_xcpd_runs
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
`TimeSeriesRun` carries ROI time series, original frame indices, and acquisition
|
|
14
|
-
metadata. XCP-D loaders preserve censor gaps. ETS
|
|
15
|
-
|
|
16
|
-
an
|
|
14
|
+
metadata. XCP-D loaders preserve censor gaps. ETS standardizes within each
|
|
15
|
+
retained segment. CAP standardizes within each run by default, with segment-wise
|
|
16
|
+
scaling available as an option. Both retain segment boundaries for temporal
|
|
17
|
+
summaries.
|
|
17
18
|
|
|
18
19
|
## Connectivity
|
|
19
20
|
|
|
@@ -6,6 +6,11 @@ whole-edge window-pattern and low-rank covariance endpoints, information
|
|
|
6
6
|
estimates, state fitting,
|
|
7
7
|
decoding, scoring, alignment, repeated-fit stability, and paired inference.
|
|
8
8
|
|
|
9
|
+
Commands do not automatically reuse previous computation results. Store and
|
|
10
|
+
model inputs are loaded only when explicitly supplied; output paths name saved
|
|
11
|
+
results, not cache directories. `build-store` and `fit-states` require new output
|
|
12
|
+
paths and fail if those paths already exist.
|
|
13
|
+
|
|
9
14
|
## Inspect and build a FeatureStore
|
|
10
15
|
|
|
11
16
|
```bash
|
|
@@ -24,6 +29,24 @@ dfc-kit build-store /path/to/xcpd /path/to/features.store \
|
|
|
24
29
|
All temporal methods preserve original frame indices and never cross a censor
|
|
25
30
|
gap. `--method` accepts `window-fc`, `cap`, `ets`, `mtd`, or `leida`.
|
|
26
31
|
|
|
32
|
+
For CAP, build a store of instantaneous ROI patterns and fit materialized Lloyd
|
|
33
|
+
KMeans without a second pooled standardization:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
dfc-kit build-store /path/to/xcpd /path/to/cap.store \
|
|
37
|
+
--atlas Schaefer200 --space MNI152NLin2009cAsym \
|
|
38
|
+
--method cap --cap-standardization run
|
|
39
|
+
dfc-kit fit-states /path/to/cap.store /path/to/models/cap-k5.model \
|
|
40
|
+
--method kmeans --n-states 5 --seed 20260818 \
|
|
41
|
+
--n-init 20 --max-iter 300 \
|
|
42
|
+
--fitting-mode materialized --algorithm lloyd --no-standardize-features
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`--cap-standardization` defaults to `run`: each ROI is standardized over all
|
|
46
|
+
retained frames of a run, while censor-bounded sequence boundaries remain.
|
|
47
|
+
Choose `segment` for the earlier per-segment scaling. The default `fit-states`
|
|
48
|
+
streaming path is MiniBatchKMeans, not the in-memory CAP Lloyd default.
|
|
49
|
+
|
|
27
50
|
## Fit, decode, and score states
|
|
28
51
|
|
|
29
52
|
```bash
|
|
@@ -47,7 +47,8 @@ future predictions from the returned model already use reference numbering.
|
|
|
47
47
|
CAP fits use the same `KMeansStateModel` contract but expose a domain-specific
|
|
48
48
|
alignment entry point. Use `align_cap_centroids` for CAP models. It defaults to Pearson matching because
|
|
49
49
|
CAP centres describe relative spatial co-activation configurations after
|
|
50
|
-
|
|
50
|
+
ROI standardization. This matching step is separate from the Euclidean distance
|
|
51
|
+
used to fit the clusters:
|
|
51
52
|
|
|
52
53
|
```python
|
|
53
54
|
cap_alignment = align_cap_centroids(reference_cap.model, candidate_cap.model)
|
|
@@ -186,39 +186,63 @@ materialized Lloyd KMeans; incompatible engines are rejected explicitly.
|
|
|
186
186
|
## CAP
|
|
187
187
|
|
|
188
188
|
```python
|
|
189
|
-
from dfckit.states import
|
|
189
|
+
from dfckit.states import (
|
|
190
|
+
cap_sequences,
|
|
191
|
+
cap_state_maps,
|
|
192
|
+
fit_cap_states,
|
|
193
|
+
predict_kmeans_states,
|
|
194
|
+
summarize_state_assignments,
|
|
195
|
+
)
|
|
190
196
|
|
|
197
|
+
features = cap_sequences(training_dataset)
|
|
191
198
|
fit = fit_cap_states(training_dataset, n_states=5, seed=20260818)
|
|
199
|
+
maps = cap_state_maps(features, fit.assignments)
|
|
200
|
+
metrics = summarize_state_assignments(fit.assignments)
|
|
201
|
+
|
|
202
|
+
unseen_features = cap_sequences(unseen_dataset)
|
|
203
|
+
unseen_assignments = predict_kmeans_states(fit.model, unseen_features)
|
|
192
204
|
```
|
|
193
205
|
|
|
194
|
-
CAP
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
CAP uses every retained instantaneous ROI pattern. By default, each ROI is
|
|
207
|
+
centered and scaled over all retained frames of its run (`ddof=0`), with no
|
|
208
|
+
second scaling across participants. Runs need at least two retained frames;
|
|
209
|
+
isolated retained frames still contribute after run-wise scaling. Censor gaps
|
|
210
|
+
remain sequence boundaries for dwell and transition metrics. `fit_cap_states`
|
|
211
|
+
returns a `KMeansFitResult` and defaults to full-data Euclidean Lloyd KMeans
|
|
212
|
+
without PCA (`n_init=20`, `max_iter=300`). Set `algorithm="minibatch"` for
|
|
213
|
+
MiniBatchKMeans. `standardization="segment"` retains the earlier per-segment
|
|
214
|
+
scaling, which omits segments shorter than two frames.
|
|
215
|
+
|
|
216
|
+
`maps` is a read-only `(n_states, n_rois)` array: for each state, it averages
|
|
217
|
+
the standardized BOLD patterns of frames assigned that final label, with equal
|
|
218
|
+
weight per frame. A state with no assigned frames has a NaN map. These activity
|
|
219
|
+
maps are not FC matrices and do not change the fitted centers or prediction.
|
|
198
220
|
Use `align_cap_centroids` to align repeated CAP fits. Its default is Pearson
|
|
199
221
|
pattern matching; generic KMeans and HMM alignment continues to default to
|
|
200
222
|
standardized Euclidean distance.
|
|
201
223
|
|
|
202
224
|
For a disk-backed XCP-D cohort, build the equivalent CAP FeatureStore with
|
|
203
225
|
`dfc-kit build-store --method cap` or `write_cap_store`. The store uses the
|
|
204
|
-
source contract `cap:within-
|
|
205
|
-
and one sequence per uninterrupted retained segment. Its rows preserve
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
MiniBatchKMeans with `--no-standardize-features`:
|
|
226
|
+
source contract `cap:within-run-roi-zscore-ddof0`, one ROI feature per column,
|
|
227
|
+
and one sequence per uninterrupted retained segment. Its rows preserve original
|
|
228
|
+
frame indices. To fit the same geometry as the in-memory default, use
|
|
229
|
+
materialized Lloyd KMeans without pooled feature standardization:
|
|
209
230
|
|
|
210
231
|
```bash
|
|
211
232
|
dfc-kit fit-states cap.store models/cap-k5.model \
|
|
212
233
|
--method kmeans --n-states 5 --seed 20260818 \
|
|
213
234
|
--n-init 20 --max-iter 300 \
|
|
214
|
-
--fitting-mode materialized --algorithm
|
|
235
|
+
--fitting-mode materialized --algorithm lloyd \
|
|
215
236
|
--no-standardize-features
|
|
216
237
|
```
|
|
217
238
|
|
|
218
239
|
Materialized fitting loads the selected cohort and calls one complete
|
|
219
|
-
`
|
|
220
|
-
|
|
221
|
-
|
|
240
|
+
`KMeans.fit`; it is intended for moderate stores. The default CLI fitting path
|
|
241
|
+
uses bounded-memory streaming `MiniBatchKMeans.partial_fit`, so select
|
|
242
|
+
`--fitting-mode materialized --algorithm lloyd` explicitly for this CAP fit.
|
|
243
|
+
|
|
244
|
+
For background, see the [original full-frame CAP study](https://pmc.ncbi.nlm.nih.gov/articles/PMC3913885/)
|
|
245
|
+
and an [ROI-pattern CAP study](https://pmc.ncbi.nlm.nih.gov/articles/PMC4386757/).
|
|
222
246
|
|
|
223
247
|
## LEiDA states
|
|
224
248
|
|
|
@@ -14,6 +14,15 @@ FeatureStore accepts only the current manifest schema. Every sequence record
|
|
|
14
14
|
therefore has an explicit acquisition field, which may be `None` when no
|
|
15
15
|
acquisition label is available.
|
|
16
16
|
|
|
17
|
+
## Storage versus computation caches
|
|
18
|
+
|
|
19
|
+
dfc-kit does not automatically cache computed results across calls. Numerical
|
|
20
|
+
analysis calls recompute their outputs; disk storage is explicit through store
|
|
21
|
+
writers and artifact save/load functions. Creating a store at an existing path
|
|
22
|
+
raises an error rather than silently reusing its contents. To compute features
|
|
23
|
+
without writing them to disk, use the estimator's in-memory API, such as
|
|
24
|
+
`SlidingWindowFC.transform(run)`, instead of a store writer.
|
|
25
|
+
|
|
17
26
|
## Stream sliding-window FC
|
|
18
27
|
|
|
19
28
|
```python
|
|
@@ -63,9 +72,8 @@ is required; that path avoids edge materialization.
|
|
|
63
72
|
## Stream CAP
|
|
64
73
|
|
|
65
74
|
CAP stores contain instantaneous ROI patterns, not ROI-pair edges. The writer
|
|
66
|
-
standardizes every ROI
|
|
67
|
-
|
|
68
|
-
bounded chunks:
|
|
75
|
+
standardizes every ROI across all retained frames of each run with population
|
|
76
|
+
standard deviation (`ddof=0`) and writes rows in bounded chunks:
|
|
69
77
|
|
|
70
78
|
```python
|
|
71
79
|
from dfckit.storage import append_cap, write_cap_store
|
|
@@ -80,14 +88,16 @@ store = write_cap_store(
|
|
|
80
88
|
append_cap(store, another_run, chunk_size=256)
|
|
81
89
|
```
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
Runs need at least two retained frames. Each retained censor-bounded segment
|
|
92
|
+
becomes its own FeatureStore sequence, including isolated retained frames.
|
|
93
|
+
Every stored row retains its original frame index in both
|
|
94
|
+
`sample_start_indices` and `sample_end_indices`, so later state metrics do not
|
|
95
|
+
join censor gaps. The source contract is `cap:within-run-roi-zscore-ddof0`,
|
|
96
|
+
and the feature keys are the ROI names in input order. Set
|
|
97
|
+
`standardization="segment"` in `write_cap_store` or `append_cap` to use the
|
|
98
|
+
earlier per-segment scaling and omit segments shorter than two frames; its
|
|
99
|
+
source contract is `cap:within-segment-roi-zscore-ddof0`. Standardization
|
|
100
|
+
precedes chunking, so `chunk_size` does not change CAP values.
|
|
91
101
|
|
|
92
102
|
## Stream LEiDA
|
|
93
103
|
|
|
@@ -129,6 +139,11 @@ sequence_part = store.read_sequence(
|
|
|
129
139
|
append_window_fc(store, another_run, estimator, chunk_size=128)
|
|
130
140
|
```
|
|
131
141
|
|
|
142
|
+
`iter_chunks()` uses read-only memory mapping by default. Pass `mmap=False` to
|
|
143
|
+
load each chunk into ordinary NumPy arrays instead. This controls how existing
|
|
144
|
+
files are read, not whether previous computations are reused. Neither mode
|
|
145
|
+
disables the operating system's filesystem cache.
|
|
146
|
+
|
|
132
147
|
`read_dataset()` reconstructs ordinary `FeatureSequenceDataset` objects for
|
|
133
148
|
existing in-memory state APIs. For genuinely large analyses, iterate chunks or
|
|
134
149
|
read selected sequences instead of reconstructing the full dataset.
|
|
@@ -266,7 +281,7 @@ fit = fit_kmeans_store_materialized(
|
|
|
266
281
|
seed=20260818,
|
|
267
282
|
n_init=20,
|
|
268
283
|
max_iter=300,
|
|
269
|
-
algorithm="
|
|
284
|
+
algorithm="lloyd",
|
|
270
285
|
standardize_features=False,
|
|
271
286
|
)
|
|
272
287
|
```
|
|
@@ -275,8 +290,8 @@ Materialized fitting loads the selected cohort into memory; it is not an
|
|
|
275
290
|
out-of-core operation. `algorithm="lloyd"` calls scikit-learn `KMeans.fit`,
|
|
276
291
|
whereas `algorithm="minibatch"` calls `MiniBatchKMeans.fit`. Both retain the
|
|
277
292
|
FeatureStore source contract, feature identities, and selected fit subjects in
|
|
278
|
-
the returned model. CAP rows have already been
|
|
279
|
-
|
|
293
|
+
the returned model. CAP rows have already been standardized within run (or
|
|
294
|
+
within segment when requested), so CAP fitting normally uses
|
|
280
295
|
`standardize_features=False`; enabling it adds a second pooled feature
|
|
281
296
|
standardization and changes the fitted geometry.
|
|
282
297
|
|
|
@@ -285,8 +300,8 @@ The two KMeans paths have distinct numerical and memory semantics:
|
|
|
285
300
|
| Mode | Algorithm | Feature rows in RAM | Estimator operation | Typical use |
|
|
286
301
|
| --- | --- | --- | --- | --- |
|
|
287
302
|
| `streaming` | `minibatch` only | bounded chunks | repeated `partial_fit` passes | large stores |
|
|
288
|
-
| `materialized` | `lloyd` | selected cohort | one `KMeans.fit` call |
|
|
289
|
-
| `materialized` | `minibatch` | selected cohort | one `MiniBatchKMeans.fit` call |
|
|
303
|
+
| `materialized` | `lloyd` | selected cohort | one `KMeans.fit` call | default in-memory CAP fit |
|
|
304
|
+
| `materialized` | `minibatch` | selected cohort | one `MiniBatchKMeans.fit` call | optional MiniBatch fit |
|
|
290
305
|
|
|
291
306
|
The streaming path is not a hidden alias for `MiniBatchKMeans.fit`: its
|
|
292
307
|
initialization sample, chunk traversal, and repeated `partial_fit` updates are
|
|
@@ -132,6 +132,7 @@ tests/regression/test_nbs_independent_reference.py
|
|
|
132
132
|
tests/regression/test_nbs_reference.py
|
|
133
133
|
tests/unit/__init__.py
|
|
134
134
|
tests/unit/test_artifact_helpers.py
|
|
135
|
+
tests/unit/test_cap.py
|
|
135
136
|
tests/unit/test_cli.py
|
|
136
137
|
tests/unit/test_correlation.py
|
|
137
138
|
tests/unit/test_cross_validation.py
|
|
@@ -9,6 +9,39 @@ from ._arrays import readonly_copy as _readonly
|
|
|
9
9
|
from .data import TimeSeriesRun
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
def _roi_zscore(values: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
13
|
+
scale = values.std(axis=0, ddof=0)
|
|
14
|
+
scale = np.where(scale < 1e-8, 1.0, scale)
|
|
15
|
+
return (values - values.mean(axis=0)) / scale
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _standardized_samples(
|
|
19
|
+
run: TimeSeriesRun,
|
|
20
|
+
*,
|
|
21
|
+
standardization: str = "run",
|
|
22
|
+
method_name: str = "CAP",
|
|
23
|
+
) -> tuple[NDArray[np.float64], NDArray[np.int64], NDArray[np.int64]]:
|
|
24
|
+
"""Standardize retained ROI samples while preserving censor boundaries.
|
|
25
|
+
|
|
26
|
+
Run scaling uses all retained frames together, including isolated frames.
|
|
27
|
+
Segment scaling omits segments shorter than two frames.
|
|
28
|
+
"""
|
|
29
|
+
if standardization == "segment":
|
|
30
|
+
return _segment_standardized_samples(run, method_name=method_name)
|
|
31
|
+
if standardization != "run":
|
|
32
|
+
raise ValueError("standardization must be 'run' or 'segment'")
|
|
33
|
+
if run.n_frames < 2:
|
|
34
|
+
raise ValueError(f"{method_name} requires at least two retained frames per run")
|
|
35
|
+
values = _roi_zscore(run.values)
|
|
36
|
+
if not np.isfinite(values).all():
|
|
37
|
+
raise ValueError(f"standardized {method_name} samples contain non-finite values")
|
|
38
|
+
segment_ids = np.concatenate([
|
|
39
|
+
np.full(len(positions), segment_id, dtype=np.int64)
|
|
40
|
+
for segment_id, positions in enumerate(run.segments())
|
|
41
|
+
])
|
|
42
|
+
return _readonly(values), _readonly(run.original_indices), _readonly(segment_ids)
|
|
43
|
+
|
|
44
|
+
|
|
12
45
|
def _segment_standardized_samples(
|
|
13
46
|
run: TimeSeriesRun,
|
|
14
47
|
*,
|
|
@@ -28,9 +61,7 @@ def _segment_standardized_samples(
|
|
|
28
61
|
if len(positions) < 2:
|
|
29
62
|
continue
|
|
30
63
|
values = run.values[positions]
|
|
31
|
-
|
|
32
|
-
scale = np.where(scale < 1e-8, 1.0, scale)
|
|
33
|
-
samples.append((values - values.mean(axis=0)) / scale)
|
|
64
|
+
samples.append(_roi_zscore(values))
|
|
34
65
|
original_indices.append(run.original_indices[positions])
|
|
35
66
|
segment_ids.append(np.full(len(positions), segment_id, dtype=np.int64))
|
|
36
67
|
|
|
@@ -71,6 +71,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
71
71
|
build.add_argument("--window-step", type=int, default=8)
|
|
72
72
|
build.add_argument("--taper", choices=("hamming", "uniform"), default="hamming")
|
|
73
73
|
build.add_argument("--minimum-segment-length", type=int, default=20)
|
|
74
|
+
build.add_argument(
|
|
75
|
+
"--cap-standardization", choices=("run", "segment"), default="run",
|
|
76
|
+
help="CAP ROI z-score scope; run uses all retained frames per acquisition",
|
|
77
|
+
)
|
|
74
78
|
|
|
75
79
|
information = subparsers.add_parser(
|
|
76
80
|
"fixed-information",
|
|
@@ -164,6 +164,7 @@ def build_store(namespace: argparse.Namespace) -> dict[str, object]:
|
|
|
164
164
|
store = write_cap_store(
|
|
165
165
|
namespace.output,
|
|
166
166
|
dataset.runs,
|
|
167
|
+
standardization=namespace.cap_standardization,
|
|
167
168
|
chunk_size=namespace.chunk_size,
|
|
168
169
|
dtype=namespace.dtype,
|
|
169
170
|
)
|
|
@@ -11,7 +11,7 @@ from .alignment import (
|
|
|
11
11
|
relabel_gaussian_hmm_model,
|
|
12
12
|
relabel_kmeans_model,
|
|
13
13
|
)
|
|
14
|
-
from .cap import cap_sequences, fit_cap_states
|
|
14
|
+
from .cap import cap_sequences, cap_state_maps, fit_cap_states
|
|
15
15
|
from .cross_validation import SubjectValidationFold, make_subject_validation_folds
|
|
16
16
|
from .data import (
|
|
17
17
|
FeatureSequence,
|
|
@@ -77,6 +77,7 @@ __all__ = [
|
|
|
77
77
|
"apply_gaussian_hmm_alignment",
|
|
78
78
|
"apply_state_alignment",
|
|
79
79
|
"cap_sequences",
|
|
80
|
+
"cap_state_maps",
|
|
80
81
|
"compare_state_model_scores",
|
|
81
82
|
"fit_cap_states",
|
|
82
83
|
"fit_gaussian_hmm_states",
|
|
@@ -193,9 +193,8 @@ def align_cap_centroids(
|
|
|
193
193
|
) -> StateAlignment:
|
|
194
194
|
"""Align CAP state centres, using pattern-shape matching by default.
|
|
195
195
|
|
|
196
|
-
CAP centres
|
|
197
|
-
|
|
198
|
-
spatial configuration without treating a proportional amplitude change as
|
|
196
|
+
CAP centres describe ROI-wise standardized activity. Pearson matching
|
|
197
|
+
compares the spatial configuration without treating a proportional amplitude change as
|
|
199
198
|
a different CAP state. Pass ``metric="euclidean"`` when amplitude should
|
|
200
199
|
also contribute to the CAP pairing.
|
|
201
200
|
"""
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Co-activation pattern feature construction and KMeans fitting."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from numpy.typing import NDArray
|
|
7
|
+
|
|
8
|
+
from .._arrays import readonly_copy
|
|
9
|
+
from .._preprocessing import _standardized_samples
|
|
10
|
+
from ..data import TimeSeriesDataset
|
|
11
|
+
from .data import (
|
|
12
|
+
FeatureSequence,
|
|
13
|
+
FeatureSequenceDataset,
|
|
14
|
+
StateAssignments,
|
|
15
|
+
_segment_feature_sequences,
|
|
16
|
+
)
|
|
17
|
+
from .kmeans import KMeansFitResult, fit_kmeans_states
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def cap_sequences(
|
|
21
|
+
dataset: TimeSeriesDataset,
|
|
22
|
+
*,
|
|
23
|
+
standardization: str = "run",
|
|
24
|
+
) -> FeatureSequenceDataset:
|
|
25
|
+
"""Create instantaneous ROI patterns from all retained frames.
|
|
26
|
+
|
|
27
|
+
Each run is ROI-wise z-scored over its retained frames by default (ddof=0).
|
|
28
|
+
``standardization='segment'`` instead scales each contiguous segment and
|
|
29
|
+
omits singleton segments. Both modes keep censor gaps as sequence boundaries.
|
|
30
|
+
"""
|
|
31
|
+
dataset.require_subject_ids("CAP state modeling")
|
|
32
|
+
feature_keys = tuple((roi,) for roi in dataset.roi_names)
|
|
33
|
+
sequences: list[FeatureSequence] = []
|
|
34
|
+
for run in dataset.runs:
|
|
35
|
+
assert run.subject is not None
|
|
36
|
+
standardized, original_indices, segment_ids = _standardized_samples(
|
|
37
|
+
run, standardization=standardization, method_name="CAP"
|
|
38
|
+
)
|
|
39
|
+
sequences.extend(
|
|
40
|
+
_segment_feature_sequences(
|
|
41
|
+
run, standardized, original_indices, original_indices, segment_ids,
|
|
42
|
+
feature_keys=feature_keys,
|
|
43
|
+
source_contract=f"cap:within-{standardization}-roi-zscore-ddof0",
|
|
44
|
+
interval=run.tr,
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
return FeatureSequenceDataset(sequences)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def fit_cap_states(
|
|
51
|
+
dataset: TimeSeriesDataset,
|
|
52
|
+
*,
|
|
53
|
+
n_states: int,
|
|
54
|
+
seed: int,
|
|
55
|
+
n_init: int = 20,
|
|
56
|
+
max_iter: int = 300,
|
|
57
|
+
standardization: str = "run",
|
|
58
|
+
algorithm: str = "lloyd",
|
|
59
|
+
batch_size: int = 4096,
|
|
60
|
+
reassignment_ratio: float = 0.01,
|
|
61
|
+
) -> KMeansFitResult:
|
|
62
|
+
"""Fit Euclidean KMeans to standardized instantaneous ROI patterns.
|
|
63
|
+
|
|
64
|
+
The default uses full-batch Lloyd KMeans, with no PCA or second feature
|
|
65
|
+
standardization. ``algorithm='minibatch'`` enables approximate fitting.
|
|
66
|
+
Use :func:`cap_state_maps` for exact frame-average activity maps under the
|
|
67
|
+
final assignments, rather than assuming estimated centers equal those maps.
|
|
68
|
+
"""
|
|
69
|
+
return fit_kmeans_states(
|
|
70
|
+
cap_sequences(dataset, standardization=standardization),
|
|
71
|
+
n_states=n_states,
|
|
72
|
+
seed=seed,
|
|
73
|
+
n_init=n_init,
|
|
74
|
+
max_iter=max_iter,
|
|
75
|
+
algorithm=algorithm,
|
|
76
|
+
standardize_features=False,
|
|
77
|
+
batch_size=batch_size,
|
|
78
|
+
reassignment_ratio=reassignment_ratio,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def cap_state_maps(
|
|
83
|
+
features: FeatureSequenceDataset,
|
|
84
|
+
assignments: StateAssignments,
|
|
85
|
+
) -> NDArray[np.float64]:
|
|
86
|
+
"""Average standardized ROI activity over the frames assigned to each CAP.
|
|
87
|
+
|
|
88
|
+
Returns a read-only states-by-ROIs array in ``features.feature_keys`` order.
|
|
89
|
+
Frames have equal weight; states with no assigned frames have NaN rows.
|
|
90
|
+
Sequences may be reordered, but acquisition identities and frame indices
|
|
91
|
+
must match. These descriptive maps do not change the fitted model centers.
|
|
92
|
+
"""
|
|
93
|
+
if not features.source_contract.startswith("cap:") or any(
|
|
94
|
+
len(key) != 1 for key in features.feature_keys
|
|
95
|
+
):
|
|
96
|
+
raise ValueError("CAP maps require CAP ROI features from cap_sequences or a CAP store")
|
|
97
|
+
if features.source_contract != assignments.source_contract:
|
|
98
|
+
raise ValueError("CAP maps require matching standardization source contracts")
|
|
99
|
+
left, right = features.sample_interval_seconds, assignments.sample_interval_seconds
|
|
100
|
+
if (left is None) != (right is None) or (
|
|
101
|
+
left is not None and right is not None and not np.isclose(left, right, rtol=0, atol=1e-9)
|
|
102
|
+
):
|
|
103
|
+
raise ValueError("CAP maps require matching sample intervals")
|
|
104
|
+
|
|
105
|
+
def identity(sequence):
|
|
106
|
+
return sequence.subject, sequence.session, sequence.acquisition_id, sequence.segment_id
|
|
107
|
+
|
|
108
|
+
labels_by_sequence = {identity(sequence): sequence for sequence in assignments.sequences}
|
|
109
|
+
if {identity(sequence) for sequence in features.sequences} != set(labels_by_sequence):
|
|
110
|
+
raise ValueError("CAP maps require matching sequence identities")
|
|
111
|
+
sums = np.zeros((assignments.n_states, len(features.feature_keys)), dtype=float)
|
|
112
|
+
counts = np.zeros(assignments.n_states, dtype=np.int64)
|
|
113
|
+
for sequence in features.sequences:
|
|
114
|
+
assigned = labels_by_sequence[identity(sequence)]
|
|
115
|
+
if not (
|
|
116
|
+
np.array_equal(sequence.sample_start_indices, assigned.sample_start_indices)
|
|
117
|
+
and np.array_equal(sequence.sample_end_indices, assigned.sample_end_indices)
|
|
118
|
+
):
|
|
119
|
+
raise ValueError("CAP maps require matching frame indices")
|
|
120
|
+
np.add.at(sums, assigned.labels, sequence.values)
|
|
121
|
+
counts += np.bincount(assigned.labels, minlength=assignments.n_states)
|
|
122
|
+
maps = np.full(sums.shape, np.nan)
|
|
123
|
+
np.divide(sums, counts[:, None], out=maps, where=counts[:, None] > 0)
|
|
124
|
+
return readonly_copy(maps)
|
|
@@ -8,7 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
from numpy.typing import NDArray
|
|
10
10
|
|
|
11
|
-
from .._preprocessing import
|
|
11
|
+
from .._preprocessing import _standardized_samples
|
|
12
12
|
from ..connectivity._edge_products import edge_products
|
|
13
13
|
from ..connectivity.correlation import edge_index, weighted_correlation
|
|
14
14
|
from ..connectivity.instantaneous import _InstantaneousEstimator, _InstantaneousRows
|
|
@@ -211,20 +211,23 @@ def append_cap(
|
|
|
211
211
|
store: FeatureStore,
|
|
212
212
|
run: TimeSeriesRun,
|
|
213
213
|
*,
|
|
214
|
+
standardization: str = "run",
|
|
214
215
|
chunk_size: int = 128,
|
|
215
216
|
) -> None:
|
|
216
|
-
"""Append segment-standardized
|
|
217
|
+
"""Append run- or segment-standardized ROI patterns in bounded chunks."""
|
|
217
218
|
if run.subject is None:
|
|
218
219
|
raise ValueError("stored CAP requires a subject identifier")
|
|
219
220
|
size = _validated_chunk_size(chunk_size)
|
|
220
221
|
keys = _roi_feature_keys(run.roi_names)
|
|
221
|
-
contract = "cap:within-
|
|
222
|
+
contract = f"cap:within-{standardization}-roi-zscore-ddof0"
|
|
222
223
|
store.require_contract(
|
|
223
224
|
feature_keys=keys,
|
|
224
225
|
source_contract=contract,
|
|
225
226
|
sample_interval_seconds=run.tr,
|
|
226
227
|
)
|
|
227
|
-
standardized, original, segment_ids =
|
|
228
|
+
standardized, original, segment_ids = _standardized_samples(
|
|
229
|
+
run, standardization=standardization, method_name="CAP"
|
|
230
|
+
)
|
|
228
231
|
_append_segment_rows(
|
|
229
232
|
store, run, standardized, original, original, segment_ids, chunk_size=size,
|
|
230
233
|
)
|
|
@@ -234,21 +237,24 @@ def write_cap_store(
|
|
|
234
237
|
root: str | Path,
|
|
235
238
|
runs: Sequence[TimeSeriesRun],
|
|
236
239
|
*,
|
|
240
|
+
standardization: str = "run",
|
|
237
241
|
chunk_size: int = 128,
|
|
238
242
|
dtype: str | np.dtype = "float64",
|
|
239
243
|
) -> FeatureStore:
|
|
240
|
-
"""
|
|
244
|
+
"""Store CAP patterns, using run-wise ROI z-scores by default."""
|
|
245
|
+
if standardization not in {"run", "segment"}:
|
|
246
|
+
raise ValueError("standardization must be 'run' or 'segment'")
|
|
241
247
|
dataset = TimeSeriesDataset(runs)
|
|
242
248
|
dataset.require_subject_ids("stored CAP")
|
|
243
249
|
store = FeatureStore.create(
|
|
244
250
|
root,
|
|
245
251
|
feature_keys=_roi_feature_keys(dataset.roi_names),
|
|
246
|
-
source_contract="cap:within-
|
|
252
|
+
source_contract=f"cap:within-{standardization}-roi-zscore-ddof0",
|
|
247
253
|
sample_interval_seconds=dataset.tr,
|
|
248
254
|
dtype=dtype,
|
|
249
255
|
)
|
|
250
256
|
for run in dataset.runs:
|
|
251
|
-
append_cap(store, run, chunk_size=chunk_size)
|
|
257
|
+
append_cap(store, run, standardization=standardization, chunk_size=chunk_size)
|
|
252
258
|
return store
|
|
253
259
|
|
|
254
260
|
|