Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 49 additions & 9 deletions brainbox/io/one.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,20 @@ def _get_spike_sorting_collection(self, spike_sorter='pykilosort'):
_logger.debug(f"selecting: {collection} to load amongst candidates: {self.collections}")
return collection

def load_spike_sorting_object(self, obj, *args, **kwargs):
"""
Loads an ALF object
:param obj: object name, str between 'spikes', 'clusters' or 'channels'
:param spike_sorter: (defaults to 'pykilosort')
:param dataset_types: list of extra dataset types, for example ['spikes.samples']
:param collection: string specifiying the collection, for example 'alf/probe01/pykilosort'
:param kwargs: additional arguments to be passed to one.api.One.load_object
:param missing: 'raise' (default) or 'ignore'
:return:
"""
self.download_spike_sorting_object(obj, *args, **kwargs)
return alfio.load_object(self.files[obj])

def download_spike_sorting_object(self, obj, spike_sorter='pykilosort', dataset_types=None, collection=None,
missing='raise', **kwargs):
"""
Expand Down Expand Up @@ -998,21 +1012,41 @@ def url(self):
webclient = getattr(self.one, '_web_client', None)
return webclient.rel_path2url(get_alf_path(self.session_path)) if webclient else None

def samples2times(self, values, direction='forward'):
"""
:param values: numpy array of times in seconds or samples to resync
:param direction: 'forward' (samples probe time to seconds main time) or 'reverse'
(seconds main time to samples probe time)
:return:
"""
def _get_probe_info(self):
if self._sync is None:
timestamps = self.one.load_dataset(
self.eid, dataset='_spikeglx_*.timestamps.npy', collection=f'raw_ephys_data/{self.pname}')
try:
ap_meta = spikeglx.read_meta_data(self.one.load_dataset(
self.eid, dataset='_spikeglx_*.ap.meta', collection=f'raw_ephys_data/{self.pname}'))
fs = spikeglx._get_fs_from_meta(ap_meta)
except ALFObjectNotFound:
ap_meta = None
fs = 30_000
self._sync = {
'timestamps': timestamps,
'forward': interp1d(timestamps[:, 0], timestamps[:, 1], fill_value='extrapolate'),
'reverse': interp1d(timestamps[:, 1], timestamps[:, 0], fill_value='extrapolate'),
'ap_meta': ap_meta,
'fs': fs,
}

def timesprobe2times(self, values, direction='forward'):
self._get_probe_info()
if direction == 'forward':
return self._sync['forward'](values * self._sync['fs'])
elif direction == 'reverse':
return self._sync['reverse'](values) / self._sync['fs']

def samples2times(self, values, direction='forward'):
"""
Converts ephys sample values to session main clock seconds
:param values: numpy array of times in seconds or samples to resync
:param direction: 'forward' (samples probe time to seconds main time) or 'reverse'
(seconds main time to samples probe time)
:return:
"""
self._get_probe_info()
return self._sync[direction](values)

@property
Expand Down Expand Up @@ -1390,15 +1424,21 @@ class EphysSessionLoader(SessionLoader):
"""
Spike sorting enhanced version of SessionLoader
Loads spike sorting data for all probes in the session, in the self.ephys dict
>>> EphysSessionLoader(eid=eid, one=one)
To select for a specific probe
>>> EphysSessionLoader(eid=eid, one=one, pid=pid)
"""
def __init__(self, *args, **kwargs):
def __init__(self, *args, pname=None, pid=None, **kwargs):
"""
Needs an active connection in order to get the list of insertions in the session
:param args:
:param kwargs:
"""
super().__init__(*args, **kwargs)
insertions = self.one.alyx.rest('insertions', 'list', session=self.eid)
# if necessary, restrict the query
qargs = {} if pname is None else {'name': pname}
qargs = qargs or ({} if pid is None else {'id': pid})
insertions = self.one.alyx.rest('insertions', 'list', session=self.eid, **qargs)
self.ephys = {}
for ins in insertions:
self.ephys[ins['name']] = {}
Expand Down
27 changes: 10 additions & 17 deletions ibllib/io/extractors/training_trials.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,16 @@ class FeedbackType(BaseBpodTrialsExtractor):
var_names = 'feedbackType'

def _extract(self):
feedbackType = np.empty(len(self.bpod_trials))
feedbackType.fill(np.nan)
reward = []
error = []
no_go = []
for t in self.bpod_trials:
reward.append(~np.isnan(t['behavior_data']['States timestamps']['reward'][0][0]))
error.append(~np.isnan(t['behavior_data']['States timestamps']['error'][0][0]))
no_go.append(~np.isnan(t['behavior_data']['States timestamps']['no_go'][0][0]))

if not all(np.sum([reward, error, no_go], axis=0) == np.ones(len(self.bpod_trials))):
raise ValueError

feedbackType[reward] = 1
feedbackType[error] = -1
feedbackType[no_go] = -1
feedbackType = feedbackType.astype('int64')
feedbackType = np.zeros(len(self.bpod_trials), np.int64)
for i, t in enumerate(self.bpod_trials):
state_names = ['correct', 'error', 'no_go', 'omit_correct', 'omit_error', 'omit_no_go']
outcome = {sn: ~np.isnan(t['behavior_data']['States timestamps'].get(sn, [[np.NaN]])[0][0]) for sn in state_names}
assert np.sum(list(outcome.values())) == 1
outcome = next(k for k in outcome if outcome[k])
if outcome == 'correct':
feedbackType[i] = 1
elif outcome in ['error', 'no_go']:
feedbackType[i] = -1
return feedbackType


Expand Down
9 changes: 4 additions & 5 deletions ibllib/io/raw_data_loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,13 +323,12 @@ def _read_settings_json_compatibility_enforced(settings):
md = json.load(js)
if 'IS_MOCK' not in md.keys():
md['IS_MOCK'] = False
if not md.get('IBLRIG_VERSION_TAG'):
_logger.warning("You appear to be on an untagged version...")
return md
if 'IBLRIG_VERSION_TAG' not in md.keys():
md['IBLRIG_VERSION_TAG'] = ''
md['IBLRIG_VERSION_TAG'] = md.get('IBLRIG_VERSION', '')
# 2018-12-05 Version 3.2.3 fixes (permanent fixes in IBL_RIG from 3.2.4 on)
if parse_version(md.get('IBLRIG_VERSION_TAG') or '3.2.3') <= parse_version('3.2.3'):
if md['IBLRIG_VERSION_TAG'] == '':
pass
elif parse_version(md.get('IBLRIG_VERSION_TAG')) <= parse_version('3.2.3'):
if 'LAST_TRIAL_DATA' in md.keys():
md.pop('LAST_TRIAL_DATA')
if 'weighings' in md['PYBPOD_SUBJECT_EXTRA'].keys():
Expand Down
7 changes: 6 additions & 1 deletion release_notes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Release Notes 2.22
### Release Notes 2.22.0 (in preparation)
- trials extractors support omissions trials from neuromodulator tasks
- SpikeSortingLoader and EphysSessionLoader utils functions to load related objects such as drift

## Release Notes 2.21
### Release Notes 2.21.3 2023-03-22
### features
Expand All @@ -6,7 +11,7 @@

### bugfixes
- register_session: handle lack of taskData in passive sessions
-

### Release Notes 2.21.2 2023-02-24
### bugfixes
- get_lab function now gets lab name from session path subject name
Expand Down