Skip to content

Commit 5ee27b6

Browse files
committed
Merge branch 'develop' into one_globus
2 parents 76fda5b + 483c0c5 commit 5ee27b6

File tree

107 files changed

+5846
-5201
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+5846
-5201
lines changed

.github/workflows/ibllib_ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ jobs:
1717
fail-fast: false # Whether to stop execution of other instances
1818
max-parallel: 4
1919
matrix:
20-
os: ["ubuntu-latest"] # "windows-latest"] # , "macos-latest"
21-
python-version: ["3.8"] # "3.7",
20+
os: ["ubuntu-latest"]
21+
python-version: ["3.8"]
2222
steps:
2323
- uses: actions/checkout@v2
2424
- name: Set up Python ${{ matrix.python-version }}

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
include ibllib/atlas/allen_structure_tree.csv
22
include ibllib/atlas/beryl.npy
33
include ibllib/atlas/cosmos.npy
4+
include ibllib/atlas/swanson.npy
45
include ibllib/atlas/mappings.pqt
56
include ibllib/io/extractors/extractor_types.json
67
include brainbox/tests/wheel_test.p

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ The library is currently 2 main modules:
1212
[Release Notes here](release_notes.md)
1313

1414
## Requirements
15-
**OS**: Deployed on Linux and Windows. Minimally tested for Mac.
15+
**OS**: Only tested on Linux. Windows and Mac may work, but are not supported.
1616

17-
**Python Module**: Python 3.7 or higher, we develop on 3.8.
17+
**Python Module**: Python 3.8 or higher
1818

1919
## Installation, documentation and examples
2020
https://docs.internationalbrainlab.org
@@ -25,7 +25,7 @@ See https://int-brain-lab.github.io/iblenv/07_contribution.html
2525

2626
We use gitflow and Semantic Versioning.
2727

28-
Before commiting to your branch:
28+
Before committing to your branch:
2929
- run tests
3030
- flake8
3131
This is also enforced by continuous integration.

brainbox/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1+
import logging
12
try:
23
import one
34
except ModuleNotFoundError:
4-
logging.getLogger('ibllib').error('Missing dependency, please run `pip install ONE-api`')
5+
logging.getLogger(__name__).error('Missing dependency, please run `pip install ONE-api`')

brainbox/behavior/dlc.py

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import scipy.interpolate as interpolate
1212
from scipy.stats import zscore
1313

14-
from ibllib.dsp.smooth import smooth_interpolate_savgol
14+
from neurodsp.smooth import smooth_interpolate_savgol
1515
from brainbox.processing import bincount2D
1616
import brainbox.behavior.wheel as bbox_wheel
1717

@@ -350,11 +350,12 @@ def plot_wheel_position(wheel_position, wheel_time, trials_df):
350350

351351
plt.axvline(x=0, linestyle='--', c='k', label='stimOn')
352352
plt.axhline(y=-0.26, linestyle='--', c='g', label='reward')
353+
plt.axhline(y=0.26, linestyle='--', c='g', label='reward')
353354
plt.ylim([-0.27, 0.27])
354355
plt.xlabel('time [sec]')
355-
plt.ylabel('wheel position [rad]')
356+
plt.ylabel('wheel position diff to first value [rad]')
356357
plt.legend(loc='center right')
357-
plt.title('Wheel position')
358+
plt.title('Wheel position trial avg\n(and individual trials)')
358359
plt.tight_layout()
359360

360361
return plt.gca()
@@ -406,7 +407,7 @@ def plot_lick_hist(lick_times, trials_df):
406407
plt.plot(times, pd.DataFrame.from_dict(dict(zip(correct.index, incorrect.values))).mean(axis=1),
407408
c='gray', label='incorrect trial')
408409
plt.axvline(x=0, label='feedback', linestyle='--', c='purple')
409-
plt.title('Lick events')
410+
plt.title('Lick events trial avg')
410411
plt.xlabel('time [sec]')
411412
plt.ylabel('lick events [a.u.]')
412413
plt.legend(loc='lower right')
@@ -457,16 +458,17 @@ def plot_motion_energy_hist(camera_dict, trials_df):
457458
motion_energy = zscore(camera_dict[cam]['motion_energy'], nan_policy='omit')
458459
try:
459460
start_idx = insert_idx(camera_dict[cam]['times'], start_window)
461+
end_idx = np.array(start_idx + int(WINDOW_LEN * SAMPLING[cam]), dtype='int64')
462+
me_all = [motion_energy[start_idx[i]:end_idx[i]] for i in range(len(start_idx))]
463+
me_all = [m for m in me_all if len(m) > 0]
464+
times = np.arange(len(me_all[0])) / SAMPLING[cam] + WINDOW_LAG
465+
me_mean = np.mean(me_all, axis=0)
466+
me_std = np.std(me_all, axis=0) / np.sqrt(len(me_all))
467+
plt.plot(times, me_mean, label=f'{cam} cam', color=colors[cam], linewidth=2)
468+
plt.fill_between(times, me_mean + me_std, me_mean - me_std, color=colors[cam], alpha=0.2)
460469
except ValueError:
461-
logger.error("Camera.times are outside of the trial windows")
462-
raise
463-
end_idx = np.array(start_idx + int(WINDOW_LEN * SAMPLING[cam]), dtype='int64')
464-
me_all = [motion_energy[start_idx[i]:end_idx[i]] for i in range(len(start_idx))]
465-
times = np.arange(len(me_all[0])) / SAMPLING[cam] + WINDOW_LAG
466-
me_mean = np.mean(me_all, axis=0)
467-
me_std = np.std(me_all, axis=0) / np.sqrt(len(me_all))
468-
plt.plot(times, me_mean, label=f'{cam} cam', color=colors[cam], linewidth=2)
469-
plt.fill_between(times, me_mean + me_std, me_mean - me_std, color=colors[cam], alpha=0.2)
470+
logger.error(f"{cam}Camera camera.times are outside of the trial windows")
471+
missing_data.append(cam)
470472
except AttributeError:
471473
logger.warning(f"Cannot load motion energy and/or times data for {cam} camera")
472474
missing_data.append(cam)
@@ -479,11 +481,11 @@ def plot_motion_energy_hist(camera_dict, trials_df):
479481
plt.xlabel('time [sec]')
480482
plt.axvline(x=0, label='stimOn', linestyle='--', c='k')
481483
plt.legend(loc='lower right')
482-
plt.title('Motion Energy')
484+
plt.title('Motion Energy trial avg\n(+/- std)')
483485
if len(missing_data) > 0:
484486
ax = plt.gca()
485487
ax.text(.95, .35, f"Data incomplete for\n{' and '.join(missing_data)} camera", color='r', fontsize=10,
486-
horizontalalignment='right', verticalalignment='center', transform=ax.transAxes)
488+
fontweight='bold', horizontalalignment='right', verticalalignment='center', transform=ax.transAxes)
487489
return plt.gca()
488490

489491

@@ -523,7 +525,7 @@ def plot_speed_hist(dlc_df, cam_times, trials_df, feature='paw_r', cam='left', l
523525
plt.plot(times, pd.DataFrame.from_dict(dict(zip(incorrect.index, incorrect.values))).mean(axis=1),
524526
c='gray', label='incorrect trial')
525527
plt.axvline(x=0, label='stimOn', linestyle='--', c='r')
526-
plt.title(f'{feature.split("_")[0].capitalize()} speed ({cam} cam)')
528+
plt.title(f'{feature.capitalize()} speed trial avg\n({cam.upper()} cam)')
527529
plt.xticks([-0.5, 0, 0.5, 1, 1.5])
528530
plt.xlabel('time [sec]')
529531
plt.ylabel('speed [px/sec]')
@@ -559,8 +561,8 @@ def plot_pupil_diameter_hist(pupil_diameter, cam_times, trials_df, cam='left'):
559561
plt.plot(times, pupil_mean, label=align_to.split("_")[0], color=color)
560562
plt.fill_between(times, pupil_mean + pupil_std, pupil_mean - pupil_std, color=color, alpha=0.5)
561563
plt.axvline(x=0, linestyle='--', c='k')
562-
plt.title(f'Pupil diameter ({cam} cam)')
564+
plt.title(f'Pupil diameter trial avg\n({cam.upper()} cam)')
563565
plt.xlabel('time [sec]')
564566
plt.xticks([-0.5, 0, 0.5, 1, 1.5])
565-
plt.ylabel('pupil diameter [px]')
567+
plt.ylabel('z-scored smoothed pupil diameter [px]')
566568
plt.legend(loc='lower right', title='aligned to')

brainbox/behavior/training.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,14 @@ def get_sessions(subj, date=None, one=None):
111111
latest_sess = date
112112

113113
sessions = one.alyx.rest('sessions', 'list', subject=subj, date_range=[latest_minus_week,
114-
latest_sess], dataset_types='trials.intervals')
114+
latest_sess], dataset_types='trials.goCueTrigger_times')
115115

116116
# If not enough sessions in the last week, then just fetch them all
117117
if len(sessions) < 3:
118118
specified_date_plus = (specified_date + datetime.timedelta(days=1)).strftime("%Y-%m-%d")
119119
django_query = 'start_time__lte,' + specified_date_plus
120120
sessions = one.alyx.rest('sessions', 'list', subject=subj,
121-
dataset_types='trials.intervals', django=django_query)
121+
dataset_types='trials.goCueTrigger_times', django=django_query)
122122

123123
# If still 0 sessions then return with warning
124124
if len(sessions) == 0:
@@ -349,7 +349,7 @@ def compute_training_info(trials, trials_all):
349349
perf_easy = np.array([compute_performance_easy(trials[k]) for k in trials.keys()])
350350
n_trials = np.array([compute_n_trials(trials[k]) for k in trials.keys()])
351351
psych = compute_psychometric(trials_all, signed_contrast=signed_contrast)
352-
rt = compute_median_reaction_time(trials_all, signed_contrast=signed_contrast)
352+
rt = compute_median_reaction_time(trials_all, contrast=0, signed_contrast=signed_contrast)
353353

354354
return perf_easy, n_trials, psych, rt
355355

@@ -376,7 +376,7 @@ def compute_bias_info(trials, trials_all):
376376
n_trials = np.array([compute_n_trials(trials[k]) for k in trials.keys()])
377377
psych_20 = compute_psychometric(trials_all, signed_contrast=signed_contrast, block=0.2)
378378
psych_80 = compute_psychometric(trials_all, signed_contrast=signed_contrast, block=0.8)
379-
rt = compute_median_reaction_time(trials_all, signed_contrast=signed_contrast)
379+
rt = compute_median_reaction_time(trials_all, contrast=0, signed_contrast=signed_contrast)
380380

381381
return perf_easy, n_trials, psych_20, psych_80, rt
382382

@@ -452,7 +452,7 @@ def compute_n_trials(trials):
452452
return trials['choice'].shape[0]
453453

454454

455-
def compute_psychometric(trials, signed_contrast=None, block=None):
455+
def compute_psychometric(trials, signed_contrast=None, block=None, plotting=False):
456456
"""
457457
Compute psychometric fit parameters for trials object
458458
@@ -479,17 +479,27 @@ def compute_psychometric(trials, signed_contrast=None, block=None):
479479
prob_choose_right, contrasts, n_contrasts = compute_performance(trials, signed_contrast=signed_contrast, block=block,
480480
prob_right=True)
481481

482-
psych, _ = psy.mle_fit_psycho(
483-
np.vstack([contrasts, n_contrasts, prob_choose_right]),
484-
P_model='erf_psycho_2gammas',
485-
parstart=np.array([np.mean(contrasts), 20., 0.05, 0.05]),
486-
parmin=np.array([np.min(contrasts), 0., 0., 0.]),
487-
parmax=np.array([np.max(contrasts), 100., 1, 1]))
482+
if plotting:
483+
psych, _ = psy.mle_fit_psycho(
484+
np.vstack([contrasts, n_contrasts, prob_choose_right]),
485+
P_model='erf_psycho_2gammas',
486+
parstart=np.array([0., 40., 0.1, 0.1]),
487+
parmin=np.array([-50., 10., 0., 0.]),
488+
parmax=np.array([50., 50., 0.2, 0.2]),
489+
nfits=10)
490+
else:
491+
492+
psych, _ = psy.mle_fit_psycho(
493+
np.vstack([contrasts, n_contrasts, prob_choose_right]),
494+
P_model='erf_psycho_2gammas',
495+
parstart=np.array([np.mean(contrasts), 20., 0.05, 0.05]),
496+
parmin=np.array([np.min(contrasts), 0., 0., 0.]),
497+
parmax=np.array([np.max(contrasts), 100., 1, 1]))
488498

489499
return psych
490500

491501

492-
def compute_median_reaction_time(trials, stim_on_type='stimOn_times', signed_contrast=None):
502+
def compute_median_reaction_time(trials, stim_on_type='stimOn_times', contrast=None, signed_contrast=None):
493503
"""
494504
Compute median reaction time on zero contrast trials from trials object
495505
@@ -505,10 +515,15 @@ def compute_median_reaction_time(trials, stim_on_type='stimOn_times', signed_con
505515
"""
506516
if signed_contrast is None:
507517
signed_contrast = get_signed_contrast(trials)
508-
zero_trials = (trials.response_times - trials[stim_on_type])[signed_contrast == 0]
509-
if np.any(zero_trials):
518+
519+
if contrast is None:
520+
contrast_idx = np.full(trials.probabilityLeft.shape, True, dtype=bool)
521+
else:
522+
contrast_idx = signed_contrast == contrast
523+
524+
if np.any(contrast_idx):
510525
reaction_time = np.nanmedian((trials.response_times - trials[stim_on_type])
511-
[signed_contrast == 0])
526+
[contrast_idx])
512527
else:
513528
reaction_time = np.nan
514529

@@ -590,15 +605,15 @@ def plot_psychometric(trials, ax=None, title=None, **kwargs):
590605
contrasts_fit = np.arange(-100, 100)
591606

592607
prob_right_50, contrasts_50, _ = compute_performance(trials, signed_contrast=signed_contrast, block=0.5, prob_right=True)
593-
pars_50 = compute_psychometric(trials, signed_contrast=signed_contrast, block=0.5)
608+
pars_50 = compute_psychometric(trials, signed_contrast=signed_contrast, block=0.5, plotting=True)
594609
prob_right_fit_50 = psy.erf_psycho_2gammas(pars_50, contrasts_fit)
595610

596611
prob_right_20, contrasts_20, _ = compute_performance(trials, signed_contrast=signed_contrast, block=0.2, prob_right=True)
597-
pars_20 = compute_psychometric(trials, signed_contrast=signed_contrast, block=0.2)
612+
pars_20 = compute_psychometric(trials, signed_contrast=signed_contrast, block=0.2, plotting=True)
598613
prob_right_fit_20 = psy.erf_psycho_2gammas(pars_20, contrasts_fit)
599614

600615
prob_right_80, contrasts_80, _ = compute_performance(trials, signed_contrast=signed_contrast, block=0.8, prob_right=True)
601-
pars_80 = compute_psychometric(trials, signed_contrast=signed_contrast, block=0.8)
616+
pars_80 = compute_psychometric(trials, signed_contrast=signed_contrast, block=0.8, plotting=True)
602617
prob_right_fit_80 = psy.erf_psycho_2gammas(pars_80, contrasts_fit)
603618

604619
cmap = sns.diverging_palette(20, 220, n=3, center="dark")

brainbox/behavior/wheel.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,8 @@ def movements(t, pos, freq=1000, pos_thresh=8, t_thresh=.2, min_gap=.1, pos_thre
213213
if i2proc[-1] == t.size - 1:
214214
break
215215

216-
moving = max_disp > pos_thresh # for each window is the change in position greater than
217-
# our threshold?
218-
moving = np.insert(moving, 0, False) # First sample should always be not moving to ensure
219-
# we have an onset
216+
moving = max_disp > pos_thresh # for each window is the change in position greater than our threshold?
217+
moving = np.insert(moving, 0, False) # First sample should always be not moving to ensure we have an onset
220218
moving[-1] = False # Likewise, ensure we always end on an offset
221219

222220
onset_samps = np.where(~moving[:-1] & moving[1:])[0]

brainbox/ephys_plots.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,3 +496,30 @@ def histc(x, bins):
496496
return data.convert2dict(), fig, ax
497497

498498
return data
499+
500+
501+
def image_raw_data(raw, fs, chn_coords=None, cmap='bone', title=None, display=False, gain=-90, **kwargs):
502+
503+
def gain2level(gain):
504+
return 10 ** (gain / 20) * 4 * np.array([-1, 1])
505+
506+
ylabel = 'Channel index' if chn_coords is None else 'Distance from probe tip (um)'
507+
title = title or 'Raw data'
508+
509+
y = np.arange(raw.shape[1]) if chn_coords is None else chn_coords[:, 1]
510+
511+
x = np.array([0, raw.shape[0] - 1]) / fs * 1e3
512+
513+
data = ImagePlot(raw, y=y, cmap=cmap)
514+
data.set_labels(title=title, xlabel='Time (ms)',
515+
ylabel=ylabel, clabel='Power (uV)')
516+
clim = gain2level(gain)
517+
data.set_clim(clim=clim)
518+
data.set_xlim(xlim=x)
519+
data.set_ylim()
520+
521+
if display:
522+
ax, fig = plot_image(data.convert2dict(), **kwargs)
523+
return data.convert2dict(), fig, ax
524+
525+
return data

0 commit comments

Comments
 (0)