Skip to content

Commit 9c91903

Browse files
committed
Merge branch 'release/2.16'
2 parents 203ccff + 236ae29 commit 9c91903

File tree

8 files changed

+115
-32
lines changed

8 files changed

+115
-32
lines changed

brainbox/task/passive.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,14 @@ def get_stim_aligned_activity(stim_events, spike_times, spike_depths, z_score_fl
202202

203203
# Get rid of any nan values
204204
stim_times = stim_times[~np.isnan(stim_times)]
205-
stim_intervals = np.c_[stim_times - pre_stim, stim_times + post_stim]
206-
base_intervals = np.c_[stim_times - base_stim, stim_times - pre_stim]
207-
out_intervals = stim_intervals[:, 1] > times[-1]
205+
stim_intervals = stim_times - pre_stim
206+
base_intervals = stim_times - base_stim
207+
out_intervals = stim_intervals > times[-1]
208208

209209
idx_stim = np.searchsorted(times, stim_intervals, side='right')[np.invert(out_intervals)]
210210
idx_base = np.searchsorted(times, base_intervals, side='right')[np.invert(out_intervals)]
211+
idx_stim = np.c_[idx_stim, idx_stim + n_bins]
212+
idx_base = np.c_[idx_base, idx_base + n_bins_base]
211213

212214
stim_trials = np.zeros((depths.shape[0], n_bins, idx_stim.shape[0]))
213215
noise_trials = np.zeros((depths.shape[0], n_bins_base, idx_stim.shape[0]))

ibllib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Library implementing the International Brain Laboratory data pipeline."""
2-
__version__ = "2.15.3"
2+
__version__ = "2.16.0"
33
import warnings
44

55
from iblutil.util import get_logger

ibllib/atlas/flatmaps.py

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"""
44
from functools import lru_cache
55

6-
import pandas as pd
76
import numpy as np
87
from scipy.interpolate import interp1d
98
import matplotlib.pyplot as plt
@@ -152,6 +151,7 @@ def plot_swanson(acronyms=None, values=None, ax=None, hemisphere=None, br=None,
152151
"""
153152
mapping = 'Swanson'
154153
br = BrainRegions() if br is None else br
154+
br.compute_hierarchy()
155155
s2a = swanson()
156156
# both hemishpere
157157
if hemisphere == 'both':
@@ -169,25 +169,7 @@ def plot_swanson(acronyms=None, values=None, ax=None, hemisphere=None, br=None,
169169
im = br.rgba[regions]
170170
iswan = None
171171
else:
172-
user_aids = br.parse_acronyms_argument(acronyms)
173-
# if the user provided inputs are higher level than swanson propagate down
174-
swaids = br.id[np.unique(s2a)]
175-
maids = np.setdiff1d(user_aids, swaids) # those are the indices not in Swanson
176-
for i, maid in enumerate(maids):
177-
if maid <= 1:
178-
continue
179-
childs_in_sw = np.intersect1d(br.descendants(maid)['id'][1:], swaids)
180-
if childs_in_sw.size > 0:
181-
user_aids = np.r_[user_aids, childs_in_sw]
182-
values = np.r_[values, values[i] + childs_in_sw * 0]
183-
# the user may have input non-unique regions
184-
df = pd.DataFrame(dict(aid=user_aids, value=values)).groupby('aid').mean()
185-
aids, vals = (df.index.values, df['value'].values)
186-
# apply mapping and perform another round of aggregation
187-
_, _, ibr = np.intersect1d(aids, br.id, return_indices=True)
188-
ibr = br.mappings['Swanson-lr'][ibr]
189-
df = pd.DataFrame(dict(ibr=ibr, value=vals)).groupby('ibr').mean()
190-
ibr, vals = (df.index.values, df['value'].values)
172+
ibr, vals = br.propagate_down(acronyms, values)
191173
# we now have the mapped regions and aggregated values, map values onto swanson map
192174
iswan, iv = ismember(s2a, ibr)
193175
im = np.zeros_like(s2a, dtype=np.float32)
@@ -211,8 +193,9 @@ def plot_swanson(acronyms=None, values=None, ax=None, hemisphere=None, br=None,
211193

212194
# provides the mean to see the region on axis
213195
def format_coord(x, y):
214-
acronym = br.acronym[s2a[int(y), int(x)]]
215-
return f'x={x:1.4f}, y={x:1.4f}, {acronym}'
196+
ind = s2a[int(y), int(x)]
197+
ancestors = br.ancestors(br.id[ind])['acronym']
198+
return f'x={x:1.4f}, y={x:1.4f}, {br.acronym[ind]} \n {ancestors}'
216199

217200
ax.format_coord = format_coord
218201
return ax

ibllib/atlas/regions.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,26 @@ def leaves(self):
123123
leaves = np.setxor1d(self.id, self.parent)
124124
return self.get(np.int64(leaves[~np.isnan(leaves)]))
125125

126+
def propagate_down(self, acronyms, values):
127+
"""
128+
This function remaps a set of user specified acronyms and values to the
129+
swanson map, by filling down the child nodes when higher up values are
130+
provided.
131+
:param acronyms: list or array of allen ids or acronyms
132+
:param values: list or array of associated values
133+
:return:
134+
"""
135+
user_aids = self.parse_acronyms_argument(acronyms)
136+
_, user_indices = ismember(user_aids, self.id)
137+
self.compute_hierarchy()
138+
ia, ib = ismember(self.hierarchy, user_indices)
139+
v = np.zeros_like(ia, dtype=np.float64) * np.NaN
140+
v[ia] = values[ib]
141+
all_values = np.nanmedian(v, axis=0)
142+
indices = np.where(np.any(ia, axis=0))[0]
143+
all_values = all_values[indices]
144+
return indices, all_values
145+
126146
def _mapping_from_regions_list(self, new_map, lateralize=False):
127147
"""
128148
From a vector of regions id, creates a mapping such as
@@ -429,7 +449,7 @@ def _compute_mappings(self):
429449
"""
430450
Recomputes the mapping indices for all mappings
431451
This is left mainly as a reference for adding future mappings as this take a few seconds
432-
to execute. In production,we use the MAPPING_FILES pqt to avoid recompuing at each \
452+
to execute. In production,we use the MAPPING_FILES pqt to avoid recomputing at each \
433453
instantiation
434454
"""
435455
beryl = np.load(Path(__file__).parent.joinpath('beryl.npy'))
@@ -447,6 +467,33 @@ def _compute_mappings(self):
447467
}
448468
pd.DataFrame(self.mappings).to_parquet(FILE_MAPPINGS)
449469

470+
def compute_hierarchy(self):
471+
"""
472+
Creates a self.hierarchy attributes that is a n_levels by n_region array
473+
of indices. This is useful to perform fast vectorized computations of
474+
ancestors and descendants.
475+
:return:
476+
"""
477+
if hasattr(self, 'hierarchy'):
478+
return
479+
n_levels = np.max(self.level)
480+
n_regions = self.id.size
481+
# creates the parent index. Void and root are omitted from intersection
482+
# as they figure as NaN
483+
pmask, i_p = ismember(self.parent, self.id)
484+
self.iparent = np.arange(n_regions)
485+
self.iparent[pmask] = i_p
486+
# the last level of the hierarchy is the actual mapping, then going up level per level
487+
# we assign the parend index
488+
self.hierarchy = np.tile(np.arange(n_regions), (n_levels, 1))
489+
_mask = np.zeros(n_regions, bool)
490+
for lev in np.flipud(np.arange(n_levels)):
491+
if lev < (n_levels - 1):
492+
self.hierarchy[lev, _mask] = self.iparent[self.hierarchy[lev + 1, _mask]]
493+
sel = self.level == (lev + 1)
494+
self.hierarchy[lev, sel] = np.where(sel)[0]
495+
_mask[sel] = True
496+
450497
def remap(self, region_ids, source_map='Allen', target_map='Beryl'):
451498
"""
452499
Remap atlas regions ids from source map to target map

ibllib/oneibl/patcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def register_dataset(self, file_list, **kwargs):
111111
:param dry: (bool) False by default
112112
:return:
113113
"""
114-
return register_dataset(file_list, one=self.one, server_only=True, **kwargs)
114+
return register_dataset(file_list, one=self.one, server_only=True, exists=True, **kwargs)
115115

116116
def register_datasets(self, file_list, **kwargs):
117117
"""

ibllib/oneibl/registration.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def _check_filename_for_registration(full_file, patterns):
4646

4747

4848
def register_dataset(file_list, one=None, created_by=None, repository=None, server_only=False,
49-
versions=None, default=True, dry=False, max_md5_size=None):
49+
versions=None, default=True, dry=False, max_md5_size=None, exists=False):
5050
"""
5151
Registers a set of files belonging to a session only on the server
5252
:param file_list: (list of pathlib.Path or pathlib.Path)
@@ -99,7 +99,8 @@ def register_dataset(file_list, one=None, created_by=None, repository=None, serv
9999
'hashes': hashes,
100100
'filesizes': [p.stat().st_size for p in file_list],
101101
'versions': versions,
102-
'default': default}
102+
'default': default,
103+
'exists': exists}
103104
if not dry:
104105
if one is None:
105106
one = ONE(cache_rest=None)
@@ -142,7 +143,9 @@ def register_session_raw_data(session_path, one=None, overwrite=False, dry=False
142143
# filter 2/2 unless overwrite is True, filter out the datasets that already exist
143144
if not overwrite:
144145
files_2_register = list(filter(lambda f: f not in already_registered, files_2_register))
145-
response = register_dataset(files_2_register, one=one, versions=None, dry=dry, **kwargs)
146+
147+
data_repo = get_local_data_repository(one)
148+
response = register_dataset(files_2_register, one=one, versions=None, dry=dry, repository=data_repo, **kwargs)
146149
return files_2_register, response
147150

148151

@@ -493,3 +496,23 @@ def _glob_session(ses_path):
493496
for gp in REGISTRATION_GLOB_PATTERNS:
494497
fl.extend(list(ses_path.glob(gp)))
495498
return fl
499+
500+
501+
def get_local_data_repository(one):
502+
"""
503+
Get local data repo name from globus client
504+
:param one:
505+
:return:
506+
"""
507+
if one is None:
508+
return
509+
510+
if not Path.home().joinpath(".globusonline/lta/client-id.txt").exists():
511+
return
512+
513+
with open(Path.home().joinpath(".globusonline/lta/client-id.txt"), 'r') as fid:
514+
globus_id = fid.read()
515+
516+
data_repo = one.alyx.rest('data-repository', 'list', globus_endpoint_id=globus_id)
517+
if len(data_repo):
518+
return [da['name'] for da in data_repo][0]

ibllib/tests/test_atlas.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,26 @@ def test_argument_parser(self):
284284
self.brs.parse_acronyms_argument(acronyms + ['toto'])
285285
assert np.all(self.brs.parse_acronyms_argument(acronyms + ['toto'], mode='clip') == ids)
286286

287+
def test_compute_hierarchy(self):
288+
self.brs.compute_hierarchy()
289+
np.testing.assert_equal(
290+
self.brs.hierarchy[:, 349], np.array([2, 3, 4, 5, 6, 340, 347, 349, 349, 349]))
291+
292+
def test_propagate_down(self):
293+
acronyms = ['CB', 'ANcr2']
294+
ind0 = ismember(self.brs.acronym2id(acronyms), self.brs.id)[1]
295+
ind1 = [7, 45, 66]
296+
for ind in [ind0, ind1]:
297+
with self.subTest(indices=ind):
298+
acronyms = self.brs.acronym[ind]
299+
values = np.arange(len(acronyms))
300+
ibr, ivals = self.brs.propagate_down(acronyms, values)
301+
idesc = []
302+
for c, i in enumerate(ind):
303+
ii = ismember(self.brs.descendants(self.brs.id[i])['id'], self.brs.id)[1]
304+
idesc.append(ii)
305+
np.testing.assert_equal(ibr, np.unique(np.concatenate(idesc)))
306+
287307

288308
class TestAtlasPlots(unittest.TestCase):
289309

release_notes.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1+
## Release 2.16
2+
### Release Notes 2.16.0 2022-09-27
3+
### features
4+
- swanson flatmap: the algorithm to propagate down the hierarchy has been refined
5+
6+
### bugfixes
7+
- set exists flag to false for all data repos when registering datasets with tasks
8+
19
## Release Notes 2.15
210
### Release Notes 2.15.3 - 2022-09-26
311
- SessionLoader error handling and bug fix
412

513
### Release Notes 2.15.2 - 2022-09-22
614
- extraction pipeline: fix unpacking of empty arguments field from alyx dict that prevents running task
715

8-
### Release Notes 2.15.1
16+
### Release Notes 2.15.1 - 2022-09-21
917
- atlas: gene-expression backend and MRI Toronto atlas stretch and squeeze factors (Dan/Olivier)
1018
- FDR correction (Benjamin-Hochmann) to correct for multiple testing optional (Guido)
1119
- SpikeSortingLoader can be used with ONE local mode (Julia)

0 commit comments

Comments
 (0)