Getting Started#
Overview#
The ndx-hed extension integrates HED (Hierarchical Event Descriptors) annotations into NWB (Neurodata Without Borders) neurophysiology data files.
Neurodata Without Borders (NWB) is a data standard for organizing neurophysiology data. NWB is used extensively as the data representation for single cell and animal recordings as well as human neuroimaging modalities such as IEEG. NWB organizes all of the data from one recording session into a single file.
HED (Hierarchical Event Descriptors) is a system of standardized vocabularies and supporting tools that allows fine-grained annotation of data. HED annotations can now be used in NWB to provide HED annotations for any NWB dynamic table.
The NWB annotation quickstart explains how to get started with HED.
Note
All examples referenced in this documentation can be found in the examples directory
and can be run directly from the examples/ folder.
Core Classes#
The ndx-hed extension provides three main classes for different annotation patterns, plus
HedDurationVector, a HedValueVector for the duration column of an EventsTable:
HedLabMetaData#
Purpose: Required metadata container for HED schema specification and lab-specific definitions.
Key Features:
Stores the HED schema version(s) for the entire NWB file
Supports standard and library schemas, alone or combined, with optional namespace prefixes
Manages custom HED definitions
Must be added to
NWBFilebefore using any HED annotationsMust be named “hed_schema” (enforced by constructor)
Use Cases: Required for all HED validation and schema management.
from ndx_hed import HedLabMetaData
# Basic schema specification
hed_metadata = HedLabMetaData(hed_schema_version="8.5.0")
nwbfile.add_lab_meta_data(hed_metadata)
# With custom definitions
definitions = "(Definition/Fixation-task, (Task, Fixate))"
hed_metadata = HedLabMetaData(hed_schema_version="8.5.0", definitions=definitions)
# A library schema merged with its standard partner (one namespace, comma-joined)
hed_metadata = HedLabMetaData(hed_schema_version="8.5.0,score_2.2.0")
# Several schemas in separate namespaces: pass a list; tags from the prefixed
# schema are written as sc:Tag. The list is stored as a JSON array string.
hed_metadata = HedLabMetaData(hed_schema_version=["8.5.0", "sc:score_2.2.0"])
hed_metadata.get_hed_schema_version() # '["8.5.0", "sc:score_2.2.0"]'
hed_schema_version takes one version string, a comma-joined string of versions that share one
namespace, a JSON array string, or a list of version strings. Each version is [namespace:]X.Y.Z
for the standard schema or [namespace:]library_X.Y.Z for a library schema. The value stored in the
file is always one string, which hedtools’ load_schema_version reads back unchanged.
HedValueVector#
Purpose: Column-wide HED templates with value placeholders.
Key Features:
Stores numerical/categorical data with HED templates
Uses
#placeholder for values (e.g., “Duration/# s”)HED annotation applies to entire column
Ideal for parametric annotations
Use Cases: Shared annotations with value placeholders (#).
from ndx_hed import HedValueVector
# Create template-based annotations
duration_vector = HedValueVector(
name="duration",
description="Trial duration with HED annotation for a sensory stimulus",
data=[1.0, 1.5, 2.0],
hed="(Duration/# s, (Sensory-event, Experimental-stimulus))"
)
trials_table.add_column(duration_vector)
HedDurationVector#
Purpose: The duration column of an EventsTable with a HED template.
Key Features:
Extends the core
DurationVectorData(durations in seconds), so code that reads event durations sees an ordinary duration columnCarries one HED template with a single
#, as aHedValueVectordoes, and is validated the same wayBuilt by
get_events_tablefor a BIDSdurationcolumn that the sidecar annotatesDurationmust be in a top-level group with what it applies to in a nested group:(Duration/# s, (Sensory-event)). A BIDSonsetcolumn carries no HED, anddurationcannot be categorical.
from pynwb.event import EventsTable, TimestampVectorData
from ndx_hed import HedDurationVector
events_table = EventsTable(
name="stimulus_events",
description="Stimulus events whose durations carry HED",
columns=[
TimestampVectorData(name="timestamp", description="Event onsets", data=[1.0, 2.5]),
HedDurationVector(
name="duration",
description="Stimulus durations",
data=[0.5, 0.75],
hed="(Duration/# s, (Sensory-event, Visual-presentation))",
),
],
)
Comprehensive Examples#
The examples directory contains comprehensive runnable examples:
Example |
Description |
|---|---|
Introduction to the three main classes |
|
Adding HED to trials table |
|
Integration with the PyNWB core EventsTable |
|
Converting BIDS events to NWB with HED |
|
Comprehensive validation examples |
|
End-to-end workflow demonstration |
|
Custom HED definitions usage |
Running Examples:
# Run individual example
cd examples
python 01_basic_hed_classes.py
# Run all examples
cd examples
python run_all_examples.py
Integration with NWB Events#
The ndx-hed extension works seamlessly with the PyNWB core EventsTable (NWBEP001, PyNWB >= 4.2.0)
to provide comprehensive event annotation capabilities. HED annotations can be incorporated in three ways:
Direct HED column - Event-specific annotations
HedValueVector columns - Shared annotations with value placeholders (
HedDurationVectorfor the duration column)Categorical columns with MeaningsTable - Category-based annotations
Example 1: Direct HED Column
from pynwb.event import EventsTable, DurationVectorData
from ndx_hed import HedTags
events_table = EventsTable(
name="stimulus_events",
description="Stimulus events with direct HED annotations"
)
# Add duration column first
events_table.add_column(
name="duration",
description="Event durations",
data=[],
col_cls=DurationVectorData,
)
# Add HED tags column for event-specific annotations
events_table.add_column(
name="HED",
description="HED annotations for each event",
data=[],
col_cls=HedTags,
)
# Add rows of data
events = [
{"timestamp": 1.0, "duration": 0.5, "HED": "Eye-blink-artifact"},
{"timestamp": 25.5, "duration": 3.5, "HED": "Chewing-artifact"},
]
for event in events:
events_table.add_row(event)
Example 2: HedValueVector Columns
from ndx_hed import HedValueVector
events_table = EventsTable(
name="behavioral_events",
description="Events with HedValueVector columns"
)
# Add intensity column with HED value annotation
events_table.add_column(
name="intensity",
description="Brightness of visual stimulus",
data=[],
col_cls=HedValueVector,
hed="(Luminance, Parameter-value/#)",
)
# Add reaction time column with HED annotation
events_table.add_column(
name="reaction_time",
description="Participant response time",
data=[],
col_cls=HedValueVector,
hed="(Behavioral-evidence, Parameter-label/Reaction-time, Time-interval/# s)",
)
Example 3: Categorical Columns with MeaningsTable
from hdmf.common import MeaningsTable
from ndx_hed import HedTags
# A categorical column is a plain column annotated by a MeaningsTable. A MeaningsTable is bound
# to the column it annotates via its ``target`` and its name is auto-derived as
# "{column_name}_meanings".
# Add a plain categorical column and its data first (the target column must exist)
events_table.add_column(
name="stimulus_type",
description="Type of visual stimulus presented",
data=["circle", "square"],
)
# Create a MeaningsTable targeting the stimulus_type column
stimulus_meanings = MeaningsTable(
target=events_table["stimulus_type"],
description="Meanings and HED annotations for stimulus types",
)
# Add meaning definitions
categories = [
("circle", "Circular visual stimulus presented at screen center"),
("square", "Square visual stimulus presented at screen center"),
]
for value, meaning in categories:
stimulus_meanings.add_row(value=value, meaning=meaning)
# Add HED annotations as a column in the MeaningsTable
stimulus_meanings.add_column(
name="HED",
description="HED tags for stimulus categories",
data=[
"Sensory-event, Visual-presentation, Circle",
"Sensory-event, Visual-presentation, Square",
],
col_cls=HedTags,
)
# Attach the MeaningsTable to the EventsTable
events_table.add_meanings_table(stimulus_meanings)
# Later, retrieve the meanings for a column with:
# events_table.get_meanings_for_column("stimulus_type")
See examples/03_events_table_integration.py for detailed demonstrations.
BIDS Compatibility#
BIDS (Brain Imaging Data Structure) is a data standard for organizing neuroimaging and behavioral data from an entire experiment. The standard uses JSON files called “sidecars” to store metadata associated with its tabular files.
The ndx-hed extension provides utilities to convert between BIDS events files and NWB EventsTable format:
from ndx_hed.utils.bids2nwb import extract_meanings, get_events_table, get_bids_tabular, read_bids_tsv
import json
# Convert BIDS to EventsTable. read_bids_tsv reads the file as hedtools does: values exactly as
# written, quotes ordinary characters, and only an empty cell and n/a missing.
bids_events_file_path = "Your_events_path_here.tsv"
bids_sidecar_file_path = "Your_json_sidecar_path_here.json"
events_df = read_bids_tsv(bids_events_file_path)
with open(bids_sidecar_file_path, 'r') as f:
json_data = json.load(f)
meanings = extract_meanings(json_data)
events_table = get_events_table("task_events", "Task events", events_df, meanings)
# Convert EventsTable to BIDS
bids_df, sidecar_dict = get_bids_tabular(events_table)
See examples/04_bids_conversion.py for complete examples.
HED Validation#
Creating HED annotations for NWB data and saving these annotations as part of an NWBFile does not mean
the annotations are valid. HED validation is performed to ensure they conform to the HED schema:
from hed.errors import get_printable_issue_string
from ndx_hed.utils.hed_nwb_validator import HedNWBValidator
# Create validator and validate entire file
hed_metadata = HedLabMetaData(hed_schema_version='8.5.0')
validator = HedNWBValidator(hed_metadata)
# Assume nwbfile has already been created
issues = validator.validate_file(nwbfile)
if not issues:
print("All HED annotations are valid!")
else:
print(f"Validation error: {get_printable_issue_string(issues)}")
Validation Features:
HedNWBValidatorclass for comprehensive validationValidates HED tags against specified schema version
Supports both in-memory and file-based validation
Validates custom definitions
Provides detailed error reporting
See examples/05_hed_validation.py for comprehensive validation examples.
Architecture#
Version 0.2.0 introduces a centralized architecture where:
HedLabMetaDatais added to theNWBFileto specify the HED schema versionAll HED annotations (
HedTags,HedValueVector) reference this central schemaCustom definitions are managed centrally in
HedLabMetaDataValidation uses the schema and definitions from
HedLabMetaData
This ensures consistency across all HED annotations in a file and simplifies schema management.
Use Cases#
Event Annotation: Tag experimental events with standardized descriptors
Trial Categorization: Annotate trial types, conditions, and outcomes
Stimulus Description: Describe sensory stimuli with precise semantic tags
Behavioral Coding: Tag participant actions and responses
Artifact Marking: Identify and categorize data artifacts
Parametric Data: Annotate columns with value-based templates
Cross-Study Integration: Enable data pooling with standardized vocabularies
Compatibility#
Python: 3.10+
Dependencies: pynwb>=4.2.0, hdmf>=6.2.0, hedtools>=1.3.0
EventsTable: Provided by PyNWB core (NWBEP001) as of PyNWB 4.0.0
MATLAB: Under development (not yet available)
Additional Resources#
HED Standards Organization - Official HED specification and resources
HED python tools - Core HED Python library
NWB documentation - PyNWB library documentation
Contributing#
Contributions are welcome! Feel free to submit issues or pull requests to the GitHub repository.