Skip to content

Commit 11d2aec

Browse files
author
Thinh Nguyen
committed
code cleanup, bug fix, tested
1 parent 5834b4a commit 11d2aec

File tree

8 files changed

+64
-67
lines changed

8 files changed

+64
-67
lines changed

elements_ephys/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
from .ephys import schema as ephys
1+
__author__ = "DataJoint NEURO"
2+
__date__ = "December 15, 2020"
3+
__version__ = "0.0.1"
4+
5+
__all__ = ['__author__', '__version__', '__date__']

elements_ephys/ephys.py

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,68 +14,65 @@
1414

1515

1616
def activate(ephys_schema_name, probe_schema_name=None, create_schema=True, create_tables=True, add_objects=None):
17-
upstream_tables = ("Session", "SkullReference")
1817
assert isinstance(add_objects, Mapping)
18+
19+
upstream_tables = ("Session", "SkullReference")
1920
try:
2021
raise RuntimeError("Table %s is required for module ephys" % next(
2122
name for name in upstream_tables
22-
if not isinstance(add_objects.get(name, None), (dj.Manual, dj.Lookup, dj.Imported, dj.Computed))))
23+
if not isinstance(add_objects.get(name, None), (dj.Manual, dj.Lookup, dj.Imported, dj.Computed, dj.user_tables.OrderedClass))))
2324
except StopIteration:
2425
pass # all ok
2526

2627
required_functions = ("get_neuropixels_data_directory", "get_paramset_idx", "get_kilosort_output_directory")
27-
assert isinstance(add_objects, Mapping)
2828
try:
2929
raise RuntimeError("Function %s is required for module ephys" % next(
3030
name for name in required_functions
3131
if not inspect.isfunction(add_objects.get(name, None))))
3232
except StopIteration:
3333
pass # all ok
3434

35-
if not probe.schema.is_activated:
35+
if not probe.schema.database:
3636
probe.schema.activate(probe_schema_name or ephys_schema_name,
3737
create_schema=create_schema, create_tables=create_tables)
38+
3839
schema.activate(ephys_schema_name, create_schema=create_schema,
3940
create_tables=create_tables, add_objects=add_objects)
4041

4142

4243
# REQUIREMENTS: The workflow module must define these functions ---------------
4344

4445

45-
def get_neuropixels_data_directory():
46-
return None
46+
def get_neuropixels_data_directory(probe_insertion_key: dict) -> str:
47+
"""
48+
Retrieve the recorded Neuropixels data directory for a given ProbeInsertion
49+
:param probe_insertion_key: a dictionary of one ProbeInsertion `key`
50+
:return: a string for full path to the resulting Neuropixels data directory
51+
"""
52+
assert set(ProbeInsertion().primary_key) <= set(probe_insertion_key)
53+
raise NotImplementedError('Workflow module should define function: "get_neuropixels_data_directory"')
4754

4855

4956
def get_kilosort_output_directory(clustering_task_key: dict) -> str:
5057
"""
5158
Retrieve the Kilosort output directory for a given ClusteringTask
52-
:param clustering_task_key: a dictionary of one EphysRecording
59+
:param clustering_task_key: a dictionary of one ClusteringTask `key`
5360
:return: a string for full path to the resulting Kilosort output directory
5461
"""
5562
assert set(EphysRecording().primary_key) <= set(clustering_task_key)
56-
raise NotImplementedError('Workflow module should define')
63+
raise NotImplementedError('Workflow module should define function: "get_kilosort_output_directory"')
5764

5865

5966
def get_paramset_idx(ephys_rec_key: dict) -> int:
6067
"""
61-
Retrieve attribute `paramset_idx` from the ClusteringParamSet record for the given EphysRecording key.
62-
:param ephys_rec_key: a dictionary of one EphysRecording
68+
Retrieve attribute `paramset_idx` from the ClusteringParamSet record for the given EphysRecording.
69+
:param ephys_rec_key: a dictionary of one EphysRecording `key`
6370
:return: int specifying the `paramset_idx`
6471
"""
6572
assert set(EphysRecording().primary_key) <= set(ephys_rec_key)
66-
raise NotImplementedError('Workflow module should define')
73+
raise NotImplementedError('Workflow module should define function: get_paramset_idx')
6774

6875

69-
def dict_to_uuid(key):
70-
"""
71-
Given a dictionary `key`, returns a hash string
72-
"""
73-
hashed = hashlib.md5()
74-
for k, v in sorted(key.items()):
75-
hashed.update(str(k).encode())
76-
hashed.update(str(v).encode())
77-
return uuid.UUID(hex=hashed.hexdigest())
78-
7976
# ===================================== Probe Insertion =====================================
8077

8178

@@ -109,17 +106,13 @@ class InsertionLocation(dj.Manual):
109106

110107

111108
# ===================================== Ephys Recording =====================================
112-
# The abstract function _get_neuropixels_data_directory() should expect one argument in the form of a
113-
# dictionary with the keys from user-defined Subject and Session, as well as
114-
# "insertion_number" (as int) based on the "ProbeInsertion" table definition in this djephys
115-
116109

117110
@schema
118111
class EphysRecording(dj.Imported):
119112
definition = """
120113
-> ProbeInsertion
121114
---
122-
-> ElectrodeConfig
115+
-> probe.ElectrodeConfig
123116
sampling_rate: float # (Hz)
124117
"""
125118

@@ -143,7 +136,7 @@ def make(self, key):
143136
neuropixels_meta.probe_model))
144137

145138
# ---- compute hash for the electrode config (hash of dict of all ElectrodeConfig.Electrode) ----
146-
ec_hash = uuid.UUID(dict_to_uuid({k['electrode']: k for k in eg_members}))
139+
ec_hash = dict_to_uuid({k['electrode']: k for k in eg_members})
147140

148141
el_list = sorted([k['electrode'] for k in eg_members])
149142
el_jumps = [-1] + np.where(np.diff(el_list) > 1)[0].tolist() + [len(el_list) - 1]
@@ -152,9 +145,9 @@ def make(self, key):
152145
e_config = {'electrode_config_hash': ec_hash}
153146

154147
# ---- make new ElectrodeConfig if needed ----
155-
if not (ElectrodeConfig & e_config):
156-
ElectrodeConfig.insert1({**e_config, **probe_type, 'electrode_config_name': ec_name})
157-
ElectrodeConfig.Electrode.insert({**e_config, **m} for m in eg_members)
148+
if not (probe.ElectrodeConfig & e_config):
149+
probe.ElectrodeConfig.insert1({**e_config, **probe_type, 'electrode_config_name': ec_name})
150+
probe.ElectrodeConfig.Electrode.insert({**e_config, **m} for m in eg_members)
158151

159152
self.insert1({**key, **e_config, 'sampling_rate': neuropixels_meta.meta['imSampRate']})
160153

@@ -185,7 +178,7 @@ class Electrode(dj.Part):
185178
"""
186179

187180
def make(self, key):
188-
neuropixels_dir = EphysRecording._get_neuropixels_data_directory(key)
181+
neuropixels_dir = get_neuropixels_data_directory(key)
189182
neuropixels_recording = neuropixels.Neuropixels(neuropixels_dir)
190183

191184
lfp = neuropixels_recording.lfdata[:, :-1].T # exclude the sync channel
@@ -201,7 +194,7 @@ def make(self, key):
201194
q_electrodes = probe.ProbeType.Electrode * probe.ElectrodeConfig.Electrode & key
202195
electrodes = []
203196
for recorded_site in np.arange(lfp.shape[0]):
204-
shank, shank_col, shank_row, _ = neuropixels_recording.neuropixels_meta.shankmap['data'][recorded_site]
197+
shank, shank_col, shank_row, _ = neuropixels_recording.npx_meta.shankmap['data'][recorded_site]
205198
electrodes.append((q_electrodes
206199
& {'shank': shank,
207200
'shank_col': shank_col,
@@ -268,14 +261,15 @@ class ClusteringTask(dj.Imported):
268261
"""
269262

270263
def make(self, key):
271-
key['paramset_idx'] = ClusteringTask._get_paramset_idx(key)
272-
self.insert1(key)
264+
key['paramset_idx'] = get_paramset_idx(key)
273265

274-
data_dir = pathlib.Path(ClusteringTask._get_ks_data_dir(key))
266+
data_dir = pathlib.Path(get_kilosort_output_directory(key))
275267
if not data_dir.exists():
276268
# this is where we can trigger the clustering job
277269
print(f'Clustering results not yet found for {key} - Triggering kilosort not yet implemented!')
278270

271+
self.insert1(key)
272+
279273

280274
@schema
281275
class ClusterQualityLabel(dj.Lookup):
@@ -318,7 +312,7 @@ class Unit(dj.Part):
318312
"""
319313

320314
def make(self, key):
321-
ks_dir = ClusteringTask._get_ks_data_dir(key)
315+
ks_dir = get_kilosort_output_directory(key)
322316
ks = kilosort.Kilosort(ks_dir)
323317
# ---------- Clustering ----------
324318
creation_time, is_curated, is_qc = kilosort.extract_clustering_info(ks_dir)
@@ -461,7 +455,7 @@ def make(self, key):
461455

462456

463457
def get_neuropixels_chn2electrode_map(ephys_recording_key):
464-
neuropixels_dir = EphysRecording._get_neuropixels_data_directory(ephys_recording_key)
458+
neuropixels_dir = get_neuropixels_data_directory(ephys_recording_key)
465459
meta_filepath = next(pathlib.Path(neuropixels_dir).glob('*.ap.meta'))
466460
neuropixels_meta = neuropixels.NeuropixelsMeta(meta_filepath)
467461
e_config_key = (EphysRecording * probe.ElectrodeConfig & ephys_recording_key).fetch1('KEY')
@@ -474,3 +468,14 @@ def get_neuropixels_chn2electrode_map(ephys_recording_key):
474468
'shank_col': shank_col,
475469
'shank_row': shank_row}).fetch1('KEY')
476470
return chn2electrode_map
471+
472+
473+
def dict_to_uuid(key):
474+
"""
475+
Given a dictionary `key`, returns a hash string
476+
"""
477+
hashed = hashlib.md5()
478+
for k, v in sorted(key.items()):
479+
hashed.update(str(k).encode())
480+
hashed.update(str(v).encode())
481+
return uuid.UUID(hex=hashed.hexdigest())

elements_ephys/probe.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
def activate(schema_name, create_schema=True, create_tables=True):
1111
schema.activate(schema_name, create_schema=create_schema, create_tables=create_tables)
1212

13+
1314
@schema
1415
class ProbeType(dj.Lookup):
1516
definition = """

elements_ephys/readers/kilosort.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,11 @@
44
import numpy as np
55
import re
66
import logging
7+
from .utils import convert_to_number
78

89
log = logging.getLogger(__name__)
910

1011

11-
def convert_to_number(value: str):
12-
if isinstance(value, str):
13-
try:
14-
value = int(value)
15-
except ValueError:
16-
try:
17-
value = float(value)
18-
except ValueError:
19-
pass
20-
return value
21-
22-
2312
class Kilosort:
2413

2514
ks_files = [
@@ -85,7 +74,7 @@ def _stat(self):
8574
prm = {}
8675
for line in open(f, 'r').readlines():
8776
k, v = line.strip('\n').split('=')
88-
prm[k.strip()] = handle_string(v.strip())
77+
prm[k.strip()] = convert_to_number(v.strip())
8978
log.debug('prm: {}'.format(prm))
9079
self._data[base] = prm
9180

elements_ephys/readers/neuropixels.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
from datetime import datetime
22
import numpy as np
33
import pathlib
4-
5-
6-
def convert_to_number(value: str):
7-
if isinstance(value, str):
8-
try:
9-
value = int(value)
10-
except ValueError:
11-
try:
12-
value = float(value)
13-
except ValueError:
14-
pass
15-
return value
4+
from .utils import convert_to_number
165

176

187
class Neuropixels:

elements_ephys/readers/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
3+
def convert_to_number(value: str):
4+
if isinstance(value, str):
5+
try:
6+
value = int(value)
7+
except ValueError:
8+
try:
9+
value = float(value)
10+
except ValueError:
11+
pass
12+
return value

requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
datajoint
2-
canonical-utils @ git+https://github.com/vathes/canonical-utils.git
1+
datajoint>=0.12.7

setup.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
#!/usr/bin/env python
21
from setuptools import setup, find_packages
32
from os import path
4-
import sys
53

64
here = path.abspath(path.dirname(__file__))
75

0 commit comments

Comments
 (0)