Skip to content

Commit cd65539

Browse files
author
Thinh Nguyen
authored
Merge pull request #184 from JaerongA/datajoint-spikeinterface
Fix & cleanup ephys_no_curation.py & si_spike_sorting.py
2 parents 90b81e3 + 01ff816 commit cd65539

File tree

2 files changed

+18
-42
lines changed

2 files changed

+18
-42
lines changed

element_array_ephys/ephys_no_curation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,8 +1603,8 @@ class Waveform(dj.Part):
16031603
-> master
16041604
-> CuratedClustering.Unit
16051605
---
1606-
amplitude: float # (uV) absolute difference between waveform peak and trough
1607-
duration: float # (ms) time between waveform peak and trough
1606+
amplitude=null: float # (uV) absolute difference between waveform peak and trough
1607+
duration=null: float # (ms) time between waveform peak and trough
16081608
halfwidth=null: float # (ms) spike width at half max amplitude
16091609
pt_ratio=null: float # absolute amplitude of peak divided by absolute amplitude of trough relative to 0
16101610
repolarization_slope=null: float # the repolarization slope was defined by fitting a regression line to the first 30us from trough to peak

element_array_ephys/spike_sorting/si_spike_sorting.py

Lines changed: 16 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,17 @@
11
"""
2-
The following DataJoint pipeline implements the sequence of steps in the spike-sorting routine featured in the
3-
"spikeinterface" pipeline.
4-
Spikeinterface developed by Alessio Buccino, Samuel Garcia, Cole Hurwitz, Jeremy Magland, and Matthias Hennig (https://github.com/SpikeInterface)
5-
6-
The DataJoint pipeline currently incorporated Spikeinterfaces approach of running Kilosort using a container
7-
8-
The follow pipeline features intermediary tables:
9-
1. PreProcessing - for preprocessing steps (no GPU required)
10-
- create recording extractor and link it to a probe
11-
- bandpass filtering
12-
- common mode referencing
13-
2. SIClustering - kilosort (MATLAB) - requires GPU and docker/singularity containers
14-
- supports kilosort 2.0, 2.5 or 3.0 (https://github.com/MouseLand/Kilosort.git)
15-
3. PostProcessing - for postprocessing steps (no GPU required)
16-
- create waveform extractor object
17-
- extract templates, waveforms and snrs
18-
- quality_metrics
2+
The following DataJoint pipeline implements the sequence of steps in the spike-sorting routine featured in the "spikeinterface" pipeline. Spikeinterface was developed by Alessio Buccino, Samuel Garcia, Cole Hurwitz, Jeremy Magland, and Matthias Hennig (https://github.com/SpikeInterface)
193
"""
204

21-
import pathlib
225
from datetime import datetime
236

247
import datajoint as dj
258
import pandas as pd
26-
import probeinterface as pi
279
import spikeinterface as si
2810
from element_array_ephys import get_logger, probe, readers
2911
from element_interface.utils import find_full_path
3012
from spikeinterface import exporters, postprocessing, qualitymetrics, sorters
3113

14+
from .. import get_logger, probe, readers
3215
from . import si_preprocessing
3316

3417
log = get_logger(__name__)
@@ -64,12 +47,6 @@ def activate(
6447

6548
SI_SORTERS = [s.replace("_", ".") for s in si.sorters.sorter_dict.keys()]
6649

67-
SI_READERS = {
68-
"Open Ephys": si.extractors.read_openephys,
69-
"SpikeGLX": si.extractors.read_spikeglx,
70-
"Intan": si.extractors.read_intan,
71-
}
72-
7350

7451
@schema
7552
class PreProcessing(dj.Imported):
@@ -125,9 +102,7 @@ def make(self, key):
125102
output_dir = find_full_path(ephys.get_ephys_root_data_dir(), output_dir)
126103
recording_dir = output_dir / sorter_name / "recording"
127104
recording_dir.mkdir(parents=True, exist_ok=True)
128-
recording_file = (
129-
recording_dir / "si_recording.pkl"
130-
) # recording cache to be created for each key
105+
recording_file = recording_dir / "si_recording.pkl"
131106

132107
# Create SI recording extractor object
133108
if acq_software == "SpikeGLX":
@@ -142,12 +117,15 @@ def make(self, key):
142117
assert len(oe_probe.recording_info["recording_files"]) == 1
143118
data_dir = oe_probe.recording_info["recording_files"][0]
144119
else:
145-
raise NotImplementedError(f"Not implemented for {acq_software}")
120+
acq_software = acq_software.replace(" ", "").lower()
121+
si_extractor: si.extractors.neoextractors = (
122+
si.extractors.extractorlist.recording_extractor_full_dict[acq_software]
123+
) # data extractor object
146124

147125
stream_names, stream_ids = si.extractors.get_neo_streams(
148-
acq_software.strip().lower(), folder_path=data_dir
126+
acq_software, folder_path=data_dir
149127
)
150-
si_recording: si.BaseRecording = SI_READERS[acq_software](
128+
si_recording: si.BaseRecording = si_extractor[acq_software](
151129
folder_path=data_dir, stream_name=stream_names[0]
152130
)
153131

@@ -205,23 +183,23 @@ def make(self, key):
205183
ephys.ClusteringTask * ephys.ClusteringParamSet & key
206184
).fetch1("clustering_method", "clustering_output_dir", "params")
207185
output_dir = find_full_path(ephys.get_ephys_root_data_dir(), output_dir)
208-
209-
# Get sorter method and create output directory.
210186
sorter_name = clustering_method.replace(".", "_")
211187
recording_file = output_dir / sorter_name / "recording" / "si_recording.pkl"
212188
si_recording: si.BaseRecording = si.load_extractor(recording_file)
213189

214190
# Run sorting
191+
# Sorting performed in a dedicated docker environment if the sorter is not built in the spikeinterface package.
215192
si_sorting: si.sorters.BaseSorter = si.sorters.run_sorter(
216193
sorter_name=sorter_name,
217194
recording=si_recording,
218195
output_folder=output_dir / sorter_name / "spike_sorting",
219196
remove_existing_folder=True,
220197
verbose=True,
221-
docker_image=True,
198+
docker_image=sorter_name not in si.sorters.installed_sorters(),
222199
**params.get("SI_SORTING_PARAMS", {}),
223200
)
224201

202+
# Save sorting object
225203
sorting_save_path = (
226204
output_dir / sorter_name / "spike_sorting" / "si_sorting.pkl"
227205
)
@@ -253,15 +231,14 @@ class PostProcessing(dj.Imported):
253231
def make(self, key):
254232
execution_time = datetime.utcnow()
255233

256-
# Load recording object.
234+
# Load recording & sorting object.
257235
clustering_method, output_dir, params = (
258236
ephys.ClusteringTask * ephys.ClusteringParamSet & key
259237
).fetch1("clustering_method", "clustering_output_dir", "params")
260238
output_dir = find_full_path(ephys.get_ephys_root_data_dir(), output_dir)
261-
262-
# Get sorter method and create output directory.
263239
sorter_name = clustering_method.replace(".", "_")
264240
output_dir = find_full_path(ephys.get_ephys_root_data_dir(), output_dir)
241+
265242
recording_file = output_dir / sorter_name / "recording" / "si_recording.pkl"
266243
sorting_file = output_dir / sorter_name / "spike_sorting" / "si_sorting.pkl"
267244

@@ -301,14 +278,13 @@ def make(self, key):
301278
_ = si.postprocessing.compute_principal_components(
302279
waveform_extractor=we, **params.get("SI_QUALITY_METRICS_PARAMS", None)
303280
)
281+
metrics = si.qualitymetrics.compute_quality_metrics(waveform_extractor=we)
282+
304283
# Save the output (metrics.csv to the output dir)
305284
metrics_output_dir = output_dir / sorter_name / "metrics"
306285
metrics_output_dir.mkdir(parents=True, exist_ok=True)
307-
308-
metrics = si.qualitymetrics.compute_quality_metrics(waveform_extractor=we)
309286
metrics.to_csv(metrics_output_dir / "metrics.csv")
310287

311-
# Save results
312288
self.insert1(
313289
{
314290
**key,

0 commit comments

Comments
 (0)