NWB annotation quickstart

This tutorial provides a step-by-step guide to adding HED (Hierarchical Event Descriptors) annotations to NWB (Neurodata Without Borders) files using the ndx-hed extension. See HED annotation quickstart for guidelines on what annotations to choose.

We assume you have basic familiarity with creating NWB files in Python and focus on the mechanics of adding HED annotations to various NWB data structures. See the NWB Software overview for information about setting up your NWB software environment.

The examples in this tutorial use the ndx-hed extension, which provides three main classes for HED integration in NWB files. These classes work with any NWB DynamicTable, making HED annotation flexible across different types of neurophysiology data.

What is NWB?

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 intracranial EEG (IEEG). NWB organizes all of the data from one recording session into a single file.

In contrast to BIDS (Brain Imaging Data Structure), which organizes an entire experiment’s data across multiple files and directories, NWB focuses on individual recording sessions with all associated metadata, events, and measurements stored together.

Installing ndx-hed

The ndx-hed extension for Python can be installed using pip:

pip install -U ndx-hed

MATLAB support

The ndx-hed extension for MATLAB is under development and not currently available.

How HED works in NWB

HED annotations in NWB are integrated through the ndx-hed extension, which provides specialized classes that extend standard NWB data structures. The extension adds HED validation at creation time, ensuring annotations are correct before data is saved.

The three main classes

The ndx-hed extension provides three classes for different annotation scenarios:

ndx-hed classes for HED annotation

Class

Purpose

Use cases

HedLabMetaData

HED schema version and definitions

Required for all HED validation

HedTags

Row-specific HED annotations

Any DynamicTable

HedValueVector

Column-wide HED templates

Shared annotations with value placeholders (#)

HedLabMetaData (required)

Every NWB file using HED must include a HedLabMetaData object specifying the HED schema version. This is added to the NWB file’s lab metadata:

from pynwb import NWBFile
from ndx_hed import HedLabMetaData
from datetime import datetime

# Create NWB file
nwbfile = NWBFile(
    session_description="Example session with HED annotations",
    identifier="example_session_001",
    session_start_time=datetime.now()
)

# Add HED schema metadata (required)
hed_metadata = HedLabMetaData(hed_schema_version="8.4.0")
nwbfile.add_lab_meta_data(hed_metadata)

HED schema version

We recommend using the latest version of HED (currently 8.4.0). See Understanding HED versions for details on versioning.

Basic HED annotation

The most common use case is adding HED annotations to an events or trials table. This requires:

  1. Adding HedLabMetaData to the NWB file (shown above)

  2. Creating a HedTags column in your table

  3. Adding rows with HED annotations

Adding HED to trials table

Here’s a complete example of adding HED annotations to the trials table:

from ndx_hed import HedTags

# Add HED column to trials table
nwbfile.add_trial_column(
    name="HED",
    col_cls=HedTags,
    data=[],
    description="HED annotations for trials"
)

# Add trials with HED annotations
nwbfile.add_trial(
    start_time=0.0,
    stop_time=1.0,
    HED="Sensory-event, Visual-presentation, (Experimental-stimulus, Target)"
)

nwbfile.add_trial(
    start_time=1.5,
    stop_time=2.5,
    HED="Agent-action, (Press, Mouse-button), Correct-action"
)

Each trial now has a HED annotation describing what happened during that time period. The annotations are validated when the trial is added.

Creating standalone HedTags

You can also create HedTags objects independently before adding them to a table:

from ndx_hed import HedTags

# Create a HedTags vector with 3 annotations
tags = HedTags(
    hed_version='8.4.0',
    data=[
        "Sensory-event, Visual-presentation",
        "Agent-action, Press, Correct-action",
        "Sensory-event, Auditory-presentation"
    ]
)

# Add another annotation
tags.add_row("Agent-action, Press, Incorrect-action")

This creates a validated HedTags object with 4 elements. Each element is a complete HED string.

Annotation recipe for NWB events

When annotating NWB events, follow the same principles as general HED annotation. Start by categorizing each event:

Standard event categories

  • Sensory-event: Stimulus presentation (visual, auditory, etc.)

  • Agent-action: Participant or subject actions

  • Data-feature: Computed features or annotations

  • Experiment-control: Recording control events

  • Experiment-structure: Block structure, conditions

  • Measurement-event: Sensor readings, observations

See HED annotation quickstart for detailed guidelines on selecting appropriate tags for each event type.

Example: Visual experiment

Here’s how you might annotate a simple visual discrimination experiment:

# Add HED column
nwbfile.add_trial_column(
    name="HED",
    col_cls=HedTags,
    data=[],
    description="HED annotations for trial events"
)

# Trial with target stimulus
nwbfile.add_trial(
    start_time=0.0,
    stop_time=1.0,
    stimulus="face",
    response="correct",
    HED="Sensory-event, Visual-presentation, "
        "(Experimental-stimulus, Target, (Face, Image))"
)

# Trial with distractor
nwbfile.add_trial(
    start_time=2.0,
    stop_time=3.0,
    stimulus="house",
    response="correct_rejection",
    HED="Sensory-event, Visual-presentation, "
        "(Experimental-stimulus, Non-target, Distractor, (Building, Image))"
)

Integration with ndx-events

The ndx-events extension provides specialized data structures for representing event information. HED annotations can be added to any of its table classes:

ndx-events tables that support HED

Table

Purpose

Analogy

EventsTypesTable

Event type definitions

Similar to BIDS events.json

EventsTable

Event instances

Similar to BIDS events.tsv

TtlTypesTable

TTL type definitions

Hardware trigger types

TtlTable

TTL instances

Hardware trigger events

Example with EventsTable

from ndx_events import EventsTable
from ndx_hed import HedTags

# Create events table with HED column
events_table = EventsTable(
    name="task_events",
    description="Task events with HED annotations"
)

# Add HED column
events_table.add_column(
    name="HED",
    col_cls=HedTags,
    description="HED annotations for events"
)

# Add events
events_table.add_row(
    timestamp=0.5,
    label="stimulus_onset",
    HED="Sensory-event, Visual-presentation"
)

events_table.add_row(
    timestamp=1.2,
    label="button_press",
    HED="Agent-action, Press, Correct-action"
)

# Add to NWB file
nwbfile.add_acquisition(events_table)

HED validation

HED annotations are validated when you create HedTags objects, but you can also validate an entire NWB file:

from ndx_hed.utils.hed_nwb_validator import HedNWBValidator

# Create validator with schema version
validator = HedNWBValidator(hed_metadata)

# Validate entire file
issues = validator.validate_file(nwbfile)

if not issues:
    print("✓ All HED annotations are valid!")
else:
    print("Found issues:")
    for issue in issues:
        print(f"  - {issue}")

Validation ensures that:

  • All HED tags are in the schema

  • Tag structure is correct (parentheses, commas)

  • Required tags are present

  • Value placeholders are used correctly

Converting BIDS events to NWB

If you have BIDS-formatted event files with HED annotations, you can convert them to NWB format:

from ndx_hed.utils.bids2nwb import extract_meanings, get_events_table
import pandas as pd
import json

# Load BIDS events and sidecar
events_df = pd.read_csv("sub-001_task-faces_events.tsv", sep="\t")
with open("task-faces_events.json") as f:
    sidecar_dict = json.load(f)

# Extract HED annotations
meanings = extract_meanings(sidecar_dict)

# Create NWB EventsTable
events_table = get_events_table(
    "task_events",
    "Face processing task events",
    events_df,
    meanings
)

# Add to NWB file
nwbfile.add_acquisition(events_table)

This preserves your HED annotations when migrating from BIDS to NWB format.

Saving and reading NWB files

Once you’ve added HED annotations, save the NWB file normally:

from pynwb import NWBHDF5IO

# Write file
with NWBHDF5IO("annotated_session.nwb", mode="w") as io:
    io.write(nwbfile)

# Read file back
with NWBHDF5IO("annotated_session.nwb", mode="r") as io:
    nwbfile_in = io.read()
    
    # Access HED annotations
    for trial in nwbfile_in.trials:
        print(f"Trial {trial.id}: {trial.HED}")

HED annotations are preserved through the save/load cycle and can be accessed like any other NWB data.

Advanced usage

Using HedValueVector for templates

For repeated annotations with varying values, use HedValueVector:

from ndx_hed import HedValueVector

# Create template with placeholder
response_template = HedValueVector(
    hed_version='8.4.0',
    data=[
        "Agent-action, Press, (Reaction-time/#, Duration/#)",
        "Agent-action, Press, (Reaction-time/#, Duration/#)"
    ],
    description="Response events with reaction time"
)

The # placeholder gets replaced with actual values during analysis.

Custom HED definitions

For lab-specific terms, you can add definitions to HedLabMetaData:

hed_metadata = HedLabMetaData(
    hed_schema_version="8.4.0",
    hed_definition="(Definition/MyCondition, (Condition-variable/A, Condition-variable/B))"
)
nwbfile.add_lab_meta_data(hed_metadata)

Then reference the definition in your annotations:

nwbfile.add_trial(
    start_time=0.0,
    stop_time=1.0,
    HED="Def/MyCondition"
)

Additional resources

Next steps

After completing this quickstart:

  1. Explore examples: See the ndx-hed examples directory for complete working examples

  2. Learn annotation principles: Review HED annotation quickstart for choosing appropriate tags

  3. Validate your data: Use HED validation tools to ensure correctness

  4. Search and analyze: Learn about HED search capabilities for finding events

  5. Generate summaries: Use HED summaries to analyze your annotated data