Skip to content

Commit

Permalink
ElectrodesTable
Browse files Browse the repository at this point in the history
  • Loading branch information
mavaylon1 committed Apr 18, 2024
1 parent 2259bed commit 2be97c9
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 20 deletions.
45 changes: 43 additions & 2 deletions src/pynwb/ecephys.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import warnings
from collections.abc import Iterable

from hdmf.common import DynamicTableRegion
from hdmf.common import DynamicTableRegion, DynamicTable, VectorData
from hdmf.data_utils import DataChunkIterator, assertEqualShape
from hdmf.utils import docval, popargs, get_docval, popargs_to_dict, get_data_shape
from hdmf.utils import docval, popargs, getargs, get_docval, popargs_to_dict, get_data_shape

from . import register_class, CORE_NAMESPACE
from .base import TimeSeries
Expand Down Expand Up @@ -37,6 +37,47 @@ def __init__(self, **kwargs):
setattr(self, key, val)


@register_class('ElectrodesTable', CORE_NAMESPACE)
class ElectrodesTable(DynamicTable):
"""TODO"""

__columns__ = (
{'name': 'location', 'description': 'TODO', 'required': True},
{'name': 'group', 'description': 'TODO', 'required': True})

@docval({'name': 'group_name', 'type': VectorData, 'doc':'TODO', 'default': None},
{'name': 'x', 'type': float, 'doc':'TODO', 'default': None},
{'name': 'y', 'type': float, 'doc':'TODO', 'default': None},
{'name': 'z', 'type': float, 'doc':'TODO', 'default': None},
{'name': 'imp', 'type': float, 'doc':'TODO', 'default': None},
{'name': 'filtering', 'type': str, 'doc':'TODO', 'default': None},
{'name': 'rel_x', 'type': float, 'doc':'TODO', 'default': None},
{'name': 'rel_y', 'type': float, 'doc':'TODO', 'default': None},
{'name': 'rel_z', 'type': float, 'doc':'TODO', 'default': None},
{'name': 'reference', 'type': VectorData, 'doc':'TODO', 'default': None},)
def __init__(self, **kwargs):
kwargs['name'] = 'electrodes'
kwargs['description'] = 'metadata about extracellular electrodes'

# optional fields
keys_to_set = (
'group_name',
'x',
'y',
'z',
'imp',
'filtering',
'rel_x',
'rel_y',
'rel_z',
'reference')
args_to_set = popargs_to_dict(keys_to_set, kwargs)
for key, val in args_to_set.items():
setattr(self, key, val)

super().__init__(**kwargs)


@register_class('ElectricalSeries', CORE_NAMESPACE)
class ElectricalSeries(TimeSeries):
"""
Expand Down
34 changes: 17 additions & 17 deletions src/pynwb/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from .base import TimeSeries, ProcessingModule
from .device import Device
from .epoch import TimeIntervals
from .ecephys import ElectrodeGroup
from .ecephys import ElectrodeGroup, ElectrodesTable
from .icephys import (IntracellularElectrode, SweepTable, PatchClampSeries, IntracellularRecordingsTable,
SimultaneousRecordingsTable, SequentialRecordingsTable, RepetitionsTable,
ExperimentalConditionsTable)
Expand Down Expand Up @@ -377,7 +377,7 @@ class NWBFile(MultiContainerInterface, HERDManager):
{'name': 'lab_meta_data', 'type': (list, tuple), 'default': None,
'doc': 'an extension that contains lab-specific meta-data'},
{'name': 'electrodes', 'type': DynamicTable,
'doc': 'the ElectrodeTable that belongs to this NWBFile', 'default': None},
'doc': 'the ElectrodesTable that belongs to this NWBFile', 'default': None},
{'name': 'electrode_groups', 'type': Iterable,
'doc': 'the ElectrodeGroups that belong to this NWBFile', 'default': None},
{'name': 'ic_electrodes', 'type': (list, tuple),
Expand Down Expand Up @@ -644,7 +644,7 @@ def add_epoch(self, **kwargs):

def __check_electrodes(self):
if self.electrodes is None:
self.electrodes = ElectrodeTable()
self.electrodes = ElectrodesTable()

@docval(*get_docval(DynamicTable.add_column), allow_extra=True)
def add_electrode_column(self, **kwargs):
Expand Down Expand Up @@ -701,8 +701,8 @@ def add_electrode(self, **kwargs):
raise ValueError("The 'location' argument is required when creating an electrode.")
if not kwargs['group']:
raise ValueError("The 'group' argument is required when creating an electrode.")
if d.get('group_name', None) is None:
d['group_name'] = d['group'].name
# if d.get('group_name', None) is None:
# d['group_name'] = d['group'].name

new_cols = [('x', 'the x coordinate of the position (+x is posterior)'),
('y', 'the y coordinate of the position (+y is inferior)'),
Expand Down Expand Up @@ -738,7 +738,7 @@ def create_electrode_table_region(self, **kwargs):
for idx in region:
if idx < 0 or idx >= len(self.electrodes):
raise IndexError('The index ' + str(idx) +
' is out of range for the ElectrodeTable of length '
' is out of range for the ElectrodesTable of length '
+ str(len(self.electrodes)))
desc = getargs('description', kwargs)
name = getargs('name', kwargs)
Expand Down Expand Up @@ -820,13 +820,13 @@ def add_invalid_time_interval(self, **kwargs):
self.__check_invalid_times()
self.invalid_times.add_interval(**kwargs)

@docval({'name': 'electrode_table', 'type': DynamicTable, 'doc': 'the ElectrodeTable for this file'})
@docval({'name': 'electrode_table', 'type': DynamicTable, 'doc': 'the ElectrodesTable for this file'})
def set_electrode_table(self, **kwargs):
"""
Set the electrode table of this NWBFile to an existing ElectrodeTable
Set the electrode table of this NWBFile to an existing ElectrodesTable
"""
if self.electrodes is not None:
msg = 'ElectrodeTable already exists, cannot overwrite'
msg = 'ElectrodesTable already exists, cannot overwrite'
raise ValueError(msg)
electrode_table = getargs('electrode_table', kwargs)
self.electrodes = electrode_table
Expand Down Expand Up @@ -1179,14 +1179,14 @@ def _tablefunc(table_name, description, columns):
return t


def ElectrodeTable(name='electrodes',
description='metadata about extracellular electrodes'):
return _tablefunc(name, description,
[('location', 'the location of channel within the subject e.g. brain region'),
('group', 'a reference to the ElectrodeGroup this electrode is a part of'),
('group_name', 'the name of the ElectrodeGroup this electrode is a part of')
]
)
# def ElectrodesTable(name='electrodes',
# description='metadata about extracellular electrodes'):
# return _tablefunc(name, description,
# [('location', 'the location of channel within the subject e.g. brain region'),
# ('group', 'a reference to the ElectrodeGroup this electrode is a part of'),
# ('group_name', 'the name of the ElectrodeGroup this electrode is a part of')
# ]
# )


def TrialTable(name='trials', description='metadata about experimental trials'):
Expand Down
2 changes: 2 additions & 0 deletions src/pynwb/io/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def __init__(self, spec):
# map "stimulus" to NWBDataInterface and DynamicTable and unmap the spec for TimeSeries because it is
# included in the mapping to NWBDataInterface
self.unmap(stimulus_spec.get_group('presentation').get_neurodata_type('TimeSeries'))
# breakpoint()
self.map_spec('stimulus', stimulus_spec.get_group('presentation').get_neurodata_type('NWBDataInterface'))
self.map_spec('stimulus', stimulus_spec.get_group('presentation').get_neurodata_type('DynamicTable'))
self.map_spec('stimulus_template', stimulus_spec.get_group('templates').get_neurodata_type('TimeSeries'))
Expand Down Expand Up @@ -73,6 +74,7 @@ def __init__(self, spec):

ecephys_spec = general_spec.get_group('extracellular_ephys')
self.unmap(ecephys_spec)
# breakpoint()
self.map_spec('electrodes', ecephys_spec.get_group('electrodes'))
self.map_spec('electrode_groups', ecephys_spec.get_neurodata_type('ElectrodeGroup'))

Expand Down
64 changes: 64 additions & 0 deletions tests/unit/foo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from datetime import datetime
from uuid import uuid4

import numpy as np
from dateutil.tz import tzlocal
from hdmf.common import VectorData

from pynwb import NWBHDF5IO, NWBFile
from pynwb.ecephys import LFP, ElectricalSeries, ElectrodeGroup, ElectrodesTable

nwbfile = NWBFile(
session_description="my first synthetic recording",
identifier=str(uuid4()),
session_start_time=datetime.now(tzlocal()),
experimenter=[
"Baggins, Bilbo",
],
lab="Bag End Laboratory",
institution="University of Middle Earth at the Shire",
experiment_description="I went on an adventure to reclaim vast treasures.",
session_id="LONELYMTN001",
)

device = nwbfile.create_device(
name="array", description="the best array", manufacturer="Probe Company 9000"
)

group = ElectrodeGroup( name='foo',
description="electrode group",
device=device,
location="brain area",)
# location_col = VectorData(name='location', description='foo', data=['brain area'])
# group_col = VectorData(name='groups', description='foo', data=[group])

# table = ElectrodesTable()
# nwbfile.electrodes = table
# nwbfile.add_electrode(group=group, location='brain')
# breakpoint()
# nwbfile.add_electrode_column(name="label", description="label of electrode")

nshanks = 4
nchannels_per_shank = 3
electrode_counter = 0
#
for ishank in range(nshanks):
# create an electrode group for this shank
electrode_group = nwbfile.create_electrode_group(
name="shank{}".format(ishank),
description="electrode group for shank {}".format(ishank),
device=device,
location="brain area",
)
# add electrodes to the electrode table
for ielec in range(nchannels_per_shank):
nwbfile.add_electrode(
group=electrode_group,
label="shank{}elec{}".format(ishank, ielec),
location="brain area",
)
electrode_counter += 1
# breakpoint()
with NWBHDF5IO("ecephys_tutorial.nwb", "w") as io:
io.write(nwbfile)
breakpoint()

0 comments on commit 2be97c9

Please sign in to comment.