Skip to content

Commit b510cb8

Browse files
authored
Merge pull request #200 from ttngu207/datajoint-spikeinterface
fix(probe): better handling of different Neuropixels probe types and SpikeGLX meta loader
2 parents dcfd880 + 5dc5953 commit b510cb8

File tree

7 files changed

+80
-60
lines changed

7 files changed

+80
-60
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and
44
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention.
55

6-
## [0.4.0] - 2024-05-28
6+
## [0.4.0] - 2024-08-16
77

88
+ Add - support for SpikeInterface version >= 0.101.0 (updated API)
99
+ Add - feature for memoization of spike sorting results (prevent duplicated runs)
1010

1111

12+
## [0.3.5] - 2024-08-16
13+
14+
+ Fix - Improve `spikeglx` loader in extracting neuropixels probe type from the meta file
15+
+ Update - Explicit call to `probe.create_neuropixels_probe_types()` to create entries in ProbeType
16+
17+
1218
## [0.3.4] - 2024-03-22
1319

1420
+ Add - pytest

element_array_ephys/probe.py

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
"""
2-
Neuropixels Probes
3-
"""
4-
51
import datajoint as dj
62

73
from .readers import probe_geometry
84
from .readers.probe_geometry import build_electrode_layouts
95

6+
log = dj.logger
7+
108
schema = dj.schema()
119

1210

@@ -33,20 +31,6 @@ def activate(
3331
schema_name, create_schema=create_schema, create_tables=create_tables
3432
)
3533

36-
# Add neuropixels probes
37-
for probe_type in (
38-
"neuropixels 1.0 - 3A",
39-
"neuropixels 1.0 - 3B",
40-
"neuropixels UHD",
41-
"neuropixels 2.0 - SS",
42-
"neuropixels 2.0 - MS",
43-
):
44-
if not (ProbeType & {"probe_type": probe_type}):
45-
try:
46-
ProbeType.create_neuropixels_probe(probe_type)
47-
except dj.errors.DataJointError as e:
48-
print(f"Unable to create probe-type: {probe_type}\n{str(e)}")
49-
5034

5135
@schema
5236
class ProbeType(dj.Lookup):
@@ -87,39 +71,10 @@ class Electrode(dj.Part):
8771

8872
@staticmethod
8973
def create_neuropixels_probe(probe_type: str = "neuropixels 1.0 - 3A"):
90-
"""
91-
Create `ProbeType` and `Electrode` for neuropixels probes:
92-
+ neuropixels 1.0 - 3A
93-
+ neuropixels 1.0 - 3B
94-
+ neuropixels UHD
95-
+ neuropixels 2.0 - SS
96-
+ neuropixels 2.0 - MS
97-
98-
For electrode location, the (0, 0) is the
99-
bottom left corner of the probe (ignore the tip portion)
100-
Electrode numbering is 0-indexing
101-
"""
102-
103-
npx_probes_config = probe_geometry.M
104-
npx_probes_config["neuropixels 1.0 - 3A"] = npx_probes_config["3A"]
105-
npx_probes_config["neuropixels 1.0 - 3B"] = npx_probes_config["NP1010"]
106-
npx_probes_config["neuropixels UHD"] = npx_probes_config["NP1100"]
107-
npx_probes_config["neuropixels 2.0 - SS"] = npx_probes_config["NP2000"]
108-
npx_probes_config["neuropixels 2.0 - MS"] = npx_probes_config["NP2010"]
109-
110-
probe_type = {"probe_type": probe_type}
111-
probe_params = dict(
112-
zip(
113-
probe_geometry.geom_param_names,
114-
npx_probes_config[probe_type["probe_type"]],
115-
)
116-
)
117-
electrode_layouts = probe_geometry.build_npx_probe(
118-
**{**probe_params, **probe_type}
74+
log.warning(
75+
"Class method `ProbeType.create_neuropixels_probe` is deprecated. Use `create_neuropixels_probe` instead.",
11976
)
120-
with ProbeType.connection.transaction:
121-
ProbeType.insert1(probe_type, skip_duplicates=True)
122-
ProbeType.Electrode.insert(electrode_layouts, skip_duplicates=True)
77+
return create_neuropixels_probe(probe_type)
12378

12479

12580
@schema
@@ -171,3 +126,49 @@ class Electrode(dj.Part):
171126
-> master
172127
-> ProbeType.Electrode
173128
"""
129+
130+
131+
def create_neuropixels_probe_types():
132+
# Add neuropixels probes
133+
for probe_type in (
134+
"neuropixels 1.0 - 3A",
135+
"neuropixels 1.0 - 3B",
136+
"neuropixels UHD",
137+
"neuropixels 2.0 - SS",
138+
"neuropixels 2.0 - MS",
139+
):
140+
if not (ProbeType & {"probe_type": probe_type}):
141+
create_neuropixels_probe(probe_type)
142+
143+
144+
def create_neuropixels_probe(probe_type: str = "neuropixels 1.0 - 3A"):
145+
"""
146+
Create `ProbeType` and `Electrode` for neuropixels probes:
147+
+ neuropixels 1.0 - 3A
148+
+ neuropixels 1.0 - 3B
149+
+ neuropixels UHD
150+
+ neuropixels 2.0 - SS
151+
+ neuropixels 2.0 - MS
152+
153+
For electrode location, the (0, 0) is the
154+
bottom left corner of the probe (ignore the tip portion)
155+
Electrode numbering is 0-indexing
156+
"""
157+
npx_probes_config = probe_geometry.M
158+
if probe_type not in npx_probes_config:
159+
raise ValueError(
160+
f"Probe type {probe_type} not found in probe_geometry configuration. Not a Neuropixels probe?"
161+
)
162+
163+
probe_params = dict(
164+
zip(
165+
probe_geometry.geom_param_names,
166+
npx_probes_config[probe_type],
167+
)
168+
)
169+
electrode_layouts = probe_geometry.build_npx_probe(
170+
**{**probe_params, "probe_type": probe_type}
171+
)
172+
with ProbeType.connection.transaction:
173+
ProbeType.insert1({"probe_type": probe_type})
174+
ProbeType.Electrode.insert(electrode_layouts)

element_array_ephys/readers/probe_geometry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@
101101
]
102102
)
103103

104+
# additional alias to maintain compatibility with previous naming in the pipeline
105+
M["neuropixels 1.0 - 3A"] = M["3A"]
106+
M["neuropixels 1.0 - 3B"] = M["NP1010"]
107+
M["neuropixels 1.0"] = M["NP1010"]
108+
M["neuropixels UHD"] = M["NP1100"]
109+
M["neuropixels 2.0 - SS"] = M["NP2000"]
110+
M["neuropixels 2.0 - MS"] = M["NP2010"]
111+
104112

105113
def build_npx_probe(
106114
nShank: int,

element_array_ephys/readers/spikeglx.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,18 @@ def __init__(self, meta_filepath):
262262
self.fname = meta_filepath
263263
self.meta = _read_meta(meta_filepath)
264264

265+
# Get probe part number
266+
self.probe_PN = self.meta.get("imDatPrb_pn", "3A")
267+
265268
# Infer npx probe model (e.g. 1.0 (3A, 3B) or 2.0)
266269
probe_model = self.meta.get("imDatPrb_type", 1)
267-
if probe_model <= 1:
268-
if "typeEnabled" in self.meta:
270+
if probe_model < 1:
271+
if "typeEnabled" in self.meta and self.probe_PN == "3A":
269272
self.probe_model = "neuropixels 1.0 - 3A"
270-
elif "typeImEnabled" in self.meta:
271-
self.probe_model = "neuropixels 1.0 - 3B"
273+
elif "typeImEnabled" in self.meta and self.probe_PN == "NP1010":
274+
self.probe_model = "neuropixels 1.0"
275+
else:
276+
self.probe_model = self.probe_PN
272277
elif probe_model == 1100:
273278
self.probe_model = "neuropixels UHD"
274279
elif probe_model == 21:
@@ -293,8 +298,6 @@ def __init__(self, meta_filepath):
293298
"Probe Serial Number not found in"
294299
' either "imProbeSN" or "imDatPrb_sn"'
295300
)
296-
# Get probe part number
297-
self.probe_PN = self.meta.get("imDatPrb_pn", "3A")
298301

299302
# Parse channel info
300303
self.chanmap = (

element_array_ephys/spike_sorting/si_spike_sorting.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def make(self, key):
114114

115115
si_extractor = si.extractors.neoextractors.spikeglx.SpikeGLXRecordingExtractor
116116
stream_names, stream_ids = si.extractors.get_neo_streams(
117-
acq_software, folder_path=data_dir
117+
"spikeglx", folder_path=data_dir
118118
)
119119
si_recording: si.BaseRecording = si_extractor(
120120
folder_path=data_dir, stream_name=stream_names[0]
@@ -126,7 +126,7 @@ def make(self, key):
126126
si_extractor = si.extractors.neoextractors.openephys.OpenEphysBinaryRecordingExtractor
127127

128128
stream_names, stream_ids = si.extractors.get_neo_streams(
129-
acq_software, folder_path=data_dir
129+
"openephysbinary", folder_path=data_dir
130130
)
131131
si_recording: si.BaseRecording = si_extractor(
132132
folder_path=data_dir, stream_name=stream_names[0]

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"scikit-image",
4040
"nbformat>=4.2.0",
4141
"pyopenephys>=1.1.6",
42-
"element-interface @ git+https://github.com/datajoint/element-interface.git@dev_memoized_results",
42+
"element-interface @ git+https://github.com/datajoint/element-interface.git",
4343
"numba",
4444
],
4545
extras_require={

tests/tutorial_pipeline.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,7 @@ def get_session_directory(session_key):
6464

6565
ephys.activate(db_prefix + "ephys", db_prefix + "probe", linking_module=__name__)
6666

67+
probe.create_neuropixels_probe_types()
68+
6769

6870
__all__ = [""]

0 commit comments

Comments
 (0)