Portions of the BIDS specification are defined using YAML files in order to make the specification machine-readable.
Currently the portions of the specification that rely on this schema are:
- the entity tables,
- entity definitions,
- filename templates,
- metadata tables.
Any changes to the specification should be mirrored in the schema.
At the time of this writing, the schema has the following file layout:
├── meta
│ ├── ...
│ └── versions.yaml
├── objects
│ ├── ...
│ └── suffixes.yaml
├── rules
│ ├── checks
│ │ ├── ...
│ │ └── references.yaml
│ ├── files
│ │ ├── common
│ │ │ ├── core.yaml
│ │ │ └── tables.yaml
│ │ ├── deriv
│ │ │ ├── imaging.yaml
│ │ │ └── preprocessed_data.yaml
│ │ └── raw
│ │ ├── ...
│ │ └── task.yaml
│ ├── sidecars
│ │ ├── derivatives
│ │ │ └── common_derivatives.yaml
│ │ ├── ...
│ │ └── pet.yaml
│ ├── tabular_data
│ │ ├── derivatives
│ │ │ └── common_derivatives.yaml
│ │ ├── ...
│ │ └── task.yaml
│ ├── ...
│ └── modalities.yaml
├── BIDS_VERSION
└── SCHEMA_VERSION
The top-level organization includes objects
, where terms are defined;
rules
, where constraints (such as valid filenames or required metadata fields)
are defined;
and meta
, where definitions useful for interpreting the schema are defined.
Each file is made up of YAML data, most often an object.
For example, the file rules/checks/mri.yaml
contains the contents:
PhasePartUnits:
issue:
code: PHASE_UNITS
message: |
Phase images (with the `part-phase` entity) must have units
"rad" or "arbitrary".
level: error
selectors:
- modality == "mri"
- entities.part == "phase"
- '"Units" in sidecar'
checks:
- intersects([sidecar.Units], ["rad", "arbitrary"])
When we wish to refer to a file we might write rules/checks/mri.yaml
.
Alternately, we can use rules.checks.mri
to refer to the object contained by the
file.
Using this notation, the qualified name, the contents of an entire directory or a
portion of a file can be referred to unambiguously.
For example, the entire rules/checks/
directory is referred to as rules.checks
,
and rules.checks.mri.PhasePartUnits.issue
refers to the object:
{
"code": "PHASE_UNITS",
"message": "Phase images (with the `part-phase` [...]\n\"rad\" or \"arbitrary\".\n",
"level": "error"
}
These qualified names may be used in this README, as well as in references and expressions.
Many objects throughout the schema have a description
field,
which will typically be rendered somewhere in the specification.
Because the specification is written in Markdown,
these description
fields may also contain Markdown,
including links to other locations in the specification.
Because the same description may be used in multiple locations,
a mechanism is needed to ensure that the correct path is discovered
to render the description in each location.
To do this, the path should follow the form SPEC_ROOT/path/within/source.md#anchor
.
For example, to link to the
Definitions
section of
Common principles,
use the path SPEC_ROOT/common-principles.md#definitions
:
[Common principles - Definitions](SPEC_ROOT/common-principles.md#definitions)
Note that the Markdown extension .md
MUST be used for this to render correctly.
For more information please see the following pull request and linked discussions: #1096
Some schema entries take the form:
ObjectName:
$ref: objects.metadata.OtherObjectName
This object may be dereferenced by replacing the $ref
entry
with the object being referenced.
The following two prototypical examples are presented to clarify the semantics of
references (the cases in which they are used will be presented later):
-
In
objects.enums
:_GeneticLevelEnum: type: string enum: - $ref: objects.enums.Genetic.value - $ref: objects.enums.Genomic.value - $ref: objects.enums.Epigenomic.value - $ref: objects.enums.Transcriptomic.value - $ref: objects.enums.Metabolomic.value - $ref: objects.enums.Proteomic.value
and in
objects.metadata
:GeneticLevel: name: GeneticLevel display_name: Genetic Level description: | Describes the level of analysis. Values MUST be one of `"Genetic"`, `"Genomic"`, `"Epigenomic"`, `"Transcriptomic"`, `"Metabolomic"`, or `"Proteomic"`. anyOf: - $ref: objects.enums._GeneticLevelEnum - type: array items: $ref: objects.enums._GeneticLevelEnum
Here
_GeneticLevelEnum
is used to describe the valid values ofGeneticLevel
, (which are in turn references to individual values), and the references insideGeneticLevel.anyOf
indicate that there may be a single such value or a list of values. -
In
rules.files.deriv.preprocessed_data
:anat_nonparametric_common: $ref: rules.files.raw.anat.nonparametric entities: $ref: rules.files.raw.anat.nonparametric.entities space: optional description: optional
Here, the derivative datatype rule starts by copying the raw datatype rule
rules.files.raw.anat.nonparametric
. It then overrides theentities
portion of that rule with a new object. To extend the originalentities
, it again begins by referencingrules.files.raw.anat.nonparametric.entities
, and adding the new entitiesspace
anddescription
.
Rules definitions make use of a limited language of expressions that always evaluate to true
or false
.
These expressions may be used as selectors
, determining whether a rule applies,
or checks
, determining whether a rule is satisfied.
Re-examining rules.checks.mri.PhasePartUnits
from above:
PhasePartUnits:
issue:
code: PHASE_UNITS
message: |
Phase images (with the `part-phase` entity) must have units
"rad" or "arbitrary".
level: error
selectors:
- modality == "mri"
- entities.part == "phase"
- '"Units" in sidecar'
checks:
- intersects([sidecar.Units], ["rad", "arbitrary"])
We see expressions may contain:
- fields such as
modality
,entities
(which has a.part
subfield),sidecar
- String literals such as
"mri"
,"Units"
or"rad"
- Lists containing fields or strings
- Comparison operators such as
==
(equality) orin
(subfield exists in field) - Functions such as
intersects()
In fact, the full list of fields is defined in the meta.context
object,
which (currently) contains at the top level:
schema
: access to the schema itselfdataset
: attributes of the whole datasetsubject
: attributes of the current subjectpath
: the full path of the current file (relative to dataset root)entities
: an object of entities parsed from the pathdatatype
: the datatype, parsed from the pathsuffix
: the suffix, parsed from the pathextension
: the file extensionmodality
: the file modality, determined by datatypesidecar
: the metadata values, accumulated by the inheritance principleassociations
: associated files, discovered by the inheritance principlecolumns
: the columns in the current TSV filejson
: the contents of the current JSON filegzip
: the contents of the current file GZIP headernifti_header
: selected contents of the current NIfTI file's headerome
: the contents of the current OME-XML metadatatiff
: the contents of the current TIFF file's header
Some of these are strings, while others are nested objects. These are to be populated by an interpreter of the schema, and provide the namespace in which expressions are evaluated.
The following operators should be defined by an interpreter:
Operator | Definition | Example |
---|---|---|
== |
equality | suffix == "T1w" |
!= |
inequality | entities.task != "rest" |
< /> |
less-than / greater-than | sidecar.EchoTime < 0.5 |
<= />= |
less-than-or-equal / greater-than-or-equal | 0 <= 4 |
in |
object lookup, true if RHS is a subfield of LHS | "Units" in sidecar |
! |
negation, true if the following value is false, or vice versa | !true == false |
&& |
conjunction, true if both RHS and LHS are true | "Units" in sidecar && sidecar.Units == "mm" |
|| |
disjunction, true if either RHS or LHS is true | a < mn || a > mx |
. |
object query, returns value of subfield | sidecar.Units |
[] |
array/string index, returns value of Nth element (0-indexed) | columns.participant_label[0] |
+ |
numeric addition / string concatenation | x + 1 , stem + "suffix" |
- /* // |
numeric operators (division coerces to float) | length(array) - 2 , x * 2 , 1 / 2 == 0.5 |
The following functions should be defined by an interpreter:
Function | Definition | Example | Note |
---|---|---|---|
count(arg: array, val: any) -> int |
Number of elements in an array equal to val |
count(columns.type, "EEG") |
The number of times "EEG" appears in the column "type" of the current TSV file |
exists(arg: str | array, rule: str) -> int |
Count of files in an array that exist in the dataset. String is array with length 1. See following section for the meanings of rules. | exists(sidecar.IntendedFor, "subject") |
True if all files in IntendedFor exist, relative to the subject directory. |
index(arg: array, val: any) -> int |
Index of first element in an array equal to val , null if not found |
index(["i", "j", "k"], axis) |
The number, from 0-2 corresponding to the string axis |
intersects(a: array, b: array) -> bool |
true if arguments contain any shared elements |
intersects(dataset.modalities, ["pet", "mri"]) |
True if either PET or MRI data is found in dataset |
allequal(a: array, b: array) -> bool |
true if arrays have the same length and paired elements are equal |
intersects(dataset.modalities, ["pet", "mri"]) |
True if either PET or MRI data is found in dataset |
length(arg: array) -> int |
Number of elements in an array | length(columns.onset) > 0 |
True if there is at least one value in the onset column |
match(arg: str, pattern: str) -> bool |
true if arg matches the regular expression pattern (anywhere in string) |
match(extension, ".gz$") |
True if the file extension ends with .gz |
max(arg: array) -> number |
The largest non-n/a value in an array |
max(columns.onset) |
The time of the last onset in an events.tsv file |
min(arg: array) -> number |
The smallest non-n/a value in an array |
min(sidecar.SliceTiming) == 0 |
A check that the onset of the first slice is 0s |
sorted(arg: array, method: str) -> array |
The sorted values of the input array; defaults to type-determined sort. If method is "lexical", or "numeric" use lexical or numeric sort. | sorted(sidecar.VolumeTiming) == sidecar.VolumeTiming |
True if sidecar.VolumeTiming is sorted |
substr(arg: str, start: int, end: int) -> str |
The portion of the input string spanning from start position to end position | substr(path, 0, length(path) - 3) |
path with the last three characters dropped |
type(arg: Any) -> str |
The name of the type, including "array" , "object" , "null" |
type(datatypes) |
Returns "array" |
In various places, BIDS datasets may declare links between files.
In order to validate these links,
the exists()
function returns a count of files that can be found within the dataset.
To accommodate the various ways of declaring these links,
the following rules are defined:
rule |
Definition | Example |
---|---|---|
"dataset" |
A path relative to the root of the dataset. | exists('participants.tsv', 'dataset') |
"subject" |
A path relative to the current subject directory. | exists('ses-1/anat/sub-01_ses-1_T1w.nii.gz', 'subject') |
"stimuli" |
A path relative to the /stimuli directory. |
For events.tsv : exists(columns.stim_file, 'stimuli') == length(columns.stim_file) |
"file" |
A path relative to the directory containing the current file. | For scans.tsv : exists(columns.filename, 'file') == length(columns.stim_file) |
"bids-uri" |
A URI of the form bids:<dataset>:<relative-path> . If <dataset> is empty, the current dataset is used. |
exists('bids::participants.tsv', 'bids-uri') |
Missing values in the context object have the special value null
.
This value propagates through all of the above operations in a fully-defined,
hopefully intuitive way.
Most operations involving null
simply resolve to null
:
Operation | Result |
---|---|
sidecar.MissingValue |
null |
null.anything |
null |
null[0] |
null |
null && true |
null |
null || true |
null |
!null |
null |
null + 1 |
null |
null - 1 |
null |
null * 1 |
null |
null / 1 |
null |
match(null, pattern) |
null |
intersects(list, null) |
null |
intersects(null, list) |
null |
allequal(list, null) |
null |
allequal(null, list) |
null |
substr(null, 0, 1) |
null |
substr(str, null, 1) |
null |
substr(str, 0, null) |
null |
length(null) |
null |
count(null, val) |
null |
count(list, null) |
null |
index(null, val) |
null |
index([0], null) |
null |
index([], val) |
null |
min(null) |
null |
max(null) |
null |
exists(null, "bids-uri") |
null |
exists("/path", null) |
null |
The following operators have boolean results:
Operation | Result | Comment |
---|---|---|
null == false |
false |
|
null == true |
false |
|
null != false |
true |
|
null != true |
true |
|
null == null |
true |
|
null == 1 |
false |
Also < , > , <= and >= |
"VolumeTiming" in null |
false |
The type()
function returns a string:
Operation | Result |
---|---|
type(null) |
"null" |
Finally, if an expression (selector or check) evaluates to null
,
the null
will be interpreted equivalent to false
.
That is, a null
selector will not apply the current rule, and a null
check will fail.
Object files define "objects" or "terms", which are semantic descriptions of
concepts used in BIDS. These reside under the object.*
namespace in the schema.
These files do not describe how objects of different types
(for example file suffixes and file entities) interact with one another,
or whether objects are required in a given dataset or file.
There are currently 12 sub-namespaces, which fall into five rough categories.
The namespaces are:
Namespace | Description | Group |
---|---|---|
objects.common_principles |
Terms that are used throughout BIDS | General terms |
objects.modalities |
Broad categories of data represented in BIDS, roughly matching recording instrument | General terms |
objects.entities |
Name-value pairs appearing in filenames | Name/value terms |
objects.metadata |
Name-value pairs appearing in JSON files | Name/value terms |
objects.columns |
Column headings and values appearing in TSV files | Name/value terms |
objects.datatypes |
Subdirectories that organize files by type (such as anat , eeg ) |
Value terms |
objects.suffixes |
Filename suffixes that describe the contents of the file | Value terms |
objects.extensions |
Filename component that describe the format of the file | Value terms |
objects.formats |
Terms that define the forms values (for example, in metadata) might take | Formats |
objects.files |
Files and directories that may appear at the root of a dataset | Files |
objects.enums |
Full descriptions of enumerated values used in other sub-namespaces | Value terms |
Because these objects vary, the contents of each namespace can vary.
Common fields to all objects:
Field | Description |
---|---|
description |
A description of the term that can be understood that should not depend on particular surrounding text; may contain markdown for rendering |
display_name |
A human-friendly name, for tools to display; may include spaces |
The name/value terms groups (entities
, metadata
and columns
) define terms where
a name, when present, has a given meaning, and its value may be restricted.
These objects additionally have the field:
Field | Description |
---|---|
name |
For terms that can take on multiple values (such as entities, metadata fields), the name of the term as it appears in the specification and in a dataset; must be alphanumeric; mutually exclusive with value |
type |
The type (such as string , integer , object ) of values the term describes |
format |
The format of the term (defined in objects.formats ) |
Value terms groups (datatypes
, suffixes
, extensions
) define terms where a field
can take on multiple values.
For example, a file has one datatype, as compared to a collection of entities.
These objects may have the fields:
Field | Description |
---|---|
value |
For terms that cannot take on multiple values (for example suffixes or extensions), the string value of the term |
The formats
terms provide one additional field:
Field | Description |
---|---|
pattern |
Regular expression validating a string rendering of a value |
For name/value terms, the type
and format
fields allow constraints to be placed on
the values described by the names.
Additional fields may apply to further constrain the type:
Field | Description |
---|---|
maximum /minimum /exclusiveMinimum |
Value ranges for integer and number types |
maxValue /minValue |
Value ranges for integer and number types |
maxItems /minItems |
Size ranges for array types |
enum |
List of accepted values for string types |
Some values may be more flexible, allowing multiple possible values, or may be arrays or objects:
Field | Description |
---|---|
anyOf |
A list of constraints, any of which could apply |
items |
The array described contains values whose types are constrained |
properties |
The object described has a given set of fields; the values of these fields may be constrained |
additionalProperties |
The object described has constraints on its values, but not the names |
In a few cases, two objects with the same name appear multiple times in the specification. When this happens, it is preferred to find a common definition, and clarify it in the rules (see below). However, in some cases, the object description and permissible values differ, and it needs to be defined as two separate objects.
Consider the following examples:
# reference column for channels.tsv files for EEG data
reference__eeg:
name: reference
display_name: Electrode reference
description: |
Name of the reference electrode(s).
This column is not needed when it is common to all channels.
In that case the reference electrode(s) can be specified in `*_eeg.json` as `EEGReference`).
type: string
# reference column for channels.tsv files for iEEG data
reference__ieeg:
name: reference
display_name: Electrode reference
description: |
Specification of the reference (for example, `mastoid`, `ElectrodeName01`, `intracranial`, `CAR`, `other`, `n/a`).
If the channel is not an electrode channel (for example, a microphone channel) use `n/a`.
anyOf:
- type: string
- type: string
enum:
- n/a
Here, the TSV column "reference"
means different things when used for EEG data,
as compared to iEEG data, so two definitions are needed.
Because columns use snake_case
(meaning they can be expected to contain underscores),
two underscores are needed to separate the column name from the string that indicates the use of the term.
The convention can be summed up in the following rules:
-
Each specific term takes on the form
<term>_<context>
, where<term>
is the common name that the two (or more) terms share,<context>
indicates when the specific term applies. -
If the
<term>
appears insnake_case
then<context>
begins with an extra_
.
-
objects.common_principles
Field Description display_name
Human-friendly name description
Term definition -
objects.modalities
Field Description display_name
Human-friendly name description
Term definition -
objects.entities
Field Description display_name
Human-friendly name description
Term definition name
Key of entity, such as sub
orses
type
Type of value (always string
)format
Permissible format of values, either label
orindex
enum
Exclusive list of valid values, if present Note that descriptions should apply to all uses of the entity; if additional information applies in certain contexts, that should be written in the specification, and not the schema.
-
objects.metadata
Field Description display_name
Human-friendly name description
Term definition name
Name of field in JSON object (in CamelCase
)unit
Interpretation of numeric values type
Type of value (one of array
,string
,integer
,number
,object
orboolean
)format
Permissible format of values, from definitions in objects.formats
enum
Exclusive list of valid values, if present maximum
Maximum for numeric values minimum
Minimum for numeric values *
JSON-schema fields to further constrain values -
objects.columns
Field Description display_name
Human-friendly name description
Term definition name
Name of column in TSV file (in snake_case
)unit
Interpretation of numeric values type
Type of value format
Permissible format of values, from definitions in objects.formats
pattern
Regular expression constraining string values enum
Exclusive list of valid values, if present maximum
Maximum for numeric values minimum
Minimum for numeric values *
JSON-schema fields to further constrain values -
objects.datatypes
Field Description display_name
Human-friendly name description
Term definition value
String value of datatype
-
objects.suffixes
Field Description display_name
Human-friendly name description
Term definition value
String value of suffix
unit
Interpretation of values in a data file with the given suffix maxValue
Maximum permissible value in a data file with the given suffix minValue
Minimum permissible value in a data file with the given suffix anyOf
Used to describe multiple permissible units -
objects.extensions
Field Description display_name
Human-friendly name description
Term definition value
String value of extension
-
objects.formats
Field Description display_name
Human-friendly name description
Term definition pattern
Regular expression defining format -
objects.files
Field Description display_name
Human-friendly name description
Term definition file_type
Indicator that the file is a regular file ( "regular"
) or directory ("directory"
) -
objects.enums
Field Description display_name
Human-friendly name description
Term definition value
String value of enum
The rules.*
namespace contains most of the validatable content of the schema,
apart from value constraints that can be encoded in objects
.
There are several types of rule, and this section is subject to reconsolidation as patterns are found.
Core concepts are expressions (defined above), requirement levels and issues.
BIDS follows RFC 2119 and has three requirement levels: OPTIONAL, RECOMMENDED and REQUIRED.
In the schema, we use optional
, recommended
and required
.
A rule interpreter (validator) is expected to treat:
- missing REQUIRED data/metadata as an error,
- missing RECOMMENDED data/metadata as a warning,
- and silently pass over missing OPTIONAL data.
BIDS also defines a level DEPRECATED
, rendered in the schema as deprecated
,
and corresponding to a warning if the data/metadata is present.
Issues are messages intended to be communicated to a dataset curator to indicate an issue with their dataset.
They have a code and severity as well:
Field | Description |
---|---|
code |
Issue identifier, such as EVENTS_TSV_MISSING |
level |
Issue severity (warning or error ) |
message |
Message for display to a user |
A level of warning
corresponds to a rule in the specification that is RECOMMENDED,
while a level of error
corresponds to a rule that is REQUIRED.
In some cases, an issue is contained next to a level: required
or level: recommended
as part of a larger rule.
In these cases, the level
field should be omitted from the issue
to avoid duplication or conflict.
A significant portion of BIDS is devoted to the naming of files, and almost all filenames consist of entities, a suffix, an extension, and a data type. Exceptions will be noted below.
rules.files
contains the following subdivisions.
Namespace | Description |
---|---|
rules.files.common.core |
Files and directories that reside at the top level of datasets |
rules.files.common.tables |
Tabular metadata files that associate metadata with entities |
rules.files.raw.* |
Raw data and metadata files that have entities, suffixes, datatypes and extensions |
rules.files.deriv.* |
Derivative data and metadata files that have entities, suffixes, datatypes and extensions |
rules.files.common.core
describes files that have little-to-no variability in their form.
These either have a single path
field, or a stem
field and a list of extensions
:
Field | Description |
---|---|
level |
Requirement level of file, one of (optional , recommended , required , deprecated ) |
path |
Location of file, relative to dataset root; mutually exclusive with stem and extensions |
stem |
Name of file, relative to dataset root, up to but not including the extension; mutually exclusive with path |
extensions |
List of valid extension strings, including the initial dot (. ); mutually exclusive with path |
These are the entries for dataset_description.json
and README
:
dataset_description:
level: required
path: dataset_description.json
README:
level: required
stem: README
extensions:
- ''
- .md
- .rst
- .txt
Here, README
and README.md
are both valid, while only dataset_description.json
is permitted.
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
to construct the filename.
The valid fields are:
Field | Description |
---|---|
level |
Requirement level of file, one of (optional , recommended , required , deprecated ) |
stem |
Name of file, relative to dataset root, up to but not including the extension; mutually exclusive with entities |
entities |
Object where the keys are entries in objects.entities . The value is a requirement level. |
extensions |
List of valid extension strings, including the initial dot (. ) |
For example:
participants:
level: optional
stem: participants
extensions:
- .tsv
- .json
sessions:
suffixes:
- sessions
extensions:
- .tsv
- .json
entities:
subject: required
Note that these files do not have a datatype
, but otherwise follow the same rules as above.
rules.files.raw
and rules.files.deriv
contain series of related rules.
These are largely grouped by datatype, but file types that appear in multiple locations may be grouped together.
The files described take the form:
[sub-<label>/][ses-<label>/]<datatype>/<entities>_<suffix><extension>
Rules have the following fields:
Field | Description |
---|---|
suffixes |
List of suffixes found in objects.suffixes |
extensions |
List of valid extension strings, including initial dot (. ) |
datatypes |
List of datatypes found in objects.datatypes |
entities |
Object where the keys are entries in objects.entities . The value is either a requirement level or an object described by the following table. |
Field | Requirement level | Description |
---|---|---|
level |
REQUIRED | Requirement level of field, one of (optional , recommended , required , deprecated ) |
format |
OPTIONAL | Override of entity field - Permissible format of values, either label or index |
enum |
OPTIONAL | Override of entity field - Exclusive list of valid values, if present |
As an example, let us look at a (modified) part of meg.yaml
:
meg:
suffixes:
- meg
extensions:
- .fif
datatypes:
- meg
entities:
subject: required
session: optional
task: required
acquisition: optional
run: optional
processing: optional
split: optional
crosstalk:
suffixes:
- meg
extensions:
- .fif
datatypes:
- meg
entities:
subject: required
session: optional
acquisition:
level: required
enum:
- crosstalk
In this case, the first group has one suffix: meg
.
The second group has the same suffix (meg
), but describes different rules for files with that suffix.
While the valid extension is the same for both groups (.fif
), the entities are not.
Specifically, files in the first group may have task
, run
, processing
, and split
entities,
while files in the second group may not.
Also, when files in the second group have the acq
entity, the associated value MUST be crosstalk
.
A common derivatives type is preprocessed data, where the type of the generated data is the same
as the input data.
BIDS Derivatives specifies that these files may be distinguished from raw data
with the new entities space-<label>
or desc-<label>
.
This rule is encoded:
meg_meg_common:
$ref: rules.files.raw.meg.meg
entities:
$ref: rules.files.raw.meg.meg.entities
space: optional
description: optional
When expanded, this becomes:
meg_meg_common:
suffixes:
- meg
extensions:
- .fif
datatypes:
- meg
entities:
subject: required
session: optional
task: required
acquisition: optional
run: optional
processing: optional
split: optional
space: optional
description: optional
Tabular data and JSON sidecar files follow a similar pattern:
Name | Value | |
---|---|---|
JSON | field | value |
TSV | column header | column values |
In the specification, groups of fields/columns are described together in a table
that includes the name of the field/column, the requirement level and a description.
The definitions, including name and description, appear in objects.metadata
,
and the columns appear in objects.columns
.
Here, we define YAML "tables" that can be rendered in the specification. These take the form:
RuleName:
selectors:
- expression1
- expression2
fields:
- FieldName1:
level: recommended
level_addendum: required if XYZ
description_addendum: Additional text following object description.
- FieldName2: optional
RuleNameReq:
selectors:
- expression1
- expression2
- expression3
fields:
- FieldName1:
level: required
issue:
code: ISSUE_NAME
message: A description of the problem for a user
Here we show an example of two fields, one that is RECOMMENDED in most cases but REQUIRED in another, the other of which is OPTIONAL.
selectors
indicate whether the current rule applies to a given file.
This is not rendered in the text, but may be used by a validator.
fields
is an object with keys that appear in objects.metadata
/objects.columns
.
If the value is a string, then it is a requirement level.
If it is an object, then the it has the following fields
Field | Requirement level | Description |
---|---|---|
level |
REQUIRED | Requirement level of field, one of (optional , recommended , required , deprecated ) |
level_addendum |
OPTIONAL | Additional text to describe cases where requirement level changes |
description_addendum |
OPTIONAL | Additional text to follow the objects.metadata.<fieldname>.description |
issue |
OPTIONAL | issue object, if additional communication is warranted |
The second table implements the change in the first table's level_addendum
.
The expression3
selector indicates the additional case where the more stringent
rule is applied.
-
rules.sidecars.*
Field Description selectors
List of expressions; any evaluating false indicate rule does not apply fields
Object with keys that may be found in objects.metadata
, values either a requirement level or an object -
rules.tabular_data.*
Field Description selectors
List of expressions; any evaluating false indicate rule does not apply columns
Object with keys that may be found in objects.columns
, values either a requirement level or an objectinitial_columns
An optional list of columns that must be the first N columns of a file index_columns
An optional list of columns that uniquely identify a row. additional_columns
Indicates whether additional columns may be defined. One of allowed
,allowed_if_defined
andnot_allowed
.
The following tables demonstrate how mutual exclusive, required fields,
may be set in rules.sidecars.*
:
MRIFuncRepetitionTime:
selectors:
- modality == "mri"
- datatype == "func"
- '!("VolumeTiming" in sidecar)'
- match(extension, "^\.nii(\.gz)?$")
fields:
RepetitionTime:
level: required
level_addendum: mutually exclusive with `VolumeTiming`
MRIFuncVolumeTiming:
selectors:
- modality == "mri"
- datatype == "func"
- '!("RepetitionTime" in sidecar)'
- match(extension, "^\.nii(\.gz)?$")
fields:
VolumeTiming:
level: required
level_addendum: mutually exclusive with `RepetitionTime`
An additional check will be required to assert that both are not present, but these tables may be combined for rendering purposes.
Here we present an example rule in rules.tabular_data.eeg
:
EEGChannels:
selectors:
- datatype == "eeg"
- suffix == "channels"
- extension == ".tsv"
initial_columns:
- name__channels
- type__channels
- units
columns:
name__channels: required
type__channels: required
units: required
description: optional
sampling_frequency: optional
reference: optional
low_cutoff: optional
high_cutoff: optional
notch: optional
status: optional
status_descriptions: optional
additional_columns: allowed_if_defined
rules.checks
can contain more complex rules. Structurally, these are similar to sidecar rules,
in that they have selectors.
They additionally have a checks
list, and an explicit issue.
Field | Description |
---|---|
issue |
Issue code object (see Issues |
selectors |
List of expressions; any evaluating false indicate rule does not apply |
checks |
List of expressions; any evaluating false indicate rule is violated and issue should be raised |
EventsMissing:
issue:
code: EVENTS_TSV_MISSING
message: |
Task scans should have a corresponding events.tsv file.
If this is a resting state scan you can ignore this warning or rename the task to include the word "rest".
level: warning # could be an error with the proper selectors, I think
selectors:
- '"task" in entities'
- '!match(entities.task, "rest")'
- suffix != "events"
checks:
- '"events" in associations'
-
rules.entities
- This file contains a list of keys intoobjects.entities
and simply defines the order in which entities, when present, MUST appear in filenames -
rules.common_principles
- This file contains a list of terms that appear inobjects.common_principles
that determines the order they appear in the specification
-
rules.modalities
- The keys in this file are the modalities, the values objects with the following field:Field Description datatypes
List of datatypes mapping to modality -
rules.dataset_metadata
- These are similar torules.sidecars.*
, for JSON files at the root level. This is likely to go away in favor of other approaches. -
rules.errors
- This file describes errors that cannot be expressed in the schema. This provides common codes and language that implementing validators can use to ensure the same problems are reported to users in the same way.
File SCHEMA_VERSION
in the top of the directory contains a semantic
version (MAJOR.MINOR.PATCH
) for the schema (how it is organized).
Note that while in 0.
series, breaking changes are
permitted without changing the MAJOR
(leading) component of the version.
Going forward, the 2nd, MINOR
indicator should be
incremented whenever schema organization introduces "breaking changes":
changes which would cause existing tools reading schema to
adjust their code to be able to read it again.
Additions of new components to the schema should increment the last,
PATCH
, component of the version so that tools could selectively
enable/disable loading specific components of the schema.
With the release of 1.0.0
version of the schema,
we expect that the MAJOR
component
will be incremented whenever schema organization introduces "breaking changes",
MINOR
- when adding new components to the schema,
and PATCH
- when fixing errors in existing components.
The BIDS Schema is compiled into a single, dereferenced object during
the ReadTheDocs build of the specification.
This object is published as a JSON document that can be found at /schema.json
at the root of the specification.
For example, the schema used to construct the 1.8.0 release of BIDS can be found at
https://bids-specification.readthedocs.io/en/v1.8.0/schema.json,
and the latest version that includes unreleased changes to BIDS and the schema may
be found at https://bids-specification.readthedocs.io/en/latest/schema.json.
The JSON version of the schema contains schema_version
and bids_version
keys
that identify the state of both the schema and the specification at the time it was
compiled.
The metaschema.json
file is a meta-schema that uses the JSON Schema language to
formalize the allowable directories, files, fields and values of the BIDS schema,
ensuring consistency across the entire schema directory. Validation of the schema is
incorporated into the CI, so any changes that are inconsistent will be flagged before
inclusion.