--- title: Working with Volumes and Vectors output: rmarkdown::html_vignette vignette: | %\VignetteIndexEntry{Working with Volumes and Vectors} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} params: family: red preset: homage css: albers.css resource_files: - albers.css - albers.js includes: in_header: |- --- ```{r setup, include = FALSE} if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("albersdown", quietly = TRUE)) ggplot2::theme_set(albersdown::theme_albers(family = params$family, preset = params$preset)) knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE ) suppressPackageStartupMessages(library(neuroim2)) ``` `neuroim2` has two basic data containers you reach for constantly: `NeuroVol` for one 3D image and `NeuroVec` for a 4D stack of volumes. This article shows how to move between them, inspect geometry, and extract the small pieces you actually analyze. ## Start with a single volume Use `read_vol()` when the file is a 3D image or when you want one volume at a time. ```{r read-volume} vol_file <- system.file("extdata", "global_mask2.nii.gz", package = "neuroim2") vol <- read_vol(vol_file) dim(vol) spacing(vol) origin(vol) ``` ```{r volume-check} stopifnot(length(dim(vol)) == 3L) stopifnot(all(spacing(vol) > 0)) ``` The returned object behaves like an array, but it also carries a `NeuroSpace` so you can keep voxel values tied to real spatial coordinates. ```{r inspect-space} space(vol) vol[1, 1, 1] ``` ## Read a 4D time series Use `read_vec()` when the file contains a time series or another stack of aligned volumes. ```{r read-vector} vec_file <- system.file("extdata", "global_mask_v4.nii", package = "neuroim2") vec <- read_vec(vec_file) dim(vec) vec ``` ```{r vector-check} stopifnot(length(dim(vec)) == 4L) stopifnot(dim(vec)[4] > 1L) ``` A `NeuroVec` still has one shared 3D spatial frame. The fourth dimension is the axis you usually split, subset, and summarize. ## Pull out the volumes you need `sub_vector()` is the direct way to keep only selected timepoints. ```{r subset-vector} first_two <- sub_vector(vec, 1:2) dim(first_two) ``` ```{r subset-check} stopifnot(dim(first_two)[4] == 2L) ``` ## Convert to a matrix when you need table-like work `as.matrix()` is useful when you want one row per voxel and one column per timepoint. ```{r matrix-view} mat <- as.matrix(vec) dim(mat) mat[1:4, 1:2] ``` ```{r matrix-check} stopifnot(nrow(mat) == prod(dim(vec)[1:3])) stopifnot(ncol(mat) == dim(vec)[4]) ``` That representation is convenient for temporal summaries, scaling, or passing the data into general R modeling code. ## Extract one voxel or one ROI time series For a single voxel, use `series()`. ```{r voxel-series} voxel_ts <- series(vec, 12, 12, 12) voxel_ts ``` For a spatial region, create an ROI and use `series_roi()`. ```{r roi-series} roi <- spherical_roi(drop(first_two[[1]]), c(12, 12, 12), radius = 6) roi_ts <- series_roi(vec, roi) dim(values(roi_ts)) ``` ```{r roi-check} stopifnot(length(roi) > 0L) stopifnot(nrow(values(roi_ts)) == dim(vec)[4]) ``` ## Switch to sparse storage when the support matters If only a subset of voxels should be considered present, read through a mask and keep the result sparse. ```{r sparse-vector} mask_vol <- read_vol(vec_file) > 0 sparse_vec <- read_vec(vec_file, mask = mask_vol) class(sparse_vec) dim(sparse_vec) ``` ```{r sparse-check} stopifnot(inherits(sparse_vec, "SparseNeuroVec")) stopifnot(identical(dim(sparse_vec), dim(vec))) ``` This is the right move when absent voxels should be treated as missing support, not as literal zeros. ## Where to go next - `vignette("ChoosingBackends")` for storage tradeoffs - `vignette("coordinate-systems")` for affine and orientation details - `vignette("Resampling")` for grid changes and reorientation - `vignette("AnalysisWorkflows")` for ROIs, searchlights, and split-map-reduce patterns