---
title: Working with Multiple Brain Maps using LabeledVolumeSet
author: fmristore Package
date: '`r Sys.Date()`'
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Working with Multiple Brain Maps using LabeledVolumeSet}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
params:
family: red
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(params$family))
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
message = FALSE,
warning = FALSE
)
library(fmristore)
library(neuroim2)
```
```{r fmristore-memory-options, include=FALSE}
# For this vignette, suppress fmristore's large-dataset memory warnings
# to keep the focus on the API rather than performance diagnostics.
old_fmristore_warn <- getOption("fmristore.warn_memory", TRUE)
options(fmristore.warn_memory = FALSE)
```
## What is a LabeledVolumeSet?
Imagine you have multiple brain maps from different conditions, contrasts, or subjects that you want to store together efficiently. `LabeledVolumeSet` is designed exactly for this scenario. It's like having a filing cabinet where each drawer (label) contains a complete 3D brain map.
Key benefits:
- **Organized**: Each brain volume has a meaningful label
- **Efficient**: All volumes stored in a single HDF5 file
- **Consistent**: All volumes share the same brain mask
- **Easy Access**: Retrieve volumes by name or index
## Quick Example: Storing Statistical Maps
Let's say you have t-statistic maps from different contrasts in your fMRI study:
### Step 1: Create Example Data
```{r create-data}
# Define brain dimensions
brain_dims <- c(10, 10, 5)
n_contrasts <- 3
# Simulate t-statistic maps for different contrasts
contrast_data <- array(rnorm(prod(brain_dims) * n_contrasts),
dim = c(brain_dims, n_contrasts)
)
```
### Step 2: Create Brain Mask
```{r create-mask}
# Create a brain mask (only analyze certain voxels)
brain_mask <- array(TRUE, dim = brain_dims)
brain_mask[1:3, 1:3, ] <- FALSE # Exclude corners
# Convert to neuroim2 objects
stat_maps <- NeuroVec(contrast_data, NeuroSpace(c(brain_dims, n_contrasts)))
mask <- LogicalNeuroVol(brain_mask, NeuroSpace(brain_dims))
```
### Step 3: Save with Labels
```{r save-labeled}
# Define meaningful labels
contrast_names <- c("Faces_vs_Houses", "Words_vs_Nonwords", "Motion_vs_Static")
# Save as LabeledVolumeSet
h5_file <- tempfile(fileext = ".h5")
h5_handle <- write_labeled_vec(stat_maps, mask, contrast_names, file = h5_file)
h5_handle$close_all()
```
### Step 4: Load and Access
```{r load-access}
# Load the labeled set
labeled_maps <- read_labeled_vec(h5_file)
# Access by name
faces_map <- labeled_maps[["Faces_vs_Houses"]]
print(paste("Faces contrast map dimensions:", paste(dim(faces_map), collapse = "x")))
# See all available maps
print("Available contrasts:")
print(labels(labeled_maps))
close(labeled_maps)
unlink(h5_file)
```
## Real-World Use Case: Group Analysis Results
Here's how you might organize results from a group analysis:
```{r group-analysis}
# Simulate group analysis results
n_subjects <- 20
brain_dims <- c(20, 20, 10)
# Create different statistical maps
group_maps <- list(
mean_activation = array(rnorm(prod(brain_dims), mean = 0.5), brain_dims),
t_statistic = array(rt(prod(brain_dims), df = n_subjects - 1), brain_dims),
effect_size = array(rnorm(prod(brain_dims), mean = 0, sd = 0.3), brain_dims),
p_values = array(runif(prod(brain_dims)), brain_dims)
)
# Combine into a 4D array
all_maps <- array(unlist(group_maps), dim = c(brain_dims, length(group_maps)))
stat_vec <- NeuroVec(all_maps, NeuroSpace(c(brain_dims, length(group_maps))))
# Create a mask (e.g., only gray matter)
mask_array <- array(runif(prod(brain_dims)) > 0.3, brain_dims)
mask <- LogicalNeuroVol(mask_array, NeuroSpace(brain_dims))
# Save with descriptive labels
h5_file <- tempfile(fileext = ".h5")
h5_handle <- write_labeled_vec(stat_vec, mask, names(group_maps), file = h5_file)
h5_handle$close_all()
# Load and analyze
results <- read_labeled_vec(h5_file)
# Find significant voxels
p_map <- results[["p_values"]]
t_map <- results[["t_statistic"]]
# Threshold at p < 0.05
significant_voxels <- p_map < 0.05
n_sig <- sum(significant_voxels)
print(paste("Found", n_sig, "significant voxels"))
# Get effect sizes for significant voxels
if (n_sig > 0) {
effect_map <- results[["effect_size"]]
sig_effects <- effect_map[which(significant_voxels)]
hist(sig_effects,
main = "Effect Sizes in Significant Voxels",
xlab = "Effect Size"
)
}
close(results)
unlink(h5_file)
```
## Working with Subject-Specific Maps
Store individual subject maps for later analysis:
### Create Subject Data
```{r subject-setup}
# Simulate beta maps from 5 subjects
n_subjects <- 5
brain_dims <- c(15, 15, 8)
# Create subject-specific activation maps
subject_data <- array(rnorm(prod(brain_dims) * n_subjects),
dim = c(brain_dims, n_subjects)
)
```
### Add Activation Patterns
```{r add-activations}
# Add subject-specific patterns
for (subj in 1:n_subjects) {
# Each subject has slightly different activation center
x_center <- 7 + subj
y_center <- 7 + subj
z_center <- 4
# Add activation blob
for (x in (x_center - 2):(x_center + 2)) {
for (y in (y_center - 2):(y_center + 2)) {
if (x > 0 && x <= brain_dims[1] && y > 0 && y <= brain_dims[2]) {
subject_data[x, y, z_center, subj] <-
subject_data[x, y, z_center, subj] + rnorm(1, mean = 3, sd = 0.5)
}
}
}
}
```
### Save Subject Maps
```{r save-subjects}
# Create objects
subject_vec <- NeuroVec(subject_data, NeuroSpace(c(brain_dims, n_subjects)))
mask <- LogicalNeuroVol(array(TRUE, brain_dims), NeuroSpace(brain_dims))
subject_labels <- paste0("Subject_", sprintf("%02d", 1:n_subjects))
# Save
h5_file <- tempfile(fileext = ".h5")
h5_handle <- write_labeled_vec(subject_vec, mask, subject_labels, file = h5_file)
h5_handle$close_all()
```
### Compute Group Statistics
```{r group-stats, fig.width=8, fig.height=6}
# Load and compute group average
subjects <- read_labeled_vec(h5_file)
# Extract all subjects' data efficiently
all_subjects_data <- subjects[, , , ] # Get all volumes at once
# Compute mean across subjects (4th dimension)
group_mean <- apply(all_subjects_data, c(1, 2, 3), mean)
print(paste("Group mean map dimensions:", paste(dim(group_mean), collapse = "x")))
# Find peak activation
peak_loc <- which(group_mean == max(group_mean), arr.ind = TRUE)
print(paste(
"Peak activation at voxel:",
paste(peak_loc, collapse = ", ")
))
close(subjects)
unlink(h5_file)
```
## Advanced: Efficient Access Patterns
When working with many volumes, access them efficiently:
```{r efficient-access, fig.width=8, fig.height=6}
# Create a dataset with many conditions
n_conditions <- 10
brain_dims <- c(20, 20, 10)
condition_data <- array(rnorm(prod(brain_dims) * n_conditions),
dim = c(brain_dims, n_conditions)
)
condition_vec <- NeuroVec(condition_data, NeuroSpace(c(brain_dims, n_conditions)))
mask <- LogicalNeuroVol(array(TRUE, brain_dims), NeuroSpace(brain_dims))
condition_names <- paste0("Condition_", LETTERS[1:n_conditions])
h5_file <- tempfile(fileext = ".h5")
h5_handle <- write_labeled_vec(condition_vec, mask, condition_names, file = h5_file)
h5_handle$close_all()
# Load the dataset
conditions <- read_labeled_vec(h5_file)
# Method 1: Access specific conditions by name
print("Method 1: Individual access")
cond_A <- conditions[["Condition_A"]]
cond_B <- conditions[["Condition_B"]]
# Method 2: Access multiple conditions at once
print("Method 2: Multiple conditions via indices")
first_three <- conditions[, , , 1:3] # Get first 3 conditions
print(paste(
"Dimensions of first 3 conditions:",
paste(dim(first_three), collapse = "x")
))
# Method 3: Extract values at specific voxel across all conditions
print("Method 3: Voxel time series across conditions")
voxel_10_10_5 <- conditions[10, 10, 5, ]
barplot(voxel_10_10_5,
names.arg = condition_names,
las = 2, main = "Values at Voxel [10,10,5]"
)
close(conditions)
unlink(h5_file)
```
## Best Practices
### 1. Use Meaningful Labels
```{r meaningful-labels, eval=FALSE}
# Good labels
good_labels <- c(
"PreTreatment_T1", "PostTreatment_T1",
"PreTreatment_T2", "PostTreatment_T2"
)
# Avoid generic labels
bad_labels <- c("map1", "map2", "map3", "map4")
```
### 2. Organize Your Analysis
```{r organize-analysis, eval=FALSE}
# Save different analysis stages
write_labeled_vec(raw_maps, mask,
paste0("Raw_", condition_names),
file = "raw_maps.h5"
)
write_labeled_vec(smoothed_maps, mask,
paste0("Smoothed_", condition_names),
file = "smoothed_maps.h5"
)
write_labeled_vec(stats_maps, mask,
paste0("Stats_", condition_names),
file = "statistical_maps.h5"
)
```
### 3. Remember to Close
```{r close-reminder, eval=FALSE}
lvs <- read_labeled_vec("my_maps.h5")
# ... analysis code ...
close(lvs) # Always close when done!
```
## Summary
`LabeledVolumeSet` makes it easy to:
- Store multiple related brain maps together
- Access them by meaningful names
- Work efficiently with large collections
- Keep your neuroimaging data organized
Perfect for storing statistical maps, contrast images, subject data, or any collection of related 3D brain volumes!
```{r fmristore-memory-options-reset, include=FALSE}
# Restore original fmristore memory warning behaviour
options(fmristore.warn_memory = old_fmristore_warn)
```
```{r cleanup, include=FALSE}
# Clean up temp files
temp_files <- list.files(tempdir(), pattern = "\\.h5$", full.names = TRUE)
file.remove(temp_files)
```