Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unique electrode id check #1344

Merged

Conversation

CodyCBakerPhD
Copy link
Collaborator

fix #1342

Motivation

While trying to solve this issue on spikeextractors, I ran across this problem as a contributing factor. The docstring and overall schema treatment of electrode table IDs mandates they be unique, but we can set them arbitrarily through pynwb.

The new changes use the add_row functionality in DynamicTables enforce unique electrode ids as per @oruebel's suggestion. To avoid decreases to computational performance/speed, this only triggers if the user manually specified the ids (those set automatically by default do not need to be checked).

How to test the behavior?

import numpy as np
import uuid
from datetime import datetime

import spikeextractors as se
from pynwb import NWBFile

nwbfile_kwargs = dict(
    session_description="Auto-generated by NwbRecordingExtractor without description.",
    identifier=str(uuid.uuid4()),
    session_start_time=datetime(1970, 1, 1)
)
nwbfile = NWBFile(**nwbfile_kwargs)

re = se.example_datasets.toy_example()[0]

se.NwbRecordingExtractor.add_devices(recording=re, nwbfile=nwbfile)
se.NwbRecordingExtractor.add_electrode_groups(recording=re, nwbfile=nwbfile)

defaults = dict(
    x=np.nan,
    y=np.nan,
    z=np.nan,
    # There doesn't seem to be a canonical default for impedence, if missing.
    # The NwbRecordingExtractor follows the -1.0 convention, other scripts sometimes use np.nan
    imp=-1.0,
    location="unknown",
    filtering="none",
    group_name="Electrode Group"
)
nwbfile.add_electrode(**dict(defaults, id=0, group=nwbfile.electrode_groups[str(0)]))
# nwbfile.add_electrode(**dict(defaults, id=0, group=nwbfile.electrode_groups[str(0)]))
nwbfile.add_electrode(**dict(defaults, group=nwbfile.electrode_groups[str(0)]))

Uncommenting the second to last line will trigger the new uniqueness error. There is also a test in test_io.py that checks this behavior.

Checklist

  • Have you checked our Contributing document?
  • Have you ensured the PR description clearly describes problem and the solution?
  • Is your contribution compliant with our coding style? This can be checked running flake8 from the source directory.
  • Have you checked to ensure that there aren't other open Pull Requests for the same change?
  • Have you included the relevant issue number using #XXX notation where XXX is the issue number?

src/pynwb/file.py Outdated Show resolved Hide resolved
@rly
Copy link
Contributor

rly commented Mar 11, 2021

I think checking the electrode ID is unique should be the default, regardless of whether the user is manually or automatically setting it. Imagine if the user adds an electrode with manual ID = 1. Then adds an electrode with automatic ID. DynamicTable.add_row would set that ID to be len(self.id) which is 1 and creates a duplicate.

Instead of only triggering if the user manually specified the ids, what do you think about adding a flag enforce_unique_ids to add_electrode(...), just like in DynamicTable.add_row(...), but instead of default=False, the default value is True? This gives the user more control over when the check is made.

i.e.

@docval(..., 
	    {'name': 'enforce_unique_id', 'type': bool, 'doc': 'enforce that the id in the table must be unique',
	    'default': True})
def add_electrode(self, **kwargs):

There would be an added computational cost since this would by default run every time an electrode is added. Currently the uniqueness check is not handled efficiently in NWB/HDMF. One way to do this efficiently is implementing a btree/hash-based index in DynamicTable for lookup by ID, similar to how some databases maintain uniqueness.

@CodyCBakerPhD
Copy link
Collaborator Author

Good point on the default case, I didn't think to check how the auto-population was encoded.

I think that is a great idea to just add it to the docval - I've added that now.

The computational cost isn't truly a concern at the moment (it's hardly the most expensive thing to convert to NWB) but was just an attempt at optimizing the code as much as possible. I agree, though, that more sophisticated methods of uniqueness checking could make it a complete non-issue.

@CodyCBakerPhD CodyCBakerPhD requested a review from rly June 12, 2021 21:57
@CodyCBakerPhD
Copy link
Collaborator Author

@rly Any other changes you would like me to make here?

Copy link
Contributor

@oruebel oruebel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a not to the changelog.md, otherwise this looks good to me. This PR changes default API behavior, but since the next release is going to a major version bump I don't see any issues.

@CodyCBakerPhD
Copy link
Collaborator Author

@oruebel @rly Changelog updated

oruebel
oruebel previously approved these changes Jun 16, 2021
@rly
Copy link
Contributor

rly commented Jun 16, 2021

Closing and reopening to try to trigger CI.

@rly rly closed this Jun 16, 2021
@rly rly reopened this Jun 16, 2021
@rly rly merged commit a12da31 into NeurodataWithoutBorders:dev Jun 25, 2021
@rly rly deleted the add_unique_electrode_id_check branch June 25, 2021 00:57
@rly
Copy link
Contributor

rly commented Jun 25, 2021

Thanks @CodyCBakerPhD !

rly pushed a commit that referenced this pull request Aug 10, 2021
Declare SweepTable as deprecated on init

Add TimeSeriesReferenceVectorData Container class

Add simple test for TimeSeriesReferenceVectorData

Update nwb-schema branch

Add icephys metadata container classes

Update NWBFile to support icephys metadata tables

Add unit tests for icephys metadata tables

Update imports and fix SweepTable integration tests

Fix test available namespaces testcase

Update SweepTable tutorial and declare as deprecated

Add new icephys tutorial using the new hierarchical metadata tables

Add IntracellularRecordingsTable illustration

Fix flake8 issues

Remove use of HierarchiclaDynamicTableMixin use for now

Add source PowerPoint file for icephys docs

add doc for TimeIntervals (#1352)

* add doc for TimeIntervals

* add allensdk back

Fix make clean command for docs to also clean sphinx-gallery files

Updated CHANGELOG to describe changes from this PR

Add thumbnails for domain-specfic tutorials

Add thumbnaisl for general tutorials

Update changelog

Remove ic_filtering/icephys_filtering duplicate and fix test

Fix description of ExperimentalConditionsTable

Use HDMF 2.5.0 (#1354)

DOC: make template less puzzling in how to fill out ;-) (#1336)

Co-authored-by: Ryan Ly <[email protected]>

Add ReadTheDocs badge to README (#1311)

Co-authored-by: Ryan Ly <[email protected]>

Update to use upcoming HDMF 2.5.1 (#1355)

Revert breaking changes to scratch API (#1356)

Use HDMF 2.5.2 (#1360)

Add `DfOverF` to docs (#1351)

Co-authored-by: Ryan Ly <[email protected]>

docs/gallery/general/file.py: Fix argument count in documentation

add strain optional arg to Subject (#1241)

* remove validate_core_schema.

You can check the core schema with e.g.

* Update Subject unit test

* Fix Subject unit test, add check for date_of_birth

Co-authored-by: Ryan Ly <[email protected]>

add source channels to DecompSeries

add option to include source_channels for DecompositionSeries (#1258)

* add option to include source_channels for DecompositionSeries and include round trip test
* Use latest schema
* Add unit test

Add optional link to Device in ImageSeries (#1265)

* Add optional link to Device in ImageSeries

* Add support for device in ImageSeries subclasses

* Update ImageMaskSeries docstring

* Update schema

add continuity optional arg to TimeSeries.__init__ (#1226)

* add continuity optional arg to TimeSeries.__init__

* use new enum feature of hdmf to check against controlled vocabulary

* Fix flake8

* Add integration test

* Fix flake8

* Add correct mapping for data.continuity

Co-authored-by: Ryan Ly <[email protected]>

Add support for ElectricalSeries.filtering dataset (#1270)

Set fixed value for IZeroClampSeries stimulus description

Fix test

Update changelog

Add note about allensdk py3.8 incompatibility

Add workaround for validation against cached hdmf-experimental

Update CI to avoid click8 incompatibility

Use HDMF 2.5.5

Address comments, improve doc

Fix docs, add citation info, update repo infra (#1362)

Use latest HDMF, raise min pandas, dissociate req-min and setup reqs (#1363)

Use nwb schema branch for icephys meta

Ensure start_index and count are set to -1 if TimeSeries is None

Add test for conversion of start_index and index_count vals

Fix flake8 in __compute_index docstring

Fix icephys metadata roundtrip NWBFile test base on update DynamicTableRegion behavior

Update schema (updated version to 1.4.0-alpha)

improve extensions documentation (#1350)

* improve extensions documentation

* adjustments to extensions.rst

* add tips to extension docs

* create multi-part extensions tutorial

* move API building docs to chapter 5 of extensions guide

* restructure and fix up some spots

* Update docs/source/extensions_tutorial/2_create_extension_spec_walkthrough.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/2_create_extension_spec_walkthrough.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/2_create_extension_spec_walkthrough.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/6_documenting_extension.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/4_auto_api.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Oliver Ruebel <[email protected]>

* remove detailed GroupSpec API instructions from tutorial

* remove instructions for generated the directory structure, and instead focus on generating the HTML and filling in the missing pieces.

* add link to ndx catalog

* Update docs/source/extensions_tutorial/2_create_extension_spec_walkthrough.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/2_create_extension_spec_walkthrough.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/2_create_extension_spec_walkthrough.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/4_auto_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/5_custom_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/4_auto_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/4_auto_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/4_auto_api.rst

* Update docs/source/extensions_tutorial/3_spec_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/5_custom_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update docs/source/extensions_tutorial/5_custom_api.rst

Co-authored-by: Ryan Ly <[email protected]>

* Update extensions_tutorial_home.rst

* Minor text fixes

* Minor text fixes

* Minor text fixes

Co-authored-by: Oliver Ruebel <[email protected]>
Co-authored-by: Ryan Ly <[email protected]>

Set language for code block to fix readthedocs

Add RRID for PyNWB

Add RRID to README

Update changelog

make MotionCorrection test (#1374)

* attempt to make MotionCorrection test. I don't understand why it's failing

* Fix CorrectedImageStack nwbfields

* Fix tests

* Fix test

* Fix flake8

* Fix test

Co-authored-by: Ryan Ly <[email protected]>

Add unique electrode id check (#1344)

Co-authored-by: Ryan Ly <[email protected]>

Fix integration tests with invalid test data that will be caught by future hdmf validator version (#1366)

Co-authored-by: Ryan Ly <[email protected]>

Improve documentation and test for CorrectedImageStack (#1306)

Fix test for validator update (#1376)

Update changelog

Allow h5py 3 so that this can work with hdmf rc/3.0.0

Move SweepTable DeprectationWarning to the constructor of SweepTable to clean up code

Suppress SweepTable Deprecation warning in icephys integration test

Ignore and assert deprecation warning for SweepTable during SweepTable roundtrip tests

Merge branch 'rc/2.0.0' into add/icephys_meta

Fix falke8 on icephys tutorial galleries
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add_electrode does not check uniquess of id
3 participants