Skip to content

Commit

Permalink
Improve tutorial and set the shape of dynamic region to null (#13)
Browse files Browse the repository at this point in the history
* improve tutorial

* change the dim and shape of the units table region
  • Loading branch information
h-mayorquin authored Jul 13, 2024
1 parent 55c4b28 commit f032b96
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 33 deletions.
59 changes: 33 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pip install -U git+https://github.com/catalystneuro/ndx-binned-spikes.git
The `BinnedAlignedSpikes` object is designed to store counts of spikes around a set of events (e.g., stimuli or behavioral events such as licks). The events are characterized by their timestamps and a bin data structure is used to store the spike counts around each of the event timestamps. The `BinnedAlignedSpikes` object keeps a separate count for each of the units (e.g., neurons), in other words, the spikes of the units are counted separately but aligned to the same set of events.

### Simple example
The following code illustrates the use of this extension:
The following code illustrates a minimal use of this extension:

```python
import numpy as np
Expand Down Expand Up @@ -48,19 +48,29 @@ binned_aligned_spikes = BinnedAlignedSpikes(

```

The resulting object is usually added to a processing module:
The resulting object is usually added to a processing module in an NWB file. The following code illustrates how to add the `BinnedAlignedSpikes` object to an NWB file. We fist create a nwbfile, then add the `BinnedAlignedSpikes` object to a processing module and finally write the nwbfile to disk:

```python
from pynwb.testing.mock.file import mock_NWBFile
from pynwb import NWBHDF5IO


nwbfile = mock_NWBFile()
from datetime import datetime
from zoneinfo import ZoneInfo
from pynwb import NWBHDF5IO, NWBFile

session_description = "A session of data where PSTH was produced"
session_start_time = datetime.now(ZoneInfo("Asia/Ulaanbaatar"))
identifier = "a_session_identifier"
nwbfile = NWBFile(
session_description=session_description,
session_start_time=session_start_time,
identifier=identifier,
)

ecephys_processing_module = nwbfile.create_processing_module(
name="ecephys", description="Intermediate data from extracellular electrophysiology recordings, e.g., LFP."
name="ecephys", description="Intermediate data derived from extracellular electrophysiology recordings."
)
ecephys_processing_module.add(binned_aligned_spikes)

with NWBHDF5IO("binned_aligned_spikes.nwb", "w") as io:
io.write(nwbfile)
```

### Parameters and data structure
Expand All @@ -77,7 +87,6 @@ The structure of the bins are characterized with the following parameters:
Note that in the diagram above, the `milliseconds_from_event_to_first_bin` is negative.



The `data` argument passed to the `BinnedAlignedSpikes` stores counts across all the event timestamps for each of the units. The data is a 3D array where the first dimension indexes the units, the second dimension indexes the event timestamps, and the third dimension indexes the bins where the counts are stored. The shape of the data is `(number_of_units`, `number_of_events`, `number_of_bins`).


Expand All @@ -92,18 +101,17 @@ The following diagram illustrates the structure of the data for a concrete examp


### Linking to units table
One way to make the information stored in the `BinnedAlignedSpikes` object more useful is to indicate exactly which units or neurons the first dimension of the `data` attribute corresponds to. This is **optional but recommended** as it makes the data more interpretable and useful for future users. In NWB the units are usually stored in a `Units` [table](https://pynwb.readthedocs.io/en/stable/pynwb.misc.html#pynwb.misc.Units). To illustrate how to to create this link let's ffirst create a toy `Units` table:
One way to make the information stored in the `BinnedAlignedSpikes` object more useful is to indicate exactly which units or neurons the first dimension of the `data` attribute corresponds to. This is **optional but recommended** as it makes the data more interpretable and useful for future users. In NWB the units are usually stored in a `Units` [table](https://pynwb.readthedocs.io/en/stable/pynwb.misc.html#pynwb.misc.Units). To illustrate how to to create this link let's first create a toy `Units` table:

```python
import numpy as np
from pynwb.misc import Units
from hdmf.common import DynamicTableRegion
from pynwb.testing.mock.file import mock_NWBFile

num_units = 5
max_spikes_per_unit = 10

units_table = Units(name="units")
units_table.add_column(name="unit_name", description="name of the unit")

rng = np.random.default_rng(seed=0)

Expand All @@ -115,39 +123,38 @@ for unit_index in range(num_units):

# Not all units have the same number of spikes
spike_times = times[unit_index, : spikes_per_unit[unit_index]]
unit_name = f"unit_{unit_index}"
units_table.add_unit(spike_times=spike_times, unit_name=unit_name)


# We then create a mock NWB file and add the units table
nwbfile = mock_NWBFile()
nwbfile.units = units_table
```

This will create a `Units` table with 5 units. We can then link the `BinnedAlignedSpikes` object to this table by creating a `DynamicTableRegion` object. This allows to be very specific about which units the data in the `BinnedAlignedSpikes` object corresponds to. The following code illustrates how to create the `DynamicTableRegion` object and link it to the `BinnedAlignedSpikes` object:
```python
This will create a `Units` table with 5 units. We can then link the `BinnedAlignedSpikes` object to this table by creating a `DynamicTableRegion` object. This allows to be very specific about which units the data in the `BinnedAlignedSpikes` object corresponds to. In the following code, the units described on the `BinnedAlignedSpikes` object correspond to the unit with indices 1 and 3 on the `Units` table. The rest of the procedure is the same as before:

region_indices = [0, 1, 2]
units_region = DynamicTableRegion(
data=region_indices, table=units_table, description="region of units table", name="units_region"
)
```python
from ndx_binned_spikes import BinnedAlignedSpikes
from hdmf.common import DynamicTableRegion


# Now we create the BinnedAlignedSpikes object and link it to the units table
data = np.array(
[
[ # Data of the first unit
[ # Data of the unit 1
[5, 1, 3, 2], # First timestamp bins
[6, 3, 4, 3], # Second timestamp bins
[4, 2, 1, 4], # Third timestamp bins
],
[ # Data of the second unit
[ # Data of the unit 3
[8, 4, 0, 2], # First timestamp bins
[3, 3, 4, 2], # Second timestamp bins
[2, 7, 4, 1], # Third timestamp bins
],
],
)

region_indices = [1, 3]
units_region = DynamicTableRegion(
data=region_indices, table=units_table, description="region of units table", name="units_region"
)

event_timestamps = np.array([0.25, 5.0, 12.25])
milliseconds_from_event_to_first_bin = -50.0 # The first bin is 50 ms before the event
bin_width_in_milliseconds = 100.0
Expand All @@ -163,9 +170,9 @@ binned_aligned_spikes = BinnedAlignedSpikes(
units_region=units_region,
)


```

As with the previous example this can be then added to a processing module in an NWB file and written to disk using exactly the same code as before.

---
This extension was created using [ndx-template](https://github.com/nwb-extensions/ndx-template).
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ license = {text = "MIT"}
classifiers = [
# TODO: add classifiers before release
# "Programming Language :: Python",
# "Programming Language :: Python :: 3.8",
# "Programming Language :: Python :: 3.9",
# "Programming Language :: Python :: 3.10",
# "Programming Language :: Python :: 3.11",
Expand All @@ -35,6 +34,7 @@ keywords = [
dependencies = [
"pynwb>=2.5.0",
"hdmf>=3.10.0",
"numpy<2.0"
]

# TODO: add URLs before release
Expand Down
4 changes: 0 additions & 4 deletions spec/ndx-binned-spikes.extensions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,5 @@ groups:
doc: The timestamps at which the events occurred.
- name: units_region
neurodata_type_inc: DynamicTableRegion
dims:
- number_of_units
shape:
- null
doc: A reference to the Units table region that contains the units of the data.
quantity: '?'
Empty file removed src/pynwb/README.md
Empty file.
2 changes: 0 additions & 2 deletions src/spec/create_extension_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ def main():
name="units_region",
neurodata_type_inc="DynamicTableRegion",
doc="A reference to the Units table region that contains the units of the data.",
shape=[None],
dims=["number_of_units"],
quantity="?",

)
Expand Down

0 comments on commit f032b96

Please sign in to comment.