--- 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) ```