Skip to content

Commit 411c494

Browse files
committed
Merge tag '2.35.2' into develop
2 parents e6981a9 + 7270eaf commit 411c494

File tree

12 files changed

+77
-39
lines changed

12 files changed

+77
-39
lines changed

brainbox/metrics/single_units.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ def quick_unit_metrics(spike_clusters, spike_times, spike_amps, spike_depths,
980980
r.amp_median[ir] = np.array(10 ** (camp['log_amps'].median() / 20))
981981
r.amp_std_dB[ir] = np.array(camp['log_amps'].std())
982982
srp = metrics.slidingRP_all(spikeTimes=spike_times, spikeClusters=spike_clusters,
983-
**{'sampleRate': 30000, 'binSizeCorr': 1 / 30000})
983+
sampleRate=30000, binSizeCorr=1 / 30000)
984984
r.slidingRP_viol[ir] = srp['value']
985985

986986
# loop over each cluster to compute the rest of the metrics

brainbox/plot_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ def scatter_xyc_plot(x, y, c, cmap=None, clim=None, rgb=False):
640640
data.set_clim(clim=clim)
641641
if rgb:
642642
norm = matplotlib.colors.Normalize(vmin=data.clim[0], vmax=data.clim[1], clip=True)
643-
mapper = cm.ScalarMappable(norm=norm, cmap=cm.get_cmap(cmap))
643+
mapper = cm.ScalarMappable(norm=norm, cmap=plt.get_cmap(cmap))
644644
cluster_color = np.array([mapper.to_rgba(col) for col in c])
645645
data.set_color(color=cluster_color)
646646

ibllib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33
import warnings
44

5-
__version__ = '2.35.1'
5+
__version__ = '2.35.2'
66
warnings.filterwarnings('always', category=DeprecationWarning, module='ibllib')
77

88
# if this becomes a full-blown library we should let the logging configuration to the discretion of the dev

ibllib/io/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Loaders for unprocessed IBL video and task data, and parameter files."""

ibllib/io/extractors/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""IBL rig data pre-processing functions.
2+
3+
Extractor classes for loading raw rig data and returning ALF compliant pre-processed data.
4+
"""

ibllib/io/extractors/training_trials.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
import numpy as np
3+
from itertools import accumulate
34
from packaging import version
45
from one.alf.io import AlfBunch
56

@@ -123,19 +124,16 @@ def _extract(self):
123124
def get_trial_repeat(trial):
124125
if 'debias_trial' in trial:
125126
return trial['debias_trial']
126-
else:
127+
elif 'contrast' in trial and isinstance(trial['contrast'], dict):
127128
return trial['contrast']['type'] == 'RepeatContrast'
129+
else:
130+
# For advanced choice world before version 8.19.0 there was no 'debias_trial' field
131+
# and no debiasing protocol applied, so simply return False
132+
assert self.settings['PYBPOD_PROTOCOL'].startswith('_iblrig_tasks_advancedChoiceWorld')
133+
return False
128134

129-
trial_repeated = np.array(list(map(get_trial_repeat, self.bpod_trials))).astype(int)
130-
repNum = trial_repeated.copy()
131-
c = 0
132-
for i in range(len(trial_repeated)):
133-
if trial_repeated[i] == 0:
134-
c = 0
135-
repNum[i] = 0
136-
continue
137-
c += 1
138-
repNum[i] = c
135+
trial_repeated = np.fromiter(map(get_trial_repeat, self.bpod_trials), int)
136+
repNum = np.fromiter(accumulate(trial_repeated, lambda x, y: x + y if y else 0), int)
139137
return repNum
140138

141139

@@ -163,7 +161,7 @@ class FeedbackTimes(BaseBpodTrialsExtractor):
163161
**Optional:** saves _ibl_trials.feedback_times.npy
164162
165163
Gets reward and error state init times vectors,
166-
checks if theintersection of nans is empty, then
164+
checks if the intersection of nans is empty, then
167165
merges the 2 vectors.
168166
"""
169167
save_names = '_ibl_trials.feedback_times.npy'

ibllib/io/extractors/training_wheel.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Extractors for the wheel position, velocity, and detected movement."""
12
import logging
23
from collections.abc import Sized
34

@@ -323,16 +324,35 @@ def extract_wheel_moves(re_ts, re_pos, display=False):
323324
def extract_first_movement_times(wheel_moves, trials, min_qt=None):
324325
"""
325326
Extracts the time of the first sufficiently large wheel movement for each trial.
327+
326328
To be counted, the movement must occur between go cue / stim on and before feedback /
327329
response time. The movement onset is sometimes just before the cue (occurring in the
328330
gap between quiescence end and cue start, or during the quiescence period but sub-
329-
threshold). The movement is sufficiently large if it is greater than or equal to THRESH
330-
:param wheel_moves: dictionary of detected wheel movement onsets and peak amplitudes for
331-
use in extracting each trial's time of first movement.
331+
threshold). The movement is sufficiently large if it is greater than or equal to THRESH.
332+
333+
:param wheel_moves:
332334
:param trials: dictionary of trial data
333-
:param min_qt: the minimum quiescence period, if None a default is used
334-
:return: numpy array of first movement times, bool array indicating whether movement
335-
crossed response threshold, and array of indices for wheel_moves arrays
335+
:param min_qt:
336+
:return: numpy array of
337+
338+
Parameters
339+
----------
340+
wheel_moves : dict
341+
Dictionary of detected wheel movement onsets and peak amplitudes for use in extracting each
342+
trial's time of first movement.
343+
trials : dict
344+
Dictionary of trial data.
345+
min_qt : float
346+
The minimum quiescence period in seconds, if None a default is used.
347+
348+
Returns
349+
-------
350+
numpy.array
351+
First movement times.
352+
numpy.array
353+
Bool array indicating whether movement crossed response threshold.
354+
numpy.array
355+
Indices for wheel_moves arrays.
336356
"""
337357
THRESH = .1 # peak amp should be at least .1 rad; ~1/3rd of the distance to threshold
338358
MIN_QT = .2 # default minimum enforced quiescence period
@@ -371,6 +391,8 @@ def extract_first_movement_times(wheel_moves, trials, min_qt=None):
371391

372392
class Wheel(BaseBpodTrialsExtractor):
373393
"""
394+
Wheel extractor.
395+
374396
Get wheel data from raw files and converts positions into radians mathematical convention
375397
(anti-clockwise = +) and timestamps into seconds relative to Bpod clock.
376398
**Optional:** saves _ibl_wheel.times.npy and _ibl_wheel.position.npy

ibllib/io/extractors/video_motion.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def find_nearest(array, value):
4242
class MotionAlignment:
4343
roi = {'left': ((800, 1020), (233, 1096)), 'right': ((426, 510), (104, 545)), 'body': ((402, 481), (31, 103))}
4444

45-
def __init__(self, eid=None, one=None, log=logging.getLogger(__name__), **kwargs):
45+
def __init__(self, eid=None, one=None, log=logging.getLogger(__name__), stream=False, **kwargs):
4646
self.one = one or ONE()
4747
self.eid = eid
4848
self.session_path = kwargs.pop('session_path', None) or self.one.eid2path(eid)
@@ -51,7 +51,10 @@ def __init__(self, eid=None, one=None, log=logging.getLogger(__name__), **kwargs
5151
self.trials = self.wheel = self.camera_times = None
5252
raw_cam_path = self.session_path.joinpath('raw_video_data')
5353
camera_path = list(raw_cam_path.glob('_iblrig_*Camera.raw.*'))
54-
self.video_paths = {vidio.label_from_path(x): x for x in camera_path}
54+
if stream:
55+
self.video_paths = vidio.url_from_eid(self.eid)
56+
else:
57+
self.video_paths = {vidio.label_from_path(x): x for x in camera_path}
5558
self.data = Bunch()
5659
self.alignment = Bunch()
5760

@@ -107,8 +110,8 @@ def load_data(self, download=False):
107110
if download:
108111
self.data.wheel = self.one.load_object(self.eid, 'wheel')
109112
self.data.trials = self.one.load_object(self.eid, 'trials')
110-
cam = self.one.load(self.eid, ['camera.times'], dclass_output=True)
111-
self.data.camera_times = {vidio.label_from_path(url): ts for ts, url in zip(cam.data, cam.url)}
113+
cam, det = self.one.load_datasets(self.eid, ['*Camera.times*'])
114+
self.data.camera_times = {vidio.label_from_path(d['rel_path']): ts for ts, d in zip(cam, det)}
112115
else:
113116
alf_path = self.session_path / 'alf'
114117
wheel_path = next(alf_path.rglob('*wheel.timestamps*')).parent
@@ -329,7 +332,7 @@ def animate(i):
329332
data['im'].set_data(frame)
330333

331334
mkr = find_nearest(wheel.timestamps[wheel_mask], t_x)
332-
data['marker'].set_data(wheel.timestamps[wheel_mask][mkr], wheel.position[wheel_mask][mkr])
335+
data['marker'].set_data([wheel.timestamps[wheel_mask][mkr]], [wheel.position[wheel_mask][mkr]])
333336

334337
return data['im'], data['ln'], data['marker']
335338

ibllib/oneibl/registration.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ class IBLRegistrationClient(RegistrationClient):
173173
Object that keeps the ONE instance and provides method to create sessions and register data.
174174
"""
175175

176-
def register_session(self, ses_path, file_list=True, projects=None, procedures=None):
176+
def register_session(self, ses_path, file_list=True, projects=None, procedures=None, register_reward=True):
177177
"""
178178
Register an IBL Bpod session in Alyx.
179179
@@ -188,11 +188,16 @@ def register_session(self, ses_path, file_list=True, projects=None, procedures=N
188188
The project(s) to which the experiment belongs (optional).
189189
procedures : str, list
190190
An optional list of procedures, e.g. 'Behavior training/tasks'.
191+
register_reward : bool
192+
If true, register all water administrations in the settings files, if no admins already
193+
present for this session.
191194
192195
Returns
193196
-------
194197
dict
195198
An Alyx session record.
199+
list of dict, None
200+
Alyx file records (or None if file_list is False).
196201
197202
Notes
198203
-----
@@ -321,7 +326,7 @@ def register_session(self, ses_path, file_list=True, projects=None, procedures=N
321326

322327
_logger.info(session['url'] + ' ')
323328
# create associated water administration if not found
324-
if not session['wateradmin_session_related'] and any(task_data):
329+
if register_reward and not session['wateradmin_session_related'] and any(task_data):
325330
for md, d in filter(all, zip(settings, task_data)):
326331
_, _end_time = _get_session_times(ses_path, md, d)
327332
user = md.get('PYBPOD_CREATOR')

ibllib/tests/extractors/test_extractors.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -207,21 +207,21 @@ def test_get_choice(self):
207207
self.assertTrue(all(choice[trial_nogo]) == 0)
208208

209209
def test_get_repNum(self):
210-
# TODO: Test its sawtooth
211210
# TRAINING SESSIONS
212211
rn = training_trials.RepNum(
213212
self.training_lt5['path']).extract()[0]
214213
self.assertTrue(isinstance(rn, np.ndarray))
215-
for i in range(3):
216-
self.assertTrue(i in rn)
214+
expected = [0, 1, 2, 0]
215+
np.testing.assert_array_equal(rn, expected)
217216
# -- version >= 5.0.0
218217
rn = training_trials.RepNum(
219218
self.training_ge5['path']).extract()[0]
220219
self.assertTrue(isinstance(rn, np.ndarray))
221-
for i in range(4):
222-
self.assertTrue(i in rn)
223220

224-
# BIASED SESSIONS have no repeted trials
221+
expected = [0, 0, 1, 2, 3, 0, 0, 0, 1, 2, 0, 1]
222+
np.testing.assert_array_equal(rn, expected)
223+
224+
# BIASED SESSIONS have no repeated trials
225225

226226
def test_get_rewardVolume(self):
227227
# TRAINING SESSIONS
@@ -237,14 +237,14 @@ def test_get_rewardVolume(self):
237237
rv = biased_trials.RewardVolume(
238238
self.biased_lt5['path']).extract()[0]
239239
self.assertTrue(isinstance(rv, np.ndarray))
240-
# Test if all non zero rewards are of the same value
241-
self.assertTrue(all([x == max(rv) for x in rv if x != 0]))
240+
# Test if all non-zero rewards are of the same value
241+
self.assertTrue(all(x == max(rv) for x in rv if x != 0))
242242
# -- version >= 5.0.0
243243
rv = biased_trials.RewardVolume(
244244
self.biased_ge5['path']).extract()[0]
245245
self.assertTrue(isinstance(rv, np.ndarray))
246-
# Test if all non zero rewards are of the same value
247-
self.assertTrue(all([x == max(rv) for x in rv if x != 0]))
246+
# Test if all non-zero rewards are of the same value
247+
self.assertTrue(all(x == max(rv) for x in rv if x != 0))
248248

249249
def test_get_feedback_times_ge5(self):
250250
# TRAINING SESSIONS

0 commit comments

Comments
 (0)