PyNWB: Python API for Neurodata Without Borders
repository·dev·Indexed 42 days ago
https://github.com/neurodatawithoutborders/pynwbA Python library providing a high-level API for reading, interacting with, and writing NWB (Neurodata Without Borders) files. It includes tools for creating custom NWB extensions via NWBNamespaceBuilder, managing data with NWBHDF5IO, handling legacy NWB 1.0 files, and exporting modified NWB files while preserving or regenerating object IDs.
What's inside PyNWB
- PyNWB is a Python package designed for working with Neurodata Without Borders (NWB) files. It provides a high-level API that allows developers and researchers to efficiently interact with neurophysiology data stored in the NWB format. The project aims to facilitate data sharing in neuroscience by providing a unified data format for cellular-based neurophysiology data.
Overview of PyNWB
devPyNWB is a Python package designed for working with NWB (Neurodata Without Borders) files. It provides a high-level API to efficiently interact with neurophysiology data stored in the NWB format. The NWB format is a unified data standard focused on the dynamics of groups of neurons measured under various experimental conditions, aimed at facilitating data sharing in neuroscience.How NWB specifications and neurodata_types work
devA
neurodata_type(ordata_type) is a reusable definition within a format specification. The NWB format is essentially a collection of these types. For example,NWBFileis aGroupSpecthat includes other types likeTimeSeriesandElectrodeGroup.When defining or extending specifications, two key keys are used to manage these types:
neurodata_type_inc: Used to include an existing type.neurodata_type_def: Used to define a new type.
If both keys are provided, you are creating a new type that inherits from or uses an existing type as a base.
Coordinating PyNWB releases with nwb-schema
devPyNWB relies on the
nwb-schemarepository. To ensure stability and traceability, PyNWB releases must always contain a released version ofnwb-schema.Workflow for Schema Updates
- Update Submodule: When
nwb-schema'sdevbranch is updated, update theschema_x.y.zbranch in PyNWB to point to the latest commit of thenwb-schemasubmodule. - API Changes: If a schema change requires PyNWB code changes, create a new branch and merge it into the
schema_x.y.zbranch using 'squash and merge'. - Final Release: Once
nwb-schema x.y.zis officially released, ensure theschema_x.y.zbranch in PyNWB points to the new release tag, then merge that branch intodevusing standard 'merge' (do not squash) to preserve the history of API/schema changes.
- Update Submodule: When
How Containers, Builders, and ObjectMappers interact
devThe relationship between the core components follows a specific flow for data movement:
- Reading Data: Storage Format $\rightarrow$
HDMFIO$\rightarrow$Builder$\rightarrow$ObjectMapper$\rightarrow$Container. - Writing Data:
Container$\rightarrow$ObjectMapper$\rightarrow$Builder$\rightarrow$HDMFIO$\rightarrow$ Storage Format.
ObjectMappersare constructed using aSpecand are responsible for the bidirectional conversion betweenContainerandBuilderrepresentations.- Reading Data: Storage Format $\rightarrow$
Manage NWB extensions using Namespaces
devTo create extensions to the NWB schema, you must use Namespaces. A Namespace contains metadata about the extension creator and provides a unique identifier for specifications.
- Core NWB: Uses the namespace
core. - NWBNamespace: An extension of the generic
Namespaceclass that includes thecorenamespace. - NamespaceCatalog: Used for managing multiple namespaces.
- NamespaceBuilder: A utility class for building extensions.
Key classes are located in
hdmf.spec.namespaceandpynwb.spec.- Core NWB: Uses the namespace
Understand PyNWB versioning and source distributions
devPyNWB uses
versioneerfor versioning. When downloading source code for a complete installation, avoid using the.zipor.tar.gzarchives automatically generated by GitHub releases, as these do not include submodules (likenwb-schema).For a complete source code archive that includes all necessary submodules, use the source distribution (
.tar.gz) generated by GitHub Actions, which follows the naming conventionpynwb-{version}.tar.gz.Understand the core PyNWB architectural components
devPyNWB is built on a modular architecture designed to separate data representation, specification, and I/O. The four primary abstractions are:
- Container: In-memory objects that serve as the primary interface for most applications. There is one Container for each
neurodata_typein the NWB schema (e.g.,pynwb.ecephys,pynwb.ophys). They behave similarly to a table row. - Builder: Intermediary objects used for I/O. Backend readers and writers exchange Builders rather than Containers to decouple the in-memory model from the storage format. Common types include
GroupBuilder,DatasetBuilder,LinkBuilder, andReferenceBuilder. - Spec (Specification): Python representations of YAML specifications. They define the structure, data types, and dimensions of NWB components. Key classes include
NWBAttributeSpec,NWBGroupSpec,NWBDatasetSpec, andNWBLinkSpec. - ObjectMapper: The bridge between Containers and Builders. It maintains the mapping between Container attributes and Spec components, allowing for conversion between the in-memory Container and the I/O-ready Builder.
- Container: In-memory objects that serve as the primary interface for most applications. There is one Container for each
Create a new tutorial
devTutorials in
pynwbare generated usingsphinx-gallery. Source files are located indocs/gallery/, organized into subfolders representing gallery subsections.Steps to add a tutorial:
Add the Python file: Place a new
.pyfile in the appropriate subfolder withindocs/gallery/.- To render output (plots/data): Include the prefix
plot_in the filename (e.g.,plot_my_tutorial.py). - To render code and text only: Use any other filename without the
plot_prefix.
- To render output (plots/data): Include the prefix
Manage output files: If your tutorial script generates files, add their names to the
files_to_removevariable within theclean_up_tests()function intest.py. This ensures the test suite cleans up generated artifacts after thesphinx-gallerytests run.Set position (Optional): By default, tutorials are sorted alphabetically. To specify a custom order, update the
GALLERY_ORDERvariable of theCustomSphinxGallerySectionSortKeyclass indocs/source/conf.py.Build and Verify:
- Build the documentation:
cd docs && make html - View the result:
open docs/_build/html/index.html
- Build the documentation:
Submit: Create a Pull Request to include your tutorial in the repository.
# Build the documentation cd docs make html # View the documentation open docs/_build/html/index.htmlCite PyNWB in research
devIf you use PyNWB in your research, please use the following BibTeX citation to credit the Neurodata Without Borders ecosystem:
@article {10.7554/eLife.78362, article_type = {journal}, title = {{The Neurodata Without Borders ecosystem for neurophysiological data science}}, author = {R"ubel, Oliver and Tritt, Andrew and Ly, Ryan and Dichter, Benjamin K. and Ghosh, Satrajit and Niu, Lawrence and Baker, Pamela and Soltesz, Ivan and Ng, Lydia and Svoboda, Karel and Frank, Loren and Bouchard, Kristofer E.}, editor = {Colgin, Laura L and Jadhav, Shantanu P}, volume = {11{, year = {2022}, month = {oct}, pub_date = {2022-10-04}, pages = {e78362}, citation = {eLife 2022;11:e78362}, doi = {10.7554/eLife.78362}, url = {https://doi.org/10.7554/eLife.78362}, keywords = {Neurophysiology, data ecosystem, data language, data standard, FAIR data, archive}, journal = {eLife}, issn = {2050-084X}, publisher = {eLife Sciences Publications, Ltd}, }Export a modified version of an NWB file
devYou can create a modified version of an existing NWB file while preserving the original by using the
exportfeature.Workflow:
- Open the existing file using
NWBHDF5IOin read mode (mode='r'). - Read the file into an
NWBFileobject. - Modify the
NWBFileobject or its child objects (e.g., add/remove containers, change attributes). - If you changed container attributes, you must call
nwbfile.set_modified()before exporting. - Open a new
NWBHDF5IOin write mode (mode='w') and callexport(src_io=read_io, nwbfile=nwbfile).
Important Notes:
- Dataset Modifications: Modifications to
h5py.Datasetobjects act directly on the source file on disk. To avoid unintentional changes to the source file, ensure the source is opened withmode='r'. If you want to modify a dataset only in the new file, replace the entire object with a new array holding the modified data. - Object IDs: Exported files will have the same object IDs as the original file.
- Container Movement: Moving containers within the same file is not currently supported via export.
with NWBHDF5IO(self.read_path, mode='r') as read_io: nwbfile = read_io.read() # ... # modify nwbfile nwbfile.set_modified() # necessary if modifications are changes to attributes with NWBHDF5IO(self.export_path, mode='w') as export_io: export_io.export(src_io=read_io, nwbfile=nwbfile)- Open the existing file using
Publish a release on conda-forge
devReleases on conda-forge should ideally be handled by the
regro-cf-autotick-bot, which monitors PyPI and automatically creates PRs for new versions. If manual intervention is required, you must first publish to PyPI.Manual Steps:
- Fork and clone the
pynwb-feedstockrepository. - Download the source
.tar.gzfor the new release. - Update
recipe/meta.yamlwith the newversionandsha256values. - Push the changes to your fork and create a Pull Request against the main feedstock repository.
# Update version and sha256 in meta.yaml (Linux) $ sed -i "2s/.*/{% set version = "$release" %}/" recipe/meta.yaml $ sha=$(openssl sha256 /tmp/pynwb-$release.tar.gz | awk '{print $2}') $ sed -i "3s/.*/{$ set sha256 = "$sha" %}/" recipe/meta.yaml # Update version and sha256 in meta.yaml (macOS) $ sed -i -- "2s/.*/{% set version = "$release" %}/" recipe/meta.yaml $ sha=$(openssl sha256 /tmp/pynwb-$release.tar.gz | awk '{print $2}') $ sed -i -- "3s/.*/{$ set sha256 = "$sha" %}/" recipe/meta.yaml- Fork and clone the