siibra 1.0a1__1-py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of siibra might be problematic. Click here for more details.
- siibra/VERSION +1 -0
- siibra/__init__.py +164 -0
- siibra/commons.py +823 -0
- siibra/configuration/__init__.py +17 -0
- siibra/configuration/configuration.py +189 -0
- siibra/configuration/factory.py +589 -0
- siibra/core/__init__.py +16 -0
- siibra/core/assignment.py +110 -0
- siibra/core/atlas.py +239 -0
- siibra/core/concept.py +308 -0
- siibra/core/parcellation.py +387 -0
- siibra/core/region.py +1223 -0
- siibra/core/space.py +131 -0
- siibra/core/structure.py +111 -0
- siibra/exceptions.py +63 -0
- siibra/experimental/__init__.py +19 -0
- siibra/experimental/contour.py +61 -0
- siibra/experimental/cortical_profile_sampler.py +57 -0
- siibra/experimental/patch.py +98 -0
- siibra/experimental/plane3d.py +256 -0
- siibra/explorer/__init__.py +17 -0
- siibra/explorer/url.py +222 -0
- siibra/explorer/util.py +87 -0
- siibra/features/__init__.py +117 -0
- siibra/features/anchor.py +224 -0
- siibra/features/connectivity/__init__.py +33 -0
- siibra/features/connectivity/functional_connectivity.py +57 -0
- siibra/features/connectivity/regional_connectivity.py +494 -0
- siibra/features/connectivity/streamline_counts.py +27 -0
- siibra/features/connectivity/streamline_lengths.py +27 -0
- siibra/features/connectivity/tracing_connectivity.py +30 -0
- siibra/features/dataset/__init__.py +17 -0
- siibra/features/dataset/ebrains.py +90 -0
- siibra/features/feature.py +970 -0
- siibra/features/image/__init__.py +27 -0
- siibra/features/image/image.py +115 -0
- siibra/features/image/sections.py +26 -0
- siibra/features/image/volume_of_interest.py +88 -0
- siibra/features/tabular/__init__.py +24 -0
- siibra/features/tabular/bigbrain_intensity_profile.py +77 -0
- siibra/features/tabular/cell_density_profile.py +298 -0
- siibra/features/tabular/cortical_profile.py +322 -0
- siibra/features/tabular/gene_expression.py +257 -0
- siibra/features/tabular/layerwise_bigbrain_intensities.py +62 -0
- siibra/features/tabular/layerwise_cell_density.py +95 -0
- siibra/features/tabular/receptor_density_fingerprint.py +192 -0
- siibra/features/tabular/receptor_density_profile.py +110 -0
- siibra/features/tabular/regional_timeseries_activity.py +294 -0
- siibra/features/tabular/tabular.py +139 -0
- siibra/livequeries/__init__.py +19 -0
- siibra/livequeries/allen.py +352 -0
- siibra/livequeries/bigbrain.py +197 -0
- siibra/livequeries/ebrains.py +145 -0
- siibra/livequeries/query.py +49 -0
- siibra/locations/__init__.py +91 -0
- siibra/locations/boundingbox.py +454 -0
- siibra/locations/location.py +115 -0
- siibra/locations/point.py +344 -0
- siibra/locations/pointcloud.py +349 -0
- siibra/retrieval/__init__.py +27 -0
- siibra/retrieval/cache.py +233 -0
- siibra/retrieval/datasets.py +389 -0
- siibra/retrieval/exceptions/__init__.py +27 -0
- siibra/retrieval/repositories.py +769 -0
- siibra/retrieval/requests.py +659 -0
- siibra/vocabularies/__init__.py +45 -0
- siibra/vocabularies/gene_names.json +29176 -0
- siibra/vocabularies/receptor_symbols.json +210 -0
- siibra/vocabularies/region_aliases.json +460 -0
- siibra/volumes/__init__.py +23 -0
- siibra/volumes/parcellationmap.py +1279 -0
- siibra/volumes/providers/__init__.py +20 -0
- siibra/volumes/providers/freesurfer.py +113 -0
- siibra/volumes/providers/gifti.py +165 -0
- siibra/volumes/providers/neuroglancer.py +736 -0
- siibra/volumes/providers/nifti.py +266 -0
- siibra/volumes/providers/provider.py +107 -0
- siibra/volumes/sparsemap.py +468 -0
- siibra/volumes/volume.py +892 -0
- siibra-1.0.0a1.dist-info/LICENSE +201 -0
- siibra-1.0.0a1.dist-info/METADATA +160 -0
- siibra-1.0.0a1.dist-info/RECORD +84 -0
- siibra-1.0.0a1.dist-info/WHEEL +5 -0
- siibra-1.0.0a1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Copyright 2018-2024
|
|
2
|
+
# Institute of Neuroscience and Medicine (INM-1), Forschungszentrum Jülich GmbH
|
|
3
|
+
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""Hierarchal brain regions and metadata."""
|
|
16
|
+
from . import region
|
|
17
|
+
|
|
18
|
+
from ..commons import logger, MapType, Species
|
|
19
|
+
from ..volumes import parcellationmap
|
|
20
|
+
from ..exceptions import NoMapMatchingValues
|
|
21
|
+
|
|
22
|
+
from functools import lru_cache
|
|
23
|
+
import re
|
|
24
|
+
from typing import Union, List, TYPE_CHECKING
|
|
25
|
+
try:
|
|
26
|
+
from typing import Literal
|
|
27
|
+
except ImportError:
|
|
28
|
+
# support python 3.7
|
|
29
|
+
from typing_extensions import Literal
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from .space import Space
|
|
34
|
+
|
|
35
|
+
# NOTE : such code could be used to automatically resolve
|
|
36
|
+
# multiple matching parcellations for a short spec to the newset version:
|
|
37
|
+
# try:
|
|
38
|
+
# collections = {m.version.collection for m in matches}
|
|
39
|
+
# if len(collections)==1:
|
|
40
|
+
# return sorted(matches,key=lambda m:m.version,reverse=True)[0]
|
|
41
|
+
# except Exception as e:
|
|
42
|
+
# pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ParcellationVersion:
|
|
46
|
+
def __init__(
|
|
47
|
+
self, name, parcellation, collection=None, prev_id=None, next_id=None, deprecated=False
|
|
48
|
+
):
|
|
49
|
+
self.name = name
|
|
50
|
+
self.collection = collection
|
|
51
|
+
self.parcellation = parcellation
|
|
52
|
+
self.next_id = next_id
|
|
53
|
+
self.prev_id = prev_id
|
|
54
|
+
self.deprecated = deprecated
|
|
55
|
+
|
|
56
|
+
def __eq__(self, other):
|
|
57
|
+
return all([self.name == other.name, self.collection == other.collection])
|
|
58
|
+
|
|
59
|
+
def __str__(self):
|
|
60
|
+
return self.name
|
|
61
|
+
|
|
62
|
+
def __repr__(self):
|
|
63
|
+
return self.name
|
|
64
|
+
|
|
65
|
+
def __iter__(self):
|
|
66
|
+
yield "name", self.name
|
|
67
|
+
yield "prev", self.prev_id
|
|
68
|
+
yield "next", self.next_id
|
|
69
|
+
yield "deprecated", self.deprecated
|
|
70
|
+
|
|
71
|
+
def __lt__(self, other: 'ParcellationVersion'):
|
|
72
|
+
"""
|
|
73
|
+
< operator, useful for sorting by version
|
|
74
|
+
FIXME: this is only by name, not recursing into parcellations, to avoid importing the registry here.
|
|
75
|
+
"""
|
|
76
|
+
return self.name < other.name
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class Parcellation(region.Region, configuration_folder="parcellations"):
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
identifier: str,
|
|
84
|
+
name: str,
|
|
85
|
+
species: Union[Species, str],
|
|
86
|
+
regions: Union[List[region.Region], region.Region] = (),
|
|
87
|
+
shortname: str = "",
|
|
88
|
+
description: str = "",
|
|
89
|
+
version: ParcellationVersion = None,
|
|
90
|
+
modality: str = None,
|
|
91
|
+
publications: list = [],
|
|
92
|
+
datasets: list = [],
|
|
93
|
+
prerelease: bool = False,
|
|
94
|
+
):
|
|
95
|
+
"""
|
|
96
|
+
Constructs a new parcellation object.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
identifier : str
|
|
101
|
+
Unique identifier of the parcellation
|
|
102
|
+
name : str
|
|
103
|
+
Human-readable name of the parcellation
|
|
104
|
+
species: str or Species
|
|
105
|
+
Specification of the species
|
|
106
|
+
regions: list or Region
|
|
107
|
+
shortname: str
|
|
108
|
+
Short form of human-readable name (optional)
|
|
109
|
+
description: str
|
|
110
|
+
Textual description of the parcellation
|
|
111
|
+
version : str or None
|
|
112
|
+
Version specification, optional
|
|
113
|
+
modality : str or None
|
|
114
|
+
Specification of the modality used for creating the parcellation
|
|
115
|
+
publications: list
|
|
116
|
+
List of associated publications, each a dictionary with "doi"
|
|
117
|
+
and/or "citation" fields
|
|
118
|
+
datasets : list
|
|
119
|
+
datasets associated with this region
|
|
120
|
+
"""
|
|
121
|
+
region.Region.__init__(
|
|
122
|
+
self,
|
|
123
|
+
name=name,
|
|
124
|
+
children=regions,
|
|
125
|
+
parent=None,
|
|
126
|
+
shortname=shortname,
|
|
127
|
+
description=description,
|
|
128
|
+
publications=publications,
|
|
129
|
+
datasets=datasets,
|
|
130
|
+
modality=modality,
|
|
131
|
+
prerelease=prerelease,
|
|
132
|
+
)
|
|
133
|
+
self._species_cached = Species.decode(species)
|
|
134
|
+
self._id = identifier
|
|
135
|
+
self.version = version
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def id(self):
|
|
139
|
+
return self._id
|
|
140
|
+
|
|
141
|
+
def matches(self, spec):
|
|
142
|
+
if spec not in self._CACHED_MATCHES:
|
|
143
|
+
if isinstance(spec, str):
|
|
144
|
+
if all(
|
|
145
|
+
w in self.shortname.lower()
|
|
146
|
+
for w in re.split(r'\s+', spec.lower())
|
|
147
|
+
):
|
|
148
|
+
self._CACHED_MATCHES[spec] = True
|
|
149
|
+
return super().matches(spec)
|
|
150
|
+
|
|
151
|
+
def get_map(
|
|
152
|
+
self,
|
|
153
|
+
space: Union[str, "Space"],
|
|
154
|
+
maptype: Union[Literal['labelled', 'statistical'], MapType] = MapType.LABELLED,
|
|
155
|
+
spec: str = ""
|
|
156
|
+
):
|
|
157
|
+
"""
|
|
158
|
+
Get the maps for the parcellation in the requested template space.
|
|
159
|
+
|
|
160
|
+
This might in general include multiple 3D volumes. For example,
|
|
161
|
+
the Julich-Brain atlas provides two separate maps, one per hemisphere.
|
|
162
|
+
Per default, multiple maps are concatenated into a 4D array, but you
|
|
163
|
+
can choose to retrieve a dict of 3D volumes instead using
|
|
164
|
+
`return_dict=True`.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
space: Space or str
|
|
169
|
+
reference space specification such as name, id, or a `Space` instance.
|
|
170
|
+
maptype: MapType or str
|
|
171
|
+
Type of map requested (e.g., statistical or labelled).
|
|
172
|
+
Use MapType.STATISTICAL to request probability maps.
|
|
173
|
+
Defaults to MapType.LABELLED.
|
|
174
|
+
spec: str, optional
|
|
175
|
+
In case of multiple matching maps for the given parcellation, space
|
|
176
|
+
and type, use this field to specify keywords matching the desired
|
|
177
|
+
parcellation map name. Otherwise, siibra will default to the first
|
|
178
|
+
in the list of matches (and inform with a log message)
|
|
179
|
+
Returns
|
|
180
|
+
-------
|
|
181
|
+
parcellationmap.Map or SparseMap
|
|
182
|
+
A ParcellationMap representing the volumetric map or
|
|
183
|
+
a SparseMap representing the list of statistical maps.
|
|
184
|
+
"""
|
|
185
|
+
if isinstance(maptype, str):
|
|
186
|
+
maptype = MapType[maptype.upper()]
|
|
187
|
+
assert isinstance(maptype, MapType), "Possible values of `maptype` are `MapType`s, 'labelled', 'statistical'."
|
|
188
|
+
|
|
189
|
+
candidates = [
|
|
190
|
+
m for m in parcellationmap.Map.registry()
|
|
191
|
+
if m.space.matches(space)
|
|
192
|
+
and m.maptype == maptype
|
|
193
|
+
and m.parcellation.matches(self)
|
|
194
|
+
]
|
|
195
|
+
if len(candidates) == 0:
|
|
196
|
+
raise NoMapMatchingValues(f"No '{maptype}' map in '{space}' available for {str(self)}")
|
|
197
|
+
if len(candidates) > 1:
|
|
198
|
+
spec_candidates = [
|
|
199
|
+
c for c in candidates if all(w.lower() in c.id.lower() for w in spec.split())
|
|
200
|
+
]
|
|
201
|
+
if len(spec_candidates) == 0:
|
|
202
|
+
raise NoMapMatchingValues(f"'{spec}' does not match any options from {[c.name for c in candidates]}.")
|
|
203
|
+
if len(spec_candidates) > 1:
|
|
204
|
+
logger.warning(
|
|
205
|
+
f"Multiple maps are available in this specification of space, parcellation, and map type.\n"
|
|
206
|
+
f"Choosing the first map from {[c.name for c in spec_candidates]}."
|
|
207
|
+
)
|
|
208
|
+
return spec_candidates[0]
|
|
209
|
+
return candidates[0]
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def is_newest_version(self):
|
|
213
|
+
return (self.version is None) or (self.version.next_id is None)
|
|
214
|
+
|
|
215
|
+
def _split_group_spec(self, spec: str):
|
|
216
|
+
"""
|
|
217
|
+
Split a group region specification as produced in older siibra versions
|
|
218
|
+
into the subregion specs.
|
|
219
|
+
This is used when decoding datasets that still include region name
|
|
220
|
+
specifications from old siibra versions, such as some connectivity matrices.
|
|
221
|
+
"""
|
|
222
|
+
spec = re.sub(r'Group: *', '', spec)
|
|
223
|
+
for substr in re.findall(r'\(.*?\)', spec):
|
|
224
|
+
# temporarilty replace commas inside brackets with a placeholder
|
|
225
|
+
# because these are not region spec delimiters
|
|
226
|
+
spec = spec.replace(substr, re.sub(r', *', '##', substr))
|
|
227
|
+
# process the comma separated substrings
|
|
228
|
+
candidates = list({
|
|
229
|
+
self.get_region(re.sub(r'##', ', ', s))
|
|
230
|
+
for s in re.split(r', *', spec)
|
|
231
|
+
})
|
|
232
|
+
if len(candidates) > 0:
|
|
233
|
+
return candidates
|
|
234
|
+
else:
|
|
235
|
+
return [spec]
|
|
236
|
+
|
|
237
|
+
def get_region(
|
|
238
|
+
self,
|
|
239
|
+
regionspec: Union[str, region.Region],
|
|
240
|
+
find_topmost: bool = False,
|
|
241
|
+
allow_tuple: bool = False
|
|
242
|
+
):
|
|
243
|
+
"""
|
|
244
|
+
Given a unique specification, return the corresponding region.
|
|
245
|
+
|
|
246
|
+
The spec could be a (possibly incomplete) name, or a region object.
|
|
247
|
+
This method is meant to definitely determine a valid region. Therefore,
|
|
248
|
+
if no match is found, it raises a ValueError. If multiple matches are
|
|
249
|
+
found, the method tries to return only the common parent node. If there
|
|
250
|
+
is no common parent, an exception is raised, except when
|
|
251
|
+
allow_tuple=True - then a tuple of matched regions is returned.
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
regionspec: str, Region
|
|
256
|
+
- a string with a possibly inexact name (matched both against the name and the identifier key)
|
|
257
|
+
- a Region object
|
|
258
|
+
find_topmost: bool, default: False
|
|
259
|
+
If True, will automatically return the parent of a decoded region
|
|
260
|
+
the decoded region is its only child.
|
|
261
|
+
allow_tuple: bool, default: False
|
|
262
|
+
If multiple candidates without a common parent are found,
|
|
263
|
+
return a tuple of matches instead of raising an exception.
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
Region
|
|
268
|
+
A region object defined in the parcellation.
|
|
269
|
+
|
|
270
|
+
Note
|
|
271
|
+
----
|
|
272
|
+
If the spec exactly matched with more than one region, the first
|
|
273
|
+
will be returned.
|
|
274
|
+
|
|
275
|
+
Raises
|
|
276
|
+
------
|
|
277
|
+
RuntimeError
|
|
278
|
+
If the spec matches multiple regions
|
|
279
|
+
ValueError
|
|
280
|
+
If the spec cannot be matched against any region.
|
|
281
|
+
"""
|
|
282
|
+
assert isinstance(regionspec, (str, region.Region)), f"get_region takes str or Region but you provided {type(regionspec)}"
|
|
283
|
+
if isinstance(regionspec, region.Region) and (regionspec.parcellation == self):
|
|
284
|
+
return regionspec
|
|
285
|
+
|
|
286
|
+
# if there exist an exact match of region spec to region name, return
|
|
287
|
+
if isinstance(regionspec, str):
|
|
288
|
+
exact_match = [
|
|
289
|
+
region
|
|
290
|
+
for region in self
|
|
291
|
+
if region.name == regionspec or region.key == regionspec
|
|
292
|
+
]
|
|
293
|
+
if len(exact_match) == 1:
|
|
294
|
+
return exact_match[0]
|
|
295
|
+
if len(exact_match) > 1:
|
|
296
|
+
logger.info(f"Found multiple region with exact match to {regionspec}. Returning the first one.")
|
|
297
|
+
return exact_match[0]
|
|
298
|
+
|
|
299
|
+
if regionspec.startswith("Group"): # backwards compatibility with old "Group: <region a>, <region b>" specs
|
|
300
|
+
candidates = {
|
|
301
|
+
r
|
|
302
|
+
for spec in self._split_group_spec(regionspec)
|
|
303
|
+
for r in self.find(spec, filter_children=True, find_topmost=find_topmost)
|
|
304
|
+
}
|
|
305
|
+
else:
|
|
306
|
+
candidates = self.find(regionspec, filter_children=True, find_topmost=find_topmost)
|
|
307
|
+
|
|
308
|
+
if len(candidates) > 1 and isinstance(regionspec, str):
|
|
309
|
+
# if we have an exact match of words in one region, discard other candidates.
|
|
310
|
+
querywords = {w.replace(',', '').lower() for w in regionspec.split()}
|
|
311
|
+
full_matches = []
|
|
312
|
+
for c in candidates:
|
|
313
|
+
targetwords = {w.lower() for w in c.name.split()}
|
|
314
|
+
if len(querywords & targetwords) == len(targetwords):
|
|
315
|
+
full_matches.append(c)
|
|
316
|
+
if len(full_matches) == 1:
|
|
317
|
+
candidates = full_matches
|
|
318
|
+
|
|
319
|
+
if not candidates:
|
|
320
|
+
raise ValueError(f"'{regionspec}' could not be decoded under '{self.name}'")
|
|
321
|
+
elif len(candidates) == 1:
|
|
322
|
+
return candidates[0]
|
|
323
|
+
else:
|
|
324
|
+
if allow_tuple:
|
|
325
|
+
return tuple(candidates)
|
|
326
|
+
raise RuntimeError(
|
|
327
|
+
f"Spec {regionspec!r} resulted in multiple matches: {', '.join(r.name for r in candidates)}."
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
def __getitem__(self, regionspec: Union[str, int]):
|
|
331
|
+
"""
|
|
332
|
+
Retrieve a region object from the parcellation by labelindex or partial name.
|
|
333
|
+
"""
|
|
334
|
+
return self.get_region(regionspec)
|
|
335
|
+
|
|
336
|
+
def __lt__(self, other):
|
|
337
|
+
"""
|
|
338
|
+
We sort parcellations by their version
|
|
339
|
+
"""
|
|
340
|
+
if (self.version is None) or (other.version is None):
|
|
341
|
+
logger.warning(
|
|
342
|
+
f"Sorting non-versioned instances of {self.__class__.__name__} "
|
|
343
|
+
f"by name: {self.name}, {other.name}"
|
|
344
|
+
)
|
|
345
|
+
return self.name < other.name
|
|
346
|
+
return self.version.__lt__(other.version)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@lru_cache(maxsize=128)
|
|
350
|
+
def find_regions(
|
|
351
|
+
regionspec: str,
|
|
352
|
+
filter_children=True,
|
|
353
|
+
find_topmost=False
|
|
354
|
+
):
|
|
355
|
+
"""
|
|
356
|
+
Find regions matching the given region specification across all parcellation
|
|
357
|
+
instances in the registery.
|
|
358
|
+
|
|
359
|
+
Parameters
|
|
360
|
+
----------
|
|
361
|
+
regionspec: str, regex, Region
|
|
362
|
+
- a string with a possibly inexact name (matched both against the name and the identifier key)
|
|
363
|
+
- a string in '/pattern/flags' format to use regex search (acceptable flags: aiLmsux) (see https://docs.python.org/3/library/re.html#flags)
|
|
364
|
+
- a regex applied to region names
|
|
365
|
+
- a Region object
|
|
366
|
+
filter_children : bool, default: True
|
|
367
|
+
If True, children of matched parents will not be returned
|
|
368
|
+
find_topmost : bool, default: False
|
|
369
|
+
If True (requires `filter_children=True`), will return parent
|
|
370
|
+
structures if all children are matched, even though the parent
|
|
371
|
+
itself might not match the specification.
|
|
372
|
+
|
|
373
|
+
Returns
|
|
374
|
+
-------
|
|
375
|
+
list[Region]
|
|
376
|
+
list of regions matching to the regionspec
|
|
377
|
+
"""
|
|
378
|
+
result = []
|
|
379
|
+
for p in Parcellation.registry():
|
|
380
|
+
result.extend(
|
|
381
|
+
p.find(
|
|
382
|
+
regionspec=regionspec,
|
|
383
|
+
filter_children=filter_children,
|
|
384
|
+
find_topmost=find_topmost
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
return result
|