.. _tutorials: Tutorials ========= **nwb2bids** comes pre-packaged with some demonstrative tools to help showcase its functionality on various types of NWB file contents. No additional requirements, knowledge, or experience are necessary to run these tutorials! These tutorials demonstrates how to convert NWB file(s) into a BIDS directory structure. Each action allows for alternative use cases based on the command line interface (CLI) or the Python library. All of the tutorials on this page will utilize the example NWB files produced from :ref:`generate-example-file` and :ref:`generate-example-dataset`. You can, of course, feel free to utilize your own NWB files, but the resulting output may appear different than what is shown through the generated examples. .. toctree:: :maxdepth: 2 :hidden: :caption: Getting Started generating_example_file generating_example_dataset .. _tutorial-single-file: Tutorial 1 - Converting a single file ------------------------------------- To convert a single NWB file to BIDS dataset structure, we run the following command: .. tabs:: .. tab:: CLI .. code-block:: bash cd ~/nwb2bids_tutorials/ecephys_tutorial_file nwb2bids convert ecephys.nwb --bids-directory bids_dataset_cli_1 --use-session-labels .. tab:: Python Library .. code-block:: python import pathlib import nwb2bids tutorial_directory = pathlib.Path.home() / "nwb2bids_tutorials/ecephys_tutorial_file" nwb_paths = [tutorial_directory / "ecephys.nwb"] bids_directory = tutorial_directory / "bids_dataset_py_1" bids_directory.mkdir(exist_ok=True) run_config = nwb2bids.RunConfig(bids_directory=bids_directory, use_session_labels=True) converter = nwb2bids.convert_nwb_dataset( nwb_paths=nwb_paths, run_config=run_config, ) .. invisible-code-block: python for variant in ["cli", "py"]: bids_dir = tutorial_base / f"ecephys_tutorial_file/bids_dataset_{variant}_1" expected_structure = { bids_dir: { "directories": {"sub-001"}, "files": {"dataset_description.json", "participants.json", "participants.tsv"}, }, bids_dir / "sub-001": { "directories": {"ses-A"}, "files": {"sub-001_sessions.json", "sub-001_sessions.tsv"}, }, bids_dir / "sub-001" / "ses-A": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-A" / "ecephys": { "directories": set(), "files": { "sub-001_ses-A_ecephys.nwb", "sub-001_ses-A_ecephys.json", "sub-001_ses-A_channels.tsv", "sub-001_ses-A_channels.json", "sub-001_ses-A_electrodes.tsv", "sub-001_ses-A_electrodes.json", "sub-001_ses-A_probes.tsv", "sub-001_ses-A_probes.json", }, }, } nwb2bids.testing.assert_subdirectory_structure( directory=bids_dir, expected_structure=expected_structure ) Notice how we explicitly specified the output BIDS directory in the previous step. We will cover the implicit (current working directory) approach in :ref:`tutorial-implicit-bids-directory`. You can now explore the directory structure and contents of the generated BIDS dataset. You should see something along the lines of: .. code-block:: text ecephys_tutorial_file/bids_dataset_[cli|py]_1/ ├── dataset_description.json ├── participants.tsv ├── participants.json └── sub-001/ ├── sub-001_sessions.tsv ├── sub-001_sessions.json └── ses-A/ └── ecephys/ ├── sub-001_ses-A_ecephys.nwb ├── sub-001_ses-A_ecephys.json ├── sub-001_ses-A_channels.tsv ├── sub-001_ses-A_channels.json ├── sub-001_ses-A_electrodes.tsv ├── sub-001_ses-A_electrodes.json ├── sub-001_ses-A_probes.tsv └── sub-001_ses-A_probes.json .. _tutorial-multiple-files: Tutorial 2 - Converting directories ----------------------------------- To convert all of the NWB files under a directory to BIDS, we can run the following command: .. tabs:: .. tab:: CLI .. code-block:: bash cd ~/nwb2bids_tutorials/ecephys_tutorial_dataset nwb2bids convert some_sessions --bids-directory bids_dataset_cli_2 .. tab:: Python Library .. code-block:: python import pathlib import nwb2bids tutorial_directory = pathlib.Path.home() / "nwb2bids_tutorials/ecephys_tutorial_dataset" nwb_paths = [tutorial_directory / "some_sessions"] bids_directory = tutorial_directory / "bids_dataset_py_2" bids_directory.mkdir(exist_ok=True) run_config = nwb2bids.RunConfig(bids_directory=bids_directory) converter = nwb2bids.convert_nwb_dataset( nwb_paths=nwb_paths, run_config=run_config, ) .. invisible-code-block: python for variant in ["cli", "py"]: bids_dir = tutorial_base / f"ecephys_tutorial_dataset/bids_dataset_{variant}_2" expected_structure = { bids_dir: { "directories": {"sub-001"}, "files": {"dataset_description.json", "participants.json", "participants.tsv"}, }, bids_dir / "sub-001": { "directories": {"ses-A", "ses-B"}, "files": {"sub-001_sessions.json", "sub-001_sessions.tsv"}, }, bids_dir / "sub-001" / "ses-A": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-A" / "ecephys": { "directories": set(), "files": { "sub-001_ses-A_ecephys.nwb", "sub-001_ses-A_ecephys.json", "sub-001_ses-A_channels.tsv", "sub-001_ses-A_channels.json", "sub-001_ses-A_electrodes.tsv", "sub-001_ses-A_electrodes.json", "sub-001_ses-A_probes.tsv", "sub-001_ses-A_probes.json", }, }, bids_dir / "sub-001" / "ses-B": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-B" / "ecephys": { "directories": set(), "files": { "sub-001_ses-B_ecephys.nwb", "sub-001_ses-B_ecephys.json", "sub-001_ses-B_channels.tsv", "sub-001_ses-B_channels.json", "sub-001_ses-B_electrodes.tsv", "sub-001_ses-B_electrodes.json", "sub-001_ses-B_probes.tsv", "sub-001_ses-B_probes.json", }, }, } nwb2bids.testing.assert_subdirectory_structure( directory=bids_dir, expected_structure=expected_structure ) And our BIDS dataset should look like: .. code-block:: text ecephys_tutorial_dataset/bids_dataset_[cli|py]_2/ ├── dataset_description.json ├── participants.tsv ├── participants.json └── sub-001/ ├── sub-001_sessions.tsv ├── sub-001_sessions.json ├── ses-A/ │ └── ecephys/ │ ├── sub-001_ses-A_ecephys.nwb │ ├── sub-001_ses-A_ecephys.json │ ├── sub-001_ses-A_channels.tsv │ ├── sub-001_ses-A_channels.json │ ├── sub-001_ses-A_electrodes.tsv │ ├── sub-001_ses-A_electrodes.json │ ├── sub-001_ses-A_probes.tsv │ └── sub-001_ses-A_probes.json └── ses-B/ └── ecephys/ ├── sub-001_ses-B_ecephys.nwb ├── sub-001_ses-B_ecephys.json ├── sub-001_ses-B_channels.tsv ├── sub-001_ses-B_channels.json ├── sub-001_ses-B_electrodes.tsv ├── sub-001_ses-B_electrodes.json ├── sub-001_ses-B_probes.tsv └── sub-001_ses-B_probes.json .. tip :: BIDS recommends certain best practices for deciding on high-quality labels for entities such as subjects and sessions. **nwb2bids** simply extracts these values from the NWB contents, so it is a good idea to ensure that these values are appropriate prior to conversion to BIDS. Read more about `Common Principles: Filesystem structure `_. In particular, see the section about `richness versus distinctness `_. .. _tutorial-multiple-inputs: Tutorial 3 - Multiple inputs ---------------------------- Using the example dataset generated in :ref:`generate-example-dataset`, we will now show how to convert any mix of individual NWB files and directories containing NWB files. Attentive readers may have noticed that in :ref:`tutorial-multiple-files`, we only converted the files under ``some_sessions/`` while ignoring the other NWB file at the top level (including the one called ``DO_NOT_CONVERT.nwb``). We can select which files and directories to convert like so: .. tabs:: .. tab:: CLI .. tabs:: .. tab:: Unix / macOS .. code-block:: bash cd ~/nwb2bids_tutorials/ecephys_tutorial_dataset nwb2bids convert ecephys_session_3.nwb some_sessions \ --bids-directory bids_dataset_cli_3 --use-session-labels .. tab:: Windows .. skip: next .. code-block:: bash cd ~/nwb2bids_tutorials/ecephys_tutorial_dataset nwb2bids convert ecephys_session_3.nwb some_sessions ^ --bids-directory bids_dataset_cli_3 --use-session-labels The command line can take any number of inputs (separated by spaces) prior to other flags such as ``--bids-directory``. Shell globs, such as ``*.nwb``, could be used as shown in section :ref:`tutorial-implicit-bids-directory`. These inputs can be any mix of files or directories. .. tab:: Python Library .. code-block:: python import pathlib import nwb2bids tutorial_directory = pathlib.Path.home() / "nwb2bids_tutorials/ecephys_tutorial_dataset" nwb_paths = [ tutorial_directory / "ecephys_session_3.nwb", tutorial_directory / "some_sessions", ] bids_directory = tutorial_directory / "bids_dataset_py_3" bids_directory.mkdir(exist_ok=True) run_config = nwb2bids.RunConfig(bids_directory=bids_directory, use_session_labels=True) converter = nwb2bids.convert_nwb_dataset( nwb_paths=nwb_paths, run_config=run_config, ) .. invisible-code-block: python for variant in ["cli", "py"]: bids_dir = tutorial_base / f"ecephys_tutorial_dataset/bids_dataset_{variant}_3" expected_structure = { bids_dir: { "directories": {"sub-001", "sub-002"}, "files": {"dataset_description.json", "participants.json", "participants.tsv"}, }, bids_dir / "sub-001": { "directories": {"ses-A", "ses-B"}, "files": {"sub-001_sessions.json", "sub-001_sessions.tsv"}, }, bids_dir / "sub-001" / "ses-A": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-A" / "ecephys": { "directories": set(), "files": { "sub-001_ses-A_ecephys.nwb", "sub-001_ses-A_ecephys.json", "sub-001_ses-A_channels.tsv", "sub-001_ses-A_channels.json", "sub-001_ses-A_electrodes.tsv", "sub-001_ses-A_electrodes.json", "sub-001_ses-A_probes.tsv", "sub-001_ses-A_probes.json", }, }, bids_dir / "sub-001" / "ses-B": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-B" / "ecephys": { "directories": set(), "files": { "sub-001_ses-B_ecephys.nwb", "sub-001_ses-B_ecephys.json", "sub-001_ses-B_channels.tsv", "sub-001_ses-B_channels.json", "sub-001_ses-B_electrodes.tsv", "sub-001_ses-B_electrodes.json", "sub-001_ses-B_probes.tsv", "sub-001_ses-B_probes.json", }, }, bids_dir / "sub-002": { "directories": {"ses-C"}, "files": {"sub-002_sessions.json", "sub-002_sessions.tsv"}, }, bids_dir / "sub-002" / "ses-C": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-002" / "ses-C" / "ecephys": { "directories": set(), "files": { "sub-002_ses-C_ecephys.nwb", "sub-002_ses-C_ecephys.json", "sub-002_ses-C_channels.tsv", "sub-002_ses-C_channels.json", "sub-002_ses-C_electrodes.tsv", "sub-002_ses-C_electrodes.json", "sub-002_ses-C_probes.tsv", "sub-002_ses-C_probes.json", }, }, } nwb2bids.testing.assert_subdirectory_structure( directory=bids_dir, expected_structure=expected_structure ) Our resulting BIDS dataset should now contain all three NWB files converted to BIDS: .. code-block:: text ecephys_tutorial_dataset/bids_dataset_[cli|py]_3/ ├── dataset_description.json ├── participants.tsv ├── participants.json ├── sub-001/ │ ├── sub-001_sessions.tsv │ ├── sub-001_sessions.json │ ├── ses-A/ │ │ └── ecephys/ │ │ ├── sub-001_ses-A_ecephys.nwb │ │ ├── sub-001_ses-A_ecephys.json │ │ ├── sub-001_ses-A_channels.tsv │ │ ├── sub-001_ses-A_channels.json │ │ ├── sub-001_ses-A_electrodes.tsv │ │ ├── sub-001_ses-A_electrodes.json │ │ ├── sub-001_ses-A_probes.tsv │ │ └── sub-001_ses-A_probes.json │ └── ses-B/ │ └── ecephys/ │ ├── sub-001_ses-B_ecephys.nwb │ ├── sub-001_ses-B_ecephys.json │ ├── sub-001_ses-B_channels.tsv │ ├── sub-001_ses-B_channels.json │ ├── sub-001_ses-B_electrodes.tsv │ ├── sub-001_ses-B_electrodes.json │ ├── sub-001_ses-B_probes.tsv │ └── sub-001_ses-B_probes.json └── sub-002/ ├── sub-002_sessions.tsv ├── sub-002_sessions.json └── ses-C/ └── ecephys/ ├── sub-002_ses-C_ecephys.nwb ├── sub-002_ses-C_ecephys.json ├── sub-002_ses-C_channels.tsv ├── sub-002_ses-C_channels.json ├── sub-002_ses-C_electrodes.tsv ├── sub-002_ses-C_electrodes.json ├── sub-002_ses-C_probes.tsv └── sub-002_ses-C_probes.json .. _tutorial-implicit-bids-directory: Tutorial 4 - Implicit BIDS directory ------------------------------------ In the previous tutorials we have always specified the target BIDS directory as an explicit path. It is also possible to use the current working directory as the BIDS directory, provided that it is either empty or already a partial BIDS dataset as defined by a ``dataset_description.json`` file with a ``BIDSVersion`` value specified within. To test this out, we can create a new empty directory and navigate into it before converting: .. tabs:: .. tab:: CLI .. code-block:: bash cd ~/nwb2bids_tutorials/ecephys_tutorial_dataset/ mkdir bids_dataset_cli_4 cd bids_dataset_cli_4 nwb2bids convert ../ecephys_session_3.nwb ../some_sessions/*.nwb --use-session-labels The command line can take any number of inputs (separated by spaces) prior to other flags such as ``--bids-directory``. These inputs can be any mix of files or directories. .. tab:: Python Library .. code-block:: python import os import pathlib import nwb2bids tutorial_directory = pathlib.Path.home() / "nwb2bids_tutorials/ecephys_tutorial_dataset" nwb_paths = [ tutorial_directory / "ecephys_session_3.nwb", tutorial_directory / "some_sessions", ] bids_directory = tutorial_directory / "bids_dataset_py_4" bids_directory.mkdir(exist_ok=True) os.chdir(path=bids_directory) run_config = nwb2bids.RunConfig(use_session_labels=True) converter = nwb2bids.convert_nwb_dataset(nwb_paths=nwb_paths, run_config=run_config) .. invisible-code-block: python for variant in ["cli", "py"]: bids_dir = tutorial_base / f"ecephys_tutorial_dataset/bids_dataset_{variant}_4" expected_structure = { bids_dir: { "directories": {"sub-001", "sub-002"}, "files": {"dataset_description.json", "participants.json", "participants.tsv"}, }, bids_dir / "sub-001": { "directories": {"ses-A", "ses-B"}, "files": {"sub-001_sessions.json", "sub-001_sessions.tsv"}, }, bids_dir / "sub-001" / "ses-A": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-A" / "ecephys": { "directories": set(), "files": { "sub-001_ses-A_ecephys.nwb", "sub-001_ses-A_ecephys.json", "sub-001_ses-A_channels.tsv", "sub-001_ses-A_channels.json", "sub-001_ses-A_electrodes.tsv", "sub-001_ses-A_electrodes.json", "sub-001_ses-A_probes.tsv", "sub-001_ses-A_probes.json", }, }, bids_dir / "sub-001" / "ses-B": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-B" / "ecephys": { "directories": set(), "files": { "sub-001_ses-B_ecephys.nwb", "sub-001_ses-B_ecephys.json", "sub-001_ses-B_channels.tsv", "sub-001_ses-B_channels.json", "sub-001_ses-B_electrodes.tsv", "sub-001_ses-B_electrodes.json", "sub-001_ses-B_probes.tsv", "sub-001_ses-B_probes.json", }, }, bids_dir / "sub-002": { "directories": {"ses-C"}, "files": {"sub-002_sessions.json", "sub-002_sessions.tsv"}, }, bids_dir / "sub-002" / "ses-C": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-002" / "ses-C" / "ecephys": { "directories": set(), "files": { "sub-002_ses-C_ecephys.nwb", "sub-002_ses-C_ecephys.json", "sub-002_ses-C_channels.tsv", "sub-002_ses-C_channels.json", "sub-002_ses-C_electrodes.tsv", "sub-002_ses-C_electrodes.json", "sub-002_ses-C_probes.tsv", "sub-002_ses-C_probes.json", }, }, } nwb2bids.testing.assert_subdirectory_structure( directory=bids_dir, expected_structure=expected_structure ) And the results should match what we saw at the end of :ref:`tutorial-multiple-inputs`, except that we operated entirely from within ``bids_dataset_[cli|py]_4``. .. _tutorial-additional-metadata: Tutorial 5 - Additional metadata --------------------------------- NWB files don't always include all the little metadata details you might want to include in BIDS. To include manual metadata that is absent in the source files, you can provide additional metadata through a simple JSON structure shown below. .. note:: Currently only ``dataset_description`` metadata is supported, but more detailed subject- and session-level metadata will be supported in the future. To show how such additional metadata can be included through **nwb2bids**, start by creating a file named ``metadata.json`` inside the ``ecephys_tutorial_dataset`` directory we used in :ref:`tutorial-multiple-files`. Then fill the contents of the file to match below: .. code-block:: json { "dataset_description": { "Name": "My Custom BIDS Dataset", "BIDSVersion": "1.8.0", "HEDVersion": "8.3.0", "Authors": ["Last, First"] } } To include this additional metadata during conversion, we can use the following commands: .. tabs:: .. tab:: CLI .. tabs:: .. tab:: Unix / macOS .. code-block:: bash cd ~/nwb2bids_tutorials/ecephys_tutorial_file nwb2bids convert ecephys.nwb \ --bids-directory bids_dataset_cli_5 \ --additional-metadata-file-path metadata.json \ --use-session-labels .. tab:: Windows .. skip: next .. code-block:: bash cd ~/nwb2bids_tutorials/ecephys_tutorial_file nwb2bids convert ecephys.nwb ^ --bids-directory bids_dataset_cli_5 ^ --additional-metadata-file-path metadata.json ^ --use-session-labels .. tab:: Python Library .. code-block:: python import pathlib import nwb2bids tutorial_directory = pathlib.Path.home() / "nwb2bids_tutorials/ecephys_tutorial_file" nwb_paths = [tutorial_directory / "ecephys.nwb"] bids_directory = tutorial_directory / "bids_dataset_py_5" bids_directory.mkdir(exist_ok=True) additional_metadata_file_path = tutorial_directory / "metadata.json" run_config = nwb2bids.RunConfig( bids_directory=bids_directory, additional_metadata_file_path=additional_metadata_file_path, use_session_labels=True, ) converter = nwb2bids.convert_nwb_dataset( nwb_paths=nwb_paths, run_config=run_config, ) .. invisible-code-block: python for variant in ["cli", "py"]: bids_dir = tutorial_base / f"ecephys_tutorial_file/bids_dataset_{variant}_5" expected_structure = { bids_dir: { "directories": {"sub-001"}, "files": {"dataset_description.json", "participants.json", "participants.tsv"}, }, bids_dir / "sub-001": { "directories": {"ses-A"}, "files": {"sub-001_sessions.json", "sub-001_sessions.tsv"}, }, bids_dir / "sub-001" / "ses-A": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-A" / "ecephys": { "directories": set(), "files": { "sub-001_ses-A_ecephys.nwb", "sub-001_ses-A_ecephys.json", "sub-001_ses-A_channels.tsv", "sub-001_ses-A_channels.json", "sub-001_ses-A_electrodes.tsv", "sub-001_ses-A_electrodes.json", "sub-001_ses-A_probes.tsv", "sub-001_ses-A_probes.json", }, }, } nwb2bids.testing.assert_subdirectory_structure( directory=bids_dir, expected_structure=expected_structure ) Our resulting bids `dataset_description.json` now includes our additional metadata! .. _tutorial-library-customization: Tutorial 6 - Library customization ---------------------------------- The **nwb2bids** Python library is much easier to customize and interact with than the CLI usage. The workflow that is automatically run by the helper function :func:`~nwb2bids.convert_nwb_dataset()` can be broken down into the following distinct steps: .. code-block:: python import pathlib import nwb2bids tutorial_directory = pathlib.Path.home() / "nwb2bids_tutorials/ecephys_tutorial_file" nwb_paths = [tutorial_directory / "ecephys.nwb"] bids_directory = tutorial_directory / "bids_dataset_py_6" bids_directory.mkdir(exist_ok=True) additional_metadata_file_path = tutorial_directory / "metadata.json" # Step 1: Initialize the DatasetConverter object run_config = nwb2bids.RunConfig( bids_directory=bids_directory, additional_metadata_file_path=additional_metadata_file_path, use_session_labels=True, ) converter = nwb2bids.DatasetConverter.from_nwb_paths( nwb_paths=nwb_paths, run_config=run_config, ) # Step 2: Extract metadata from NWB contents converter.extract_metadata() # Step 3: Convert NWB files to BIDS structure converter.convert_to_bids_dataset() .. invisible-code-block: python bids_dir = tutorial_base / "ecephys_tutorial_file/bids_dataset_py_6" expected_structure = { bids_dir: { "directories": {"sub-001"}, "files": {"dataset_description.json", "participants.json", "participants.tsv"}, }, bids_dir / "sub-001": { "directories": {"ses-A"}, "files": {"sub-001_sessions.json", "sub-001_sessions.tsv"}, }, bids_dir / "sub-001" / "ses-A": { "directories": {"ecephys"}, "files": set(), }, bids_dir / "sub-001" / "ses-A" / "ecephys": { "directories": set(), "files": { "sub-001_ses-A_ecephys.nwb", "sub-001_ses-A_ecephys.json", "sub-001_ses-A_channels.tsv", "sub-001_ses-A_channels.json", "sub-001_ses-A_electrodes.tsv", "sub-001_ses-A_electrodes.json", "sub-001_ses-A_probes.tsv", "sub-001_ses-A_probes.json", }, }, } nwb2bids.testing.assert_subdirectory_structure( directory=bids_dir, expected_structure=expected_structure ) The ``converter`` object (a type of :class:`~nwb2bids.DatasetConverter`) exposes many useful attributes and methods that may useful to explore. In particular, it contains all of the :class:`~nwb2bids.SessionConverter` objects that were assembled from the input NWB files. These in turn attach various metadata models, such as :class:`~nwb2bids.bids_models.BidsSessionMetadata`, from which all metadata attributes used during the BIDS conversion process may be inspected and modified prior to writing the resulting files. .. _tutorial-future: More tutorials coming soon! --------------------------- We are still compiling some detailed tutorials for more advanced use cases, such as sanitization, run configurations, non-ecephys data types, and more! Check back soon for updates.