confusius.extract¶
extract ¶
Signal extraction from fUSI data.
Modules:
-
labels–Extraction of region-aggregated signals using integer label maps.
-
mask–Extraction of signals using boolean masks.
-
reconstruction–Reconstruction of VoxelData arrays from N-D signals using masks.
Functions:
-
extract_with_labels–Extract region-aggregated signals from a VoxelData array.
-
extract_with_mask–Extract signals from a VoxelData array using a binary mask.
-
unmask–Reconstruct a VoxelData array from N-D signals using a mask.
extract_with_labels ¶
extract_with_labels(
data: DataArray,
labels: DataArray,
reduction: Literal[
"mean", "sum", "median", "min", "max", "var", "std"
] = "mean",
) -> DataArray
Extract region-aggregated signals from a VoxelData array.
For each unique non-zero label in labels, applies reduction across all voxels
belonging to that region. The native voxel dimensions (k/j/i) are collapsed
into a single region dimension.
Parameters:
-
(data¶DataArray) –VoxelData array with native voxel dims
k/j/iand aVoxelToWorldIndex, plus any number of non-spatial dimensions (e.g.,time,pose). Seeensure_voxeldata. -
(labels¶DataArray) –Integer label map sharing
data's voxel grid, in one of two formats:- Flat label map: Spatial dims only, e.g.
(k, j, i). Background voxels labeled0; each unique non-zero integer identifies a distinct, non-overlapping region. Theregioncoordinate of the output holds the integer label values. - Stacked mask format: Has a leading
maskdimension followed by spatial dims, e.g.(mask, k, j, i). Each layer has exactly one non-zero value identifying its own voxels, and regions may overlap; the non-zero value itself is not used to identify the layer, so it may repeat across layers (e.g. the same region id for left/right hemisphere layers). Theregioncoordinate of the output holds themaskcoordinate values (e.g., region label).
- Flat label map: Spatial dims only, e.g.
-
(reduction¶(mean, sum, median, min, max, var, std), default:"mean") –Aggregation function applied across voxels in each region.
Returns:
-
DataArray–Array with spatial dimensions replaced by a
regiondimension. All non-spatial dimensions are preserved.For example (flat label map):
(time, k, j, i)→(time, region)(time, pose, k, j, i)→(time, pose, region)(k, j, i)→(region,)
Raises:
-
ValueError–If
labelsordataisn't a VoxelData array, iflabels's voxel grid doesn't matchdata's, ifreductionis not a valid option, or iflabelscontains no non-zero values. -
TypeError–If
labelsis not integer dtype.
Notes
Uses flox for efficient, lazy groupby reductions on Dask-backed arrays. Data can be chunked along any dimension without restriction.
Examples:
>>> import numpy as np
>>> from confusius.extract import extract_with_labels
>>> from confusius.xarray import create_voxeldata
>>>
>>> # 3D+t data: (time, k, j, i)
>>> data = create_voxeldata(
... np.random.randn(100, 10, 20, 30),
... dims=("time", "k", "j", "i"),
... dt=0.5,
... spacing=(1.0, 1.0, 1.0),
... )
>>> labels = create_voxeldata(
... np.zeros((10, 20, 30), dtype=int),
... dims=("k", "j", "i"),
... spacing=(1.0, 1.0, 1.0),
... )
>>> labels[0, :, :] = 1 # Region 1: first k-slice.
>>> labels[1, :, :] = 2 # Region 2: second k-slice.
>>> signals = extract_with_labels(data, labels)
>>> signals.dims
('time', 'region')
>>> signals.coords["region"].values
array([1, 2])
>>>
>>> # Stacked mask format from the atlas accessor's get_masks. Left/right hemisphere
>>> # layers share a region id, but each is disambiguated by its `mask` coord.
>>> mask = atlas_fusi.atlas.get_masks(["VISp", "VISp"], sides=["left", "right"])
>>> signals = extract_with_labels(data, mask)
>>> signals.coords["region"].values
array(['VISp_L', 'VISp_R'], dtype=object)
extract_with_mask ¶
Extract signals from a VoxelData array using a binary mask.
This function flattens mask's native voxel dimensions (k/j/i) into a
single space dimension, while preserving all other dimensions of data (e.g.,
time, pose).
Parameters:
-
(data¶DataArray) –VoxelData array with native voxel dims
k/j/iand aVoxelToWorldIndex, plus any number of non-spatial dimensions (e.g.,time,pose). Seeensure_voxeldata. -
(mask¶DataArray) –Mask defining which voxels to extract, sharing
data's voxel grid. Must have boolean dtype, or integer dtype with exactly one non-zero value (0 = background, one region id = foreground). The latter format is produced byget_masks.
Returns:
-
DataArray–Array with
k/j/iflattened into aspacedimension. All non-spatial dimensions are preserved. Thespacedimension has a MultiIndex storing spatial coordinates.(time, k, j, i)→(time, space)(time, pose, k, j, i)→(time, pose, space)(k, j, i)→(space,)
For simple round-trip reconstruction, use
.unstack("space")which re-creates the original DataArray using the smallest bounding box containing the masked voxels. For full mask shape reconstruction, useconfusius.extract.unmask.
Raises:
-
ValueError–If
maskordataisn't a VoxelData array, or ifmask's voxel grid doesn't matchdata's. -
TypeError–If
maskis not boolean dtype (or a single-label integer dtype).
Examples:
>>> import numpy as np
>>> from confusius.extract import extract_with_mask
>>> from confusius.xarray import create_voxeldata
>>>
>>> # 3D+t data: (time, k, j, i)
>>> data = create_voxeldata(
... np.random.randn(100, 10, 20, 30),
... dims=("time", "k", "j", "i"),
... dt=0.5,
... spacing=(1.0, 1.0, 1.0),
... )
>>> mask = create_voxeldata(
... np.random.rand(10, 20, 30) > 0.5,
... dims=("k", "j", "i"),
... spacing=(1.0, 1.0, 1.0),
... )
>>> signals = extract_with_mask(data, mask)
>>> signals.dims
('time', 'space')
>>>
>>> # 3D+t data with extra dim: (time, pose, k, j, i)
>>> pose_data = create_voxeldata(
... np.random.randn(100, 5, 10, 20, 30),
... dims=("time", "pose", "k", "j", "i"),
... dt=0.5,
... spacing=(1.0, 1.0, 1.0),
... )
>>> pose_signals = extract_with_mask(pose_data, mask)
>>> pose_signals.dims
('time', 'pose', 'space')
unmask ¶
unmask(
signals: ndarray | DataArray,
mask: DataArray,
new_dims: list[str] | None = None,
new_dims_coords: dict[str, ndarray] | None = None,
attrs: dict | None = None,
fill_value: float = 0.0,
) -> DataArray
Reconstruct a VoxelData array from N-D signals using a mask.
Parameters:
-
(signals¶ndarray or DataArray) –Array with shape
(..., space)where...can be any number of dimensions. The last dimension must correspond to masked voxels.- If
signalsis a DataArray, it must have aspacedimension as the last dimension. All other dimensions and their coordinates are preserved. - If
signalsis a Numpy array, you can specify names and coordinates for the leading dimensions usingnew_dimsandnew_dims_coords. If not provided, dimensions are named["dim_0", "dim_1", ...]with integer coordinates.
- If
-
(mask¶DataArray) –VoxelData mask used for the original extraction. Provides spatial dimensions, coordinates, and
VoxelToWorldIndexfor reconstruction. Must be either boolean dtype, or integer dtype with exactly one non-zero value (0 = background, one region id = foreground). Spatial dimensions and coordinates must match the original data. -
(new_dims¶list of str, default:None) –Names for leading dimensions when
signalsis a Numpy array. Must match the number of leading dimensions(ndim - 1). If not provided, uses["dim_0", "dim_1", ...]. Ignored ifsignalsis a DataArray. -
(new_dims_coords¶dict[str, ndarray], default:None) –Coordinates for leading dimensions when
signalsis a Numpy array. Keys must match dimension names innew_dims. If not provided, uses integer indices for all dimensions. Ignored ifsignalsis a DataArray. -
(attrs¶dict, default:None) –Attributes to attach to the output DataArray.
-
(fill_value¶float, default:0.0) –Value to fill in non-masked voxels.
Returns:
-
DataArray–Reconstructed VoxelData array with shape
(..., *mask.dims), where spatial dimensions, coordinates, and the derived worldz/y/xcoordinates come from the mask.
Raises:
-
ValueError–If
maskisn't a valid VoxelData array, ifsignalsshape doesn't matchmask, or ifnew_dims/new_dims_coordsare inconsistent withsignalsshape. -
TypeError–If
maskis not boolean dtype (or a single-label integer dtype).
Examples:
>>> import numpy as np
>>> from confusius.extract import extract_with_mask, unmask
>>> from confusius.xarray import create_voxeldata
>>> from sklearn.cluster import KMeans
>>>
>>> data = create_voxeldata(
... np.random.rand(10, 4, 5, 6),
... dims=("time", "k", "j", "i"),
... dt=0.5,
... spacing=(1.0, 1.0, 1.0),
... )
>>> mask = create_voxeldata(
... np.random.rand(4, 5, 6) > 0.5, dims=("k", "j", "i"), spacing=(1.0, 1.0, 1.0)
... )
>>>
>>> # Extract signals into a (time, space) matrix, then cluster voxel time-courses.
>>> # Clustering needs the flat voxel axis as samples, so this can't be done
>>> # directly on the gridded (k, j, i) array.
>>> signals = extract_with_mask(data, mask)
>>> labels = KMeans(n_clusters=3, n_init="auto").fit_predict(signals.values.T)
>>>
>>> # Unmask - reconstruct the cluster labels as a spatial map, no extra dims
>>> cluster_map = unmask(labels, mask, fill_value=-1)
>>> cluster_map.dims
('k', 'j', 'i')
>>>
>>> # Unmask - two extra dims, with custom coords
>>> pose_data = np.random.randn(5, 3, signals.sizes["space"]) # (component, pose, space)
>>> spatial_pose = unmask(
... pose_data,
... mask,
... new_dims=["component", "pose"],
... new_dims_coords={"component": [1, 2, 3, 4, 5], "pose": [0, 1, 2]},
... )
>>> spatial_pose.dims
('component', 'pose', 'k', 'j', 'i')