Skip to content

Commit ffaf60b

Browse files
committed
Add element_data_loader for multiple root dirs
1 parent 4f4be8d commit ffaf60b

File tree

3 files changed

+96
-59
lines changed

3 files changed

+96
-59
lines changed

README.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# DataJoint Element - Array Electrophysiology Element
2-
DataJoint Element for array electrophysiology.
32

43
This repository features DataJoint pipeline design for extracellular array electrophysiology,
54
with ***Neuropixels*** probe and ***kilosort*** spike sorting method.
@@ -45,14 +44,24 @@ This ephys element features automatic ingestion for spike sorting results from t
4544
+ ***WaveformSet*** - A set of spike waveforms for units from a given CuratedClustering
4645

4746
## Installation
48-
```
49-
pip install element-array-ephys
50-
```
51-
52-
If you already have an older version of ***element-array-ephys*** installed using `pip`, upgrade with
53-
```
54-
pip install --upgrade element-array-ephys
55-
```
47+
48+
+ Install `element-array-ephys`
49+
```
50+
pip install element-array-ephys
51+
```
52+
53+
+ Upgrade `element-array-ephys` previously installed with `pip`
54+
```
55+
pip install --upgrade element-array-ephys
56+
```
57+
58+
+ Install `element-data-loader`
59+
60+
+ `element-data-loader` is a dependency of `element-array-ephys`, however it is not contained within `requirements.txt`.
61+
62+
```
63+
pip install "element-data-loader @ git+https://github.com/datajoint/element-data-loader"
64+
```
5665
5766
## Usage
5867

element_array_ephys/ephys.py

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
import numpy as np
55
import inspect
66
import importlib
7+
import element_data_loader
78

89
from .readers import spikeglx, kilosort, openephys
9-
from . import probe, find_full_path, find_root_directory, dict_to_uuid
10+
from . import probe
1011

1112
schema = dj.schema()
1213

@@ -46,7 +47,6 @@ def activate(ephys_schema_name, probe_schema_name=None, *, create_schema=True,
4647
global _linking_module
4748
_linking_module = linking_module
4849

49-
# activate
5050
probe.activate(probe_schema_name, create_schema=create_schema,
5151
create_tables=create_tables)
5252
schema.activate(ephys_schema_name, create_schema=create_schema,
@@ -140,14 +140,16 @@ class EphysFile(dj.Part):
140140
"""
141141

142142
def make(self, key):
143-
sess_dir = pathlib.Path(get_session_directory(key))
143+
144+
session_dir = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(),
145+
get_session_directory(key))
144146

145147
inserted_probe_serial_number = (ProbeInsertion * probe.Probe & key).fetch1('probe')
146148

147149
# search session dir and determine acquisition software
148150
for ephys_pattern, ephys_acq_type in zip(['*.ap.meta', '*.oebin'],
149151
['SpikeGLX', 'Open Ephys']):
150-
ephys_meta_filepaths = [fp for fp in sess_dir.rglob(ephys_pattern)]
152+
ephys_meta_filepaths = [fp for fp in session_dir.rglob(ephys_pattern)]
151153
if ephys_meta_filepaths:
152154
acq_software = ephys_acq_type
153155
break
@@ -187,12 +189,12 @@ def make(self, key):
187189
'acq_software': acq_software,
188190
'sampling_rate': spikeglx_meta.meta['imSampRate']})
189191

190-
root_dir = find_root_directory(get_ephys_root_data_dir(), meta_filepath)
192+
root_dir = element_data_loader.utils.find_root_directory(get_ephys_root_data_dir(), meta_filepath)
191193
self.EphysFile.insert1({
192194
**key,
193195
'file_path': meta_filepath.relative_to(root_dir).as_posix()})
194196
elif acq_software == 'Open Ephys':
195-
dataset = openephys.OpenEphys(sess_dir)
197+
dataset = openephys.OpenEphys(session_dir)
196198
for serial_number, probe_data in dataset.probes.items():
197199
if str(serial_number) == inserted_probe_serial_number:
198200
break
@@ -220,7 +222,7 @@ def make(self, key):
220222
'acq_software': acq_software,
221223
'sampling_rate': probe_data.ap_meta['sample_rate']})
222224

223-
root_dir = find_root_directory(
225+
root_dir = element_data_loader.utils.find_root_directory(
224226
get_ephys_root_data_dir(),
225227
probe_data.recording_info['recording_files'][0])
226228
self.EphysFile.insert([{**key,
@@ -290,8 +292,12 @@ def make(self, key):
290292
shank, shank_col, shank_row, _ = spikeglx_recording.apmeta.shankmap['data'][recorded_site]
291293
electrode_keys.append(probe_electrodes[(shank, shank_col, shank_row)])
292294
elif acq_software == 'Open Ephys':
293-
sess_dir = pathlib.Path(get_session_directory(key))
294-
loaded_oe = openephys.OpenEphys(sess_dir)
295+
296+
session_dir = element_data_loader.utils.find_full_path(
297+
get_ephys_root_data_dir(),
298+
get_session_directory(key))
299+
300+
loaded_oe = openephys.OpenEphys(session_dir)
295301
oe_probe = loaded_oe.probes[probe_sn]
296302

297303
lfp_channel_ind = np.arange(
@@ -358,7 +364,7 @@ def insert_new_params(cls, processing_method: str, paramset_idx: int,
358364
'paramset_idx': paramset_idx,
359365
'paramset_desc': paramset_desc,
360366
'params': params,
361-
'param_set_hash': dict_to_uuid(params)}
367+
'param_set_hash': element_data_loader.utils.dict_to_uuid(params)}
362368
param_query = cls & {'param_set_hash': param_dict['param_set_hash']}
363369

364370
if param_query: # If the specified param-set already exists
@@ -420,7 +426,7 @@ class Clustering(dj.Imported):
420426
def make(self, key):
421427
task_mode, output_dir = (ClusteringTask & key).fetch1(
422428
'task_mode', 'clustering_output_dir')
423-
kilosort_dir = find_full_path(get_ephys_root_data_dir(), output_dir)
429+
kilosort_dir = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(), output_dir)
424430

425431
if task_mode == 'load':
426432
kilosort_dataset = kilosort.Kilosort(kilosort_dir) # check if the directory is a valid Kilosort output
@@ -450,23 +456,26 @@ class Curation(dj.Manual):
450456

451457
def create1_from_clustering_task(self, key, curation_note=''):
452458
"""
453-
A convenient function to create a new corresponding "Curation"
454-
for a particular "ClusteringTask"
459+
A function to create a new corresponding "Curation" for a particular
460+
"ClusteringTask", which assumes that no curation was performed on the
461+
dataset
455462
"""
456463
if key not in Clustering():
457464
raise ValueError(f'No corresponding entry in Clustering available'
458465
f' for: {key}; do `Clustering.populate(key)`')
459466

460467
task_mode, output_dir = (ClusteringTask & key).fetch1(
461468
'task_mode', 'clustering_output_dir')
462-
kilosort_dir = find_full_path(get_ephys_root_data_dir(), output_dir)
469+
kilosort_dir = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(), output_dir)
463470

464471
creation_time, is_curated, is_qc = kilosort.extract_clustering_info(kilosort_dir)
465472
# Synthesize curation_id
466473
curation_id = dj.U().aggr(self & key, n='ifnull(max(curation_id)+1,1)').fetch1('n')
467474
self.insert1({**key, 'curation_id': curation_id,
468-
'curation_time': creation_time, 'curation_output_dir': output_dir,
469-
'quality_control': is_qc, 'manual_curation': is_curated,
475+
'curation_time': creation_time,
476+
'curation_output_dir': output_dir,
477+
'quality_control': is_qc,
478+
'manual_curation': is_curated,
470479
'curation_note': curation_note})
471480

472481

@@ -493,7 +502,7 @@ class Unit(dj.Part):
493502

494503
def make(self, key):
495504
output_dir = (Curation & key).fetch1('curation_output_dir')
496-
kilosort_dir = find_full_path(get_ephys_root_data_dir(), output_dir)
505+
kilosort_dir = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(), output_dir)
497506

498507
kilosort_dataset = kilosort.Kilosort(kilosort_dir)
499508
acq_software = (EphysRecording & key).fetch1('acq_software')
@@ -571,7 +580,7 @@ class Waveform(dj.Part):
571580

572581
def make(self, key):
573582
output_dir = (Curation & key).fetch1('curation_output_dir')
574-
kilosort_dir = find_full_path(get_ephys_root_data_dir(), output_dir)
583+
kilosort_dir = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(), output_dir)
575584

576585
kilosort_dataset = kilosort.Kilosort(kilosort_dir)
577586

@@ -613,8 +622,9 @@ def yield_unit_waveforms():
613622
spikeglx_meta_filepath = get_spikeglx_meta_filepath(key)
614623
neuropixels_recording = spikeglx.SpikeGLX(spikeglx_meta_filepath.parent)
615624
elif acq_software == 'Open Ephys':
616-
sess_dir = pathlib.Path(get_session_directory(key))
617-
openephys_dataset = openephys.OpenEphys(sess_dir)
625+
session_dir = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(),
626+
get_session_directory(key))
627+
openephys_dataset = openephys.OpenEphys(session_dir)
618628
neuropixels_recording = openephys_dataset.probes[probe_serial_number]
619629

620630
def yield_unit_waveforms():
@@ -654,16 +664,17 @@ def get_spikeglx_meta_filepath(ephys_recording_key):
654664
& 'file_path LIKE "%.ap.meta"').fetch1('file_path')
655665

656666
try:
657-
spikeglx_meta_filepath = find_full_path(get_ephys_root_data_dir(),
667+
spikeglx_meta_filepath = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(),
658668
spikeglx_meta_filepath)
659669
except FileNotFoundError:
660670
# if not found, search in session_dir again
661671
if not spikeglx_meta_filepath.exists():
662-
sess_dir = pathlib.Path(get_session_directory(ephys_recording_key))
672+
session_dir = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(),
673+
get_session_directory(ephys_recording_key))
663674
inserted_probe_serial_number = (ProbeInsertion * probe.Probe
664675
& ephys_recording_key).fetch1('probe')
665676

666-
spikeglx_meta_filepaths = [fp for fp in sess_dir.rglob('*.ap.meta')]
677+
spikeglx_meta_filepaths = [fp for fp in session_dir.rglob('*.ap.meta')]
667678
for meta_filepath in spikeglx_meta_filepaths:
668679
spikeglx_meta = spikeglx.SpikeGLXMeta(meta_filepath)
669680
if str(spikeglx_meta.probe_SN) == inserted_probe_serial_number:
@@ -696,8 +707,9 @@ def get_neuropixels_channel2electrode_map(ephys_recording_key, acq_software):
696707
for recorded_site, (shank, shank_col, shank_row, _) in enumerate(
697708
spikeglx_meta.shankmap['data'])}
698709
elif acq_software == 'Open Ephys':
699-
sess_dir = pathlib.Path(get_session_directory(ephys_recording_key))
700-
openephys_dataset = openephys.OpenEphys(sess_dir)
710+
session_dir = element_data_loader.utils.find_full_path(get_ephys_root_data_dir(),
711+
get_session_directory(ephys_recording_key))
712+
openephys_dataset = openephys.OpenEphys(session_dir)
701713
probe_serial_number = (ProbeInsertion & ephys_recording_key).fetch1('probe')
702714
probe_dataset = openephys_dataset.probes[probe_serial_number]
703715

@@ -723,7 +735,7 @@ def generate_electrode_config(probe_type: str, electrodes: list):
723735
:return: a dict representing a key of the probe.ElectrodeConfig table
724736
"""
725737
# compute hash for the electrode config (hash of dict of all ElectrodeConfig.Electrode)
726-
electrode_config_hash = dict_to_uuid({k['electrode']: k for k in electrodes})
738+
electrode_config_hash = element_data_loader.utils.dict_to_uuid({k['electrode']: k for k in electrodes})
727739

728740
electrode_list = sorted([k['electrode'] for k in electrodes])
729741
electrode_gaps = ([-1]

0 commit comments

Comments
 (0)