PyNWB: Python API for Neurodata Without Borders

repository·dev·Indexed 42 days ago

https://github.com/neurodatawithoutborders/pynwb

A 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.

Tokens
24K
Snippets
76
Records
122
Agent score
50%

What's inside PyNWB

  1. What is PyNWB?

    dev
    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.
  2. Overview of PyNWB

    dev
    PyNWB 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.
  3. How NWB specifications and neurodata_types work

    dev

    A neurodata_type (or data_type) is a reusable definition within a format specification. The NWB format is essentially a collection of these types. For example, NWBFile is a GroupSpec that includes other types like TimeSeries and ElectrodeGroup.

    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.

  4. Coordinating PyNWB releases with nwb-schema

    dev

    PyNWB relies on the nwb-schema repository. To ensure stability and traceability, PyNWB releases must always contain a released version of nwb-schema.

    Workflow for Schema Updates

    1. Update Submodule: When nwb-schema's dev branch is updated, update the schema_x.y.z branch in PyNWB to point to the latest commit of the nwb-schema submodule.
    2. API Changes: If a schema change requires PyNWB code changes, create a new branch and merge it into the schema_x.y.z branch using 'squash and merge'.
    3. Final Release: Once nwb-schema x.y.z is officially released, ensure the schema_x.y.z branch in PyNWB points to the new release tag, then merge that branch into dev using standard 'merge' (do not squash) to preserve the history of API/schema changes.
  5. How Containers, Builders, and ObjectMappers interact

    dev

    The 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.

    ObjectMappers are constructed using a Spec and are responsible for the bidirectional conversion between Container and Builder representations.

  6. Manage NWB extensions using Namespaces

    dev

    To 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 Namespace class that includes the core namespace.
    • NamespaceCatalog: Used for managing multiple namespaces.
    • NamespaceBuilder: A utility class for building extensions.

    Key classes are located in hdmf.spec.namespace and pynwb.spec.

  7. Understand PyNWB versioning and source distributions

    dev

    PyNWB uses versioneer for versioning. When downloading source code for a complete installation, avoid using the .zip or .tar.gz archives automatically generated by GitHub releases, as these do not include submodules (like nwb-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 convention pynwb-{version}.tar.gz.

  8. Understand the core PyNWB architectural components

    dev

    PyNWB is built on a modular architecture designed to separate data representation, specification, and I/O. The four primary abstractions are:

    1. Container: In-memory objects that serve as the primary interface for most applications. There is one Container for each neurodata_type in the NWB schema (e.g., pynwb.ecephys, pynwb.ophys). They behave similarly to a table row.
    2. 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, and ReferenceBuilder.
    3. Spec (Specification): Python representations of YAML specifications. They define the structure, data types, and dimensions of NWB components. Key classes include NWBAttributeSpec, NWBGroupSpec, NWBDatasetSpec, and NWBLinkSpec.
    4. 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.
  9. Create a new tutorial

    dev

    Tutorials in pynwb are generated using sphinx-gallery. Source files are located in docs/gallery/, organized into subfolders representing gallery subsections.

    Steps to add a tutorial:

    1. Add the Python file: Place a new .py file in the appropriate subfolder within docs/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.
    2. Manage output files: If your tutorial script generates files, add their names to the files_to_remove variable within the clean_up_tests() function in test.py. This ensures the test suite cleans up generated artifacts after the sphinx-gallery tests run.

    3. Set position (Optional): By default, tutorials are sorted alphabetically. To specify a custom order, update the GALLERY_ORDER variable of the CustomSphinxGallerySectionSortKey class in docs/source/conf.py.

    4. Build and Verify:

      • Build the documentation: cd docs && make html
      • View the result: open docs/_build/html/index.html
    5. 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.html
  10. Cite PyNWB in research

    dev

    If 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},
      }
  11. Export a modified version of an NWB file

    dev

    You can create a modified version of an existing NWB file while preserving the original by using the export feature.

    Workflow:

    1. Open the existing file using NWBHDF5IO in read mode (mode='r').
    2. Read the file into an NWBFile object.
    3. Modify the NWBFile object or its child objects (e.g., add/remove containers, change attributes).
    4. If you changed container attributes, you must call nwbfile.set_modified() before exporting.
    5. Open a new NWBHDF5IO in write mode (mode='w') and call export(src_io=read_io, nwbfile=nwbfile).

    Important Notes:

    • Dataset Modifications: Modifications to h5py.Dataset objects act directly on the source file on disk. To avoid unintentional changes to the source file, ensure the source is opened with mode='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)
  12. Publish a release on conda-forge

    dev

    Releases 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:

    1. Fork and clone the pynwb-feedstock repository.
    2. Download the source .tar.gz for the new release.
    3. Update recipe/meta.yaml with the new version and sha256 values.
    4. 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