ndx-extracellular-channels
is an extension of the NWB format to formally define information about neural probes as data types in NWB files. It comes with helper functions to easily construct ndx_extracellular_channels.Probe
from probeinterface.Probe
and vice versa.
It provides a new version of ElectricalSeries
called ExtracellularSeries
. Each ExtracellularSeries
is associated with its own ChannelsTable
that contains information about the channels and probe used to record the data. Each channel is mapped to contacts on the probe.
Use cases supported:
It encompasses SpikeInterface's ndx-probeinterface
extension and started originally as @D1o0g9s's ndx-probe-interface extension.
(TODO publish to PyPI)
pip install ndx_extracellular_channels
probeinterface.Probe
/ProbeGroup
object to a ndx_extracellular_channels.Probe
objectimport ndx_extracellular_channels
pi_probe = probeinterface.Probe(...)
pi_probegroup = probeinterface.ProbeGroup()
# from_probeinterface always returns a list of ndx_extracellular_channels.Probe devices
ndx_probes1 = ndx_extracellular_channels.from_probeinterface(pi_probe)
ndx_probes2 = ndx_extracellular_channels.from_probeinterface(pi_probegroup)
ndx_probes = ndx_probes1.extend(ndx_probes2)
nwbfile = pynwb.NWBFile(...)
# add Probe as NWB Devices
for ndx_probe in ndx_probes:
nwbfile.add_device(ndx_probe)
ndx_extracellular_channels.Probe
object to a probeinterface.Probe
objectimport ndx_extracellular_channels
# load ndx_extracellular_channels.Probe objects from NWB file
io = pynwb.NWBH5IO(file_path, "r")
nwbfile = io.read()
ndx_probes = []
for device in nwbfile:
if isinstance(device, ndx_extracellular_channels.Probe):
ndx_probes.append(device)
# convert to probeinterface.Probe objects
pi_probes = []
for ndx_probe in ndx_probes:
pi_probe = ndx_extracellular_channels.to_probeinterface(ndx_probe)
pi_probes.append(pi_probe)
See src/pynwb/tests/test_example_usage_probeinterface.py
for a full example.
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#ffffff', "primaryBorderColor': '#144E73', 'lineColor': '#D96F32'}}}%%
classDiagram
direction LR
class ExtracellularSeries {
<<TimeSeries>>
data : numeric
--> unit : str = "microvolts"
channels : DynamicTableRegion
--> target : ChannelsTable
channel_conversion : List[float], optional
--> axis : int = 1
}
class ChannelsTable {
<<DynamicTable>>
--------------------------------------
attributes
--------------------------------------
name : str
description : str
probe : Probe
position_reference : str, optional
electrical_reference_description : str, optional
ground : str, optional
position_confirmation_method : str, optional
--------------------------------------
columns
--------------------------------------
id : VectorData[int]
contact : DynamicTableRegion
--> target : ContactsTable
reference_contact : DynamicTableRegion, optional
--> target : ContactsTable
filter : VectorData[str], optional
---> Strings such as "Bandpass 0-300 Hz".
estimated_position_ap_in_mm : VectorData[float], optional
estimated_position_ml_in_mm : VectorData[float], optional
estimated_position_dv_in_mm : VectorData[float], optional
estimated_brain_area : VectorData[str], optional
confirmed_position_ap_in_mm : VectorData[float], optional
confirmed_position_ml_in_mm : VectorData[float], optional
confirmed_position_dv_in_mm : VectorData[float], optional
confirmed_brain_area : VectorData[str], optional
... Any other custom columns, e.g., ADC information
}
class ProbeInsertion {
<<Container>>
insertion_position_ap_in_mm : float, optional
insertion_position_ml_in_mm : float, optional
insertion_position_dv_in_mm : float, optional
position_reference : str, optional
hemisphere : Literal["left", "right"], optional
insertion_angle_pitch_in_deg : float, optional
insertion_angle_roll_in_deg : float, optional
insertion_angle_yaw_in_deg : float, optional
depth_in_um : float, optional
}
namespace ProbeInterface {
class Probe {
<<Device>>
identifier : str
--> Usually the serial number
probe_model : ProbeModel
probe_insertion : ProbeInsertion, optional
}
class ProbeModel {
<<Device>>
name : str
manufacturer : str
model : str
ndim : int, optional
planar_contour_in_um : List[Tuple[float, float], Tuple[float, float, float]], optional
contacts_table : ContactsTable
}
class ContactsTable {
<<DynamicTable>>
--------------------------------------
attributes
--------------------------------------
name : str
description : str
--------------------------------------
columns
--------------------------------------
id : VectorData[int]
relative_position_in_um : List[Tuple[float, float], Tuple[float, float, float]]
contact_id : VectorData[str], optional
shank_id : VectorData[str], optional
plane_axes : List[Tuple[int, int], Tuple[int, int, int]], optional
shape : VectorData[str], optional
radius_in_um : VectorData[float], optional
width_in_um : VectorData[float], optional
height_in_um : VectorData[float], optional
}
}
Probe *..> ProbeModel : links to probe_model
Probe *--> ProbeInsertion: might contain ProbeInsertion
ProbeModel *--> ContactsTable : contains
ExtracellularSeries ..> ChannelsTable : links to channels
ChannelsTable *..> Probe : links to probe
ChannelsTable ..> ContactsTable : row reference to contact
note for ChannelsTable "ChannelsTable is no longer global"
This extension was created using ndx-template.