---
title: Getting Started with bidser
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Getting Started with bidser}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
params:
family: red
css: albers.css
resource_files:
- albers.css
- albers.js
includes:
in_header: |-
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
message = FALSE,
warning = FALSE
)
```
```{r theme-setup, include = FALSE}
suppressPackageStartupMessages({
library(bidser)
library(tibble)
library(dplyr)
library(tidyr)
library(gluedown)
})
```
## Introduction to bidser
`bidser` is an R package designed for working with neuroimaging data organized according to the [Brain Imaging Data Structure (BIDS)](https://bids.neuroimaging.io/) standard. BIDS is a specification that describes how to organize and name neuroimaging and behavioral data, making datasets more accessible, shareable, and easier to analyze.
### What is BIDS?
BIDS organizes data into a hierarchical folder structure with standardized naming conventions:
- **Subjects** are identified by folders named `sub-XX`
- **Sessions** (optional) are identified by folders named `ses-XX`
- **Data types** are organized into modality-specific folders (`anat`, `func`, `dwi`, etc.)
- **Files** follow specific naming patterns that encode metadata (subject, session, task, run, etc.)
### What does bidser do?
`bidser` provides tools to:
- **Query and filter** files based on BIDS metadata (subject, task, run, etc.)
- **Read event files** that describe experimental paradigms
- **Work with fMRIPrep derivatives** for preprocessed data
- **Navigate complex BIDS hierarchies** without manually constructing file paths
Let's explore these capabilities using a real BIDS dataset.
## Loading a BIDS Dataset
We'll use the `ds001` dataset from the BIDS examples, which contains data from a "Balloon Analog Risk Task" experiment with 16 subjects.
```{r setup, include = FALSE}
ds001_path <- tryCatch(
get_example_bids_dataset("ds001"),
error = function(e) NULL
)
if (is.null(ds001_path)) {
knitr::knit_exit("Example dataset not available.")
}
proj <- bids_project(ds001_path)
```
```{r}
proj
```
The `bids_project` object provides a high-level interface to the dataset. We can see it contains 16 subjects with both anatomical and functional data.
## Basic Dataset Queries
### Dataset Structure
Let's explore the basic structure of this dataset:
```{r}
# Check if the dataset has multiple sessions per subject
sessions(proj)
# Get all participant IDs
participants(proj)
# What tasks are included?
tasks(proj)
# Get a summary of the dataset
bids_summary(proj)
```
### Finding Files by Type
bidser provides several ways to find files. Let's start with the most common neuroimaging file types:
```{r}
# Find all anatomical T1-weighted images
t1w_files <- search_files(proj, regex = "T1w\\.nii", full_path = FALSE)
head(t1w_files)
# Find all functional BOLD scans
bold_files <- func_scans(proj, full_path = FALSE)
head(bold_files)
```
### Filtering by Subject and Task
One of bidser's key strengths is filtering data by BIDS metadata:
```{r}
# Get functional scans for specific subjects
sub01_scans <- func_scans(proj, subid = "01")
sub02_scans <- func_scans(proj, subid = "02")
cat("Subject 01:", length(sub01_scans), "scans\n")
cat("Subject 02:", length(sub02_scans), "scans\n")
# Filter by task (ds001 only has one task, but this shows the syntax)
task_scans <- func_scans(proj, task = "balloonanalogrisktask")
cat("Balloon task:", length(task_scans), "scans total\n")
# Combine filters: specific subject AND task
sub01_task_scans <- func_scans(proj, subid = "01", task = "balloonanalogrisktask")
cat("Subject 01, balloon task:", length(sub01_task_scans), "scans\n")
```
### Working with Multiple Subjects
You can use regular expressions to select multiple subjects at once:
```{r}
# Get scans for subjects 01, 02, and 03
first_three_scans <- func_scans(proj, subid = "0[123]")
cat("First 3 subjects:", length(first_three_scans), "scans total\n")
# Get scans for all subjects (equivalent to default)
all_scans <- func_scans(proj, subid = ".*")
cat("All subjects:", length(all_scans), "scans total\n")
```
## Working with Event Files
Event files describe the experimental paradigm - when stimuli were presented, what responses occurred, etc. This is crucial for task-based fMRI analysis.
```{r}
# Find all event files
event_file_paths <- event_files(proj)
cat("Found", length(event_file_paths), "event files\n")
# Read event data into a nested data frame
events_data <- read_events(proj)
events_data
```
Let's explore the event data structure:
```{r}
# Unnest events for subject 01
first_subject_events <- events_data %>%
filter(.subid == "01") %>%
unnest(cols = c(data))
head(first_subject_events)
names(first_subject_events)
```
### Analyzing Event Data
Let's do some basic exploration of the experimental design:
```{r}
# How many trials per subject?
trial_counts <- events_data %>%
unnest(cols = c(data)) %>%
group_by(.subid) %>%
summarise(n_trials = n(), .groups = "drop")
trial_counts
```
## Working with Individual Subjects
The `bids_subject()` function provides a convenient interface for working with data from a single subject. It returns a lightweight object with helper functions that automatically filter data for that subject.
```{r}
# Create a subject-specific interface for subject 01
subject_01 <- bids_subject(proj, "01")
# Get all functional scans for this subject
sub01_scans <- subject_01$scans()
cat("Subject 01:", length(sub01_scans), "functional scans\n")
# Get event files for this subject
sub01_events <- subject_01$events()
cat("Subject 01:", length(sub01_events), "event files\n")
# Read event data for this subject
sub01_event_data <- subject_01$events()
sub01_event_data
```
This approach is particularly useful when you're doing subject-level analyses:
```{r}
subjects_to_analyze <- c("01", "02", "03")
for (subj_id in subjects_to_analyze) {
subj <- bids_subject(proj, subj_id)
scans <- subj$scans()
events <- subj$events()
cat(sprintf("Subject %s: %d scans, %d event files\n",
subj_id, length(scans), length(events)))
}
```
The subject interface makes it easy to write analysis pipelines that iterate over subjects without manually constructing filters:
```{r}
subject_trial_summary <- lapply(participants(proj)[1:3], function(subj_id) {
subj <- bids_subject(proj, subj_id)
event_data <- subj$events()
n_trials <- if (nrow(event_data) > 0) {
event_data %>% unnest(cols = c(data)) %>% nrow()
} else {
0
}
tibble(subject = subj_id, n_trials = n_trials, n_scans = length(subj$scans()))
}) %>% bind_rows()
subject_trial_summary
```
## Advanced Querying
### Custom File Searches
The `search_files()` function is very flexible for custom queries:
```{r}
# Find all JSON sidecar files
json_files <- search_files(proj, regex = "\\.json$")
cat("Found", length(json_files), "JSON files\n")
# Find files for specific runs
run1_files <- search_files(proj, regex = "bold", run = "01")
cat("Found", length(run1_files), "files from run 01\n")
# Complex pattern matching: T1w files for subjects 01-05
t1w_subset <- search_files(proj, regex = "T1w", subid = "0[1-5]")
cat("Found", length(t1w_subset), "T1w files for subjects 01-05\n")
```
### Getting Full File Paths
Sometimes you need the complete file paths for analysis:
```{r}
# Get full paths to functional scans for analysis
full_paths <- func_scans(proj, subid = "01", full_path = TRUE)
full_paths
# Check that files actually exist
all(file.exists(full_paths))
```
## Next Steps
This quickstart covered the basic functionality of bidser for querying BIDS datasets. For more advanced usage, see:
- **fMRIPrep integration**: Working with preprocessed derivatives
- **Data loading**: Reading neuroimaging data with `neurobase` or `RNifti`
- **Confound regression**: Using physiological and motion regressors
- **Group analysis**: Combining data across subjects efficiently
## Reading files produced by FMRIPrep
If you have processed a dataset with FMRIPrep, `bidser` can be used to read in many of the resultant derivative files. If a project has an FMRIPrep derivatives folder, then we can read in the BIDS hierarchy plus derivatives as follows:
```{r derivatives, eval = FALSE}
# Download an fMRIPrep example dataset
deriv_path <- get_example_bids_dataset("ds000001-fmriprep")
proj_deriv <- bids_project(deriv_path, fmriprep = TRUE)
proj_deriv
# Convenience functions for derivative files, e.g. preprocessed scans:
pscans <- preproc_scans(proj_deriv)
head(as.character(pscans))
# Read confound files
conf <- read_confounds(proj_deriv, subid = "01")
```
```{r cleanup, include=FALSE}
if (exists("ds001_path")) unlink(ds001_path, recursive = TRUE)
if (exists("deriv_path")) unlink(deriv_path, recursive = TRUE)
```