From 4f116b4805930d5a673a7a24a61e79ebb41f8645 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Fri, 17 Apr 2026 19:42:31 -0400 Subject: [PATCH] [ENH] Add participant+sessions.tsv for session-varying participant metadata Introduces a single new optional dataset-level file `participant+sessions.tsv` with a composite index `[participant_id, session_id]`. This provides a single top-level location for metadata that varies across both participants and sessions -- e.g. age at each visit, body weight, clinical scores in longitudinal studies -- complementing the existing `participants.tsv` (participant-constant) and per-subject `*_sessions.tsv` files. Note that it is already possible to provide such metadata in `sub-*/ses-*_sessions.tsv` file. So such approach just serves the way to provide an "aggregate" collection of metadata. As such, we might then need to define how it interacts with the inheritance principle, but defining that yet TODO in general for .tsv files. The `+` in the filename signals a composite index, following the convention proposed in #2273 and alternative to freshly proposed #2402 inspired by a work on BEP036 - https://github.com/bids-standard/bids-specification/pull/2123 hence attn @bids-standard/bep036 . Most of the changes are just straightforward interpolation of `participants.tsv` and `sessions.tsv` files definitions. One of the notable changes is to `meta/context.yaml` where we added `dataset.sessions` (union of all session directories across subjects) to enable session-level validation checks. I think it is only reasonable given that we did already included dataset level summaries for datatypes and modalities. But it would require bids-validator to support it. Alternative - is to drop it and that extra check we added. Ideally though we should figure out how to validate specific combinations of sub/sessions and TODO was left for that. An example `participant+sessions.tsv` with `body_weight` column for the already `7t_trt` bids-examples dataset is at - https://github.com/bids-standard/bids-examples/pull/556 where, if you also look into original `participants.tsv`, makes it a little obvious that duplication of all entries across all sessions would be dubious. - implements a single first manifestation for #2273 - I think overall we can state that it closes #1020 which theoretically could have been closed with original introduction of _sessions.tsv files. Co-Authored-By: Claude Code 2.1.113 / Claude Opus 4.6 --- src/longitudinal-and-multi-site-studies.md | 8 +++ .../data-summary-files.md | 57 +++++++++++++++++++ src/schema/README.md | 5 +- src/schema/meta/context.yaml | 12 ++++ src/schema/objects/files.yaml | 10 ++++ src/schema/rules/checks/dataset.yaml | 37 ++++++++++++ src/schema/rules/files/common/tables.yaml | 6 ++ .../rules/tabular_data/modality_agnostic.yaml | 20 +++++++ 8 files changed, 153 insertions(+), 2 deletions(-) diff --git a/src/longitudinal-and-multi-site-studies.md b/src/longitudinal-and-multi-site-studies.md index 5623bec305..32b5a635b2 100644 --- a/src/longitudinal-and-multi-site-studies.md +++ b/src/longitudinal-and-multi-site-studies.md @@ -56,6 +56,7 @@ A guide for using macros can be found at }, "sub-control01_sessions.tsv": "", }, + "participant+sessions.tsv": "", "participants.tsv": "", "dataset_description.json": "", "README": "", @@ -71,6 +72,13 @@ ses-predrug 2009-06-15T13:45:30 120 ses-postdrug 2009-06-16T13:45:30 100 ``` +In addition to per-subject `_sessions.tsv` files, +a dataset-level `participant+sessions.tsv` file can aggregate +metadata that varies across both participants and sessions +(such as age at each session) into a single file. +See [Participant and sessions file](modality-agnostic-files/data-summary-files.md#participant-and-sessions-file) +for details. + See this [example dataset](https://github.com/bids-standard/bids-examples/tree/master/7t_trt) that has been formatted using this specification and can be used diff --git a/src/modality-agnostic-files/data-summary-files.md b/src/modality-agnostic-files/data-summary-files.md index c615196b5c..3766731d66 100644 --- a/src/modality-agnostic-files/data-summary-files.md +++ b/src/modality-agnostic-files/data-summary-files.md @@ -81,6 +81,63 @@ to date of birth. } ``` +## Participant and sessions file + +Template: + +```Text +participant+sessions.tsv +participant+sessions.json +``` + + +{{ MACROS___render_text("objects.files.participant_sessions.description") }} + +We RECOMMEND to make use of these columns, and +in case that you do use them, we RECOMMEND to use the following values +for them: + + +{{ MACROS___make_columns_table("modality_agnostic.ParticipantSessions") }} + +`participant+sessions.tsv` example: + +```tsv +participant_id session_id age body_weight +sub-01 ses-predrug 34 81.3 +sub-01 ses-postdrug 35 83.1 +sub-02 ses-predrug 12 45.2 +sub-02 ses-postdrug 13 48.7 +``` + +It is RECOMMENDED to accompany each `participant+sessions.tsv` file with a sidecar +`participant+sessions.json` file to describe the TSV column names and properties +of their values (see also +the [section on tabular files](../common-principles.md#tabular-files)). +Such sidecar files are needed to interpret the data, especially so when +additional columns are defined beyond `age`, +such as `body_weight` in this example. + +`participant+sessions.json` example: + +```JSON +{ + "body_weight": { + "Description": "body weight of the participant at the time of the session", + "Format": "number", + "Units": "kg" + } +} +``` + ## Samples file Template: diff --git a/src/schema/README.md b/src/schema/README.md index c0da0fa55e..0079520690 100644 --- a/src/schema/README.md +++ b/src/schema/README.md @@ -732,8 +732,9 @@ Here, `README` and `README.md` are both valid, while only `dataset_description.j #### Tabular metadata files `rules.files.common.tables` describes TSV files and their associated metadata, -including `participants.tsv`, `samples.tsv`, `*_sessions.tsv` and `*_scans.tsv`. -The first two use the `stem` field, while the latter two specify the entities used +including `participants.tsv`, `participant+sessions.tsv`, `samples.tsv`, +`*_sessions.tsv` and `*_scans.tsv`. +The first three use the `stem` field, while the latter two specify the entities used to construct the filename. The valid fields are: diff --git a/src/schema/meta/context.yaml b/src/schema/meta/context.yaml index 7b630d61f5..b397f06434 100644 --- a/src/schema/meta/context.yaml +++ b/src/schema/meta/context.yaml @@ -80,6 +80,18 @@ properties: type: array items: type: string + sessions: + description: 'Collections of sessions in dataset' + type: object + required: + - ses_dirs + additionalProperties: false + properties: + ses_dirs: + description: 'Union of all ses-* directories found across all subjects' + type: array + items: + type: string subject: description: 'Properties and contents of the current subject' type: object diff --git a/src/schema/objects/files.yaml b/src/schema/objects/files.yaml index 9c6bc41305..8587f88fb1 100644 --- a/src/schema/objects/files.yaml +++ b/src/schema/objects/files.yaml @@ -87,6 +87,16 @@ participants: (for examples `homo sapiens`, `mus musculus`, `rattus norvegicus`). For backwards compatibility, if `species` is absent, the participant is assumed to be `homo sapiens`. +participant_sessions: + display_name: Participant and Session Information + file_type: regular + description: | + The purpose of this OPTIONAL file is to describe properties that vary + across both participants and sessions, such as age at each session. + If this file exists, it MUST contain the columns `participant_id` and `session_id`, + which MUST consist of `sub-