---
title: Advanced NeuroVec Patterns
date: '`r Sys.Date()`'
output: rmarkdown::html_vignette
vignette: |
%\VignetteIndexEntry{Advanced NeuroVec Patterns}
%\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, echo = FALSE, message = 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(purrr)
library(neuroim2)
})
options(mc.cores = 1)
```
This article is now the deeper companion to `vignette("VolumesAndVectors")`.
Read that first if you want the compact introduction to `NeuroVec`. Stay here
when you want more detail on multi-file reads, manual construction, matrix
views, and dense/sparse conversions for 4D data.
## Working with neuroimaging time-series data
This vignette focuses on the parts of the 4D workflow that remain useful after
you already understand the basic container model:
- reading many 4D files into a `NeuroVecSeq`
- creating `NeuroVec` objects from arrays or matrices
- time-series transforms and concatenation
- dense/sparse conversion patterns
- writing vectors back to disk
### Reading multiple four-dimensional images
```{r}
file_name <- system.file("extdata", "global_mask_v4.nii", package = "neuroim2")
vec <- read_vec(file_name)
vec_multi <- read_vec(c(file_name, file_name, file_name))
dim(vec_multi)
vec2 <- read_vec(rep(file_name, 10))
vec2
```
### Creating a NeuroVec from an in-memory array
You can build a `NeuroVec` directly from arrays or matrices:
```{r}
set.seed(1)
dims <- c(24, 24, 24, 5)
arr <- array(rnorm(prod(dims)), dims)
sp4 <- NeuroSpace(dims, spacing = c(2,2,2))
dvec <- NeuroVec(arr, sp4)
dim(dvec)
```
You can also start from a matrix (voxels × time or time × voxels) using `DenseNeuroVec`:
```{r}
mat <- matrix(rnorm(prod(dims)), nrow = prod(dims[1:3]), ncol = dims[4])
dvec2 <- DenseNeuroVec(mat, sp4)
all.equal(dim(dvec), dim(dvec2))
```
### Time-series transforms: z-scoring and summary volumes
Z-score each voxel’s time-series (center and scale across time):
```{r}
vec_z <- scale_series(dvec, center = TRUE, scale = TRUE)
dim(vec_z)
```
Compute a mean volume across time and return a 3D `NeuroVol`:
```{r}
M <- as.matrix(dvec) # voxels × time
vmean <- rowMeans(M) # per-voxel mean
mean3d <- NeuroVol(vmean, drop_dim(space(dvec)))
mean3d
```
### Concatenating along time
Append time points by concatenating vectors or volumes:
```{r}
dvec_more <- concat(dvec, dvec) # doubles the time dimension
dim(dvec_more)
```
### Dense ↔ sparse workflows
Sparse representations store only voxels within a mask. This is handy for large ROIs or brain masks.
```{r}
# Build a random mask and convert a dense vec to sparse
mask_arr <- array(runif(prod(dims[1:3])) > 0.7, dims[1:3])
mask_vol <- LogicalNeuroVol(mask_arr, drop_dim(sp4))
svec <- as.sparse(dvec, mask_vol) # SparseNeuroVec with explicit mask
svec # note the stored mask and cardinality
# Convert back to dense if needed
dense_again <- as.dense(svec)
all.equal(dim(dense_again), dim(dvec))
```
Tip: For file-backed or memory-mapped vectors, convert to `DenseNeuroVec` via a matrix if you need dense-only operations:
```{r}
dv_dense <- DenseNeuroVec(as.matrix(vec), space(vec))
```
### Writing vectors to disk
You can write `NeuroVec` and `NeuroVol` objects as NIfTI files:
```{r}
tmp_vec <- tempfile(fileext = ".nii.gz")
write_vec(sub_vector(vec, 1:3), tmp_vec)
file.exists(tmp_vec)
unlink(tmp_vec)
```
### Putting it together with an ROI
Combine ROI extraction with time-series transforms:
```{r}
vol3d <- read_vol(system.file("extdata", "global_mask_v4.nii", package="neuroim2"))
roi <- spherical_roi(vol3d, c(12,12,12), radius = 6)
rts <- series_roi(vec, roi) # ROIVec (T × N with coords)
# z-score each column (voxel) across time, then average within ROI
mat_roi <- values(rts) # T × N
mat_z <- base::scale(mat_roi, center=TRUE, scale=TRUE)
roi_mean <- rowMeans(mat_z)
length(roi_mean) # matches time dimension
```
This article now complements the core path rather than replacing it. Use
`vignette("VolumesAndVectors")` for the basic 4D container model and come back
here when you need denser transformation and conversion patterns.