siibra 1.0a19__py3-none-any.whl → 1.0.1a0__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.

Files changed (38) hide show
  1. siibra/VERSION +1 -1
  2. siibra/__init__.py +3 -3
  3. siibra/commons.py +0 -46
  4. siibra/configuration/factory.py +10 -20
  5. siibra/core/atlas.py +20 -14
  6. siibra/core/parcellation.py +67 -52
  7. siibra/core/region.py +133 -123
  8. siibra/exceptions.py +8 -0
  9. siibra/experimental/contour.py +6 -6
  10. siibra/experimental/patch.py +2 -2
  11. siibra/experimental/plane3d.py +8 -8
  12. siibra/features/anchor.py +12 -13
  13. siibra/features/connectivity/regional_connectivity.py +2 -2
  14. siibra/features/feature.py +14 -16
  15. siibra/features/tabular/bigbrain_intensity_profile.py +1 -1
  16. siibra/features/tabular/cell_density_profile.py +97 -63
  17. siibra/features/tabular/layerwise_cell_density.py +3 -22
  18. siibra/features/tabular/regional_timeseries_activity.py +2 -2
  19. siibra/livequeries/allen.py +39 -16
  20. siibra/livequeries/bigbrain.py +8 -8
  21. siibra/livequeries/query.py +0 -1
  22. siibra/locations/__init__.py +9 -9
  23. siibra/locations/boundingbox.py +29 -24
  24. siibra/locations/point.py +4 -4
  25. siibra/locations/{pointset.py → pointcloud.py} +30 -22
  26. siibra/retrieval/repositories.py +9 -26
  27. siibra/retrieval/requests.py +19 -2
  28. siibra/volumes/__init__.py +1 -1
  29. siibra/volumes/parcellationmap.py +88 -81
  30. siibra/volumes/providers/neuroglancer.py +62 -36
  31. siibra/volumes/providers/nifti.py +11 -25
  32. siibra/volumes/sparsemap.py +124 -245
  33. siibra/volumes/volume.py +141 -52
  34. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/METADATA +16 -3
  35. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/RECORD +38 -38
  36. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/WHEEL +1 -1
  37. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/LICENSE +0 -0
  38. {siibra-1.0a19.dist-info → siibra-1.0.1a0.dist-info}/top_level.txt +0 -0
siibra/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0a19
1
+ 1.0.1-alpha.0
siibra/__init__.py CHANGED
@@ -39,7 +39,7 @@ from . import configuration
39
39
  from . import experimental
40
40
  from .configuration import factory
41
41
  from . import features, livequeries
42
- from siibra.locations import Point, PointSet
42
+ from siibra.locations import Point, PointCloud
43
43
 
44
44
  import os as _os
45
45
  logger.info(f"Version: {__version__}")
@@ -51,7 +51,7 @@ logger.info(
51
51
  # forward access to some functions
52
52
  set_ebrains_token = _EbrainsRequest.set_token
53
53
  fetch_ebrains_token = _EbrainsRequest.fetch_token
54
- find_regions = _parcellation.Parcellation.find_regions
54
+ find_regions = _parcellation.find_regions
55
55
  from_json = factory.Factory.from_json
56
56
 
57
57
 
@@ -150,7 +150,7 @@ def __dir__():
150
150
  "get_template",
151
151
  "MapType",
152
152
  "Point",
153
- "PointSet",
153
+ "PointCloud",
154
154
  "QUIET",
155
155
  "VERBOSE",
156
156
  "fetch_ebrains_token",
siibra/commons.py CHANGED
@@ -366,9 +366,6 @@ class MapType(Enum):
366
366
  STATISTICAL = 2
367
367
 
368
368
 
369
- SIIBRA_DEFAULT_MAPTYPE = MapType.LABELLED
370
- SIIBRA_DEFAULT_MAP_THRESHOLD = 0.0
371
-
372
369
  REMOVE_FROM_NAME = [
373
370
  "hemisphere",
374
371
  " -",
@@ -586,49 +583,6 @@ def connected_components(
586
583
  )
587
584
 
588
585
 
589
- class PolyLine:
590
- """Simple polyline representation which allows equidistant sampling.."""
591
-
592
- def __init__(self, pts):
593
- self.pts = pts
594
- self.lengths = [
595
- np.sqrt(np.sum((pts[i, :] - pts[i - 1, :]) ** 2))
596
- for i in range(1, pts.shape[0])
597
- ]
598
-
599
- def length(self):
600
- return sum(self.lengths)
601
-
602
- def sample(self, d):
603
-
604
- # if d is interable, we assume a list of sample positions
605
- try:
606
- iter(d)
607
- except TypeError:
608
- positions = [d]
609
- else:
610
- positions = d
611
-
612
- samples = []
613
- for s_ in positions:
614
- s = min(max(s_, 0), 1)
615
- target_distance = s * self.length()
616
- current_distance = 0
617
- for i, length in enumerate(self.lengths):
618
- current_distance += length
619
- if current_distance >= target_distance:
620
- p1 = self.pts[i, :]
621
- p2 = self.pts[i + 1, :]
622
- r = (target_distance - current_distance + length) / length
623
- samples.append(p1 + (p2 - p1) * r)
624
- break
625
-
626
- if len(samples) == 1:
627
- return samples[0]
628
- else:
629
- return np.array(samples)
630
-
631
-
632
586
  def unify_stringlist(L: list):
633
587
  """Adds asterisks to strings that appear multiple times, so the resulting
634
588
  list has only unique strings but still the same length, order, and meaning.
@@ -24,7 +24,7 @@ from ..features.tabular import (
24
24
  )
25
25
  from ..features.image import sections, volume_of_interest
26
26
  from ..core import atlas, parcellation, space, region
27
- from ..locations import point, pointset, boundingbox
27
+ from ..locations import point, pointcloud, boundingbox
28
28
  from ..retrieval import datasets, repositories
29
29
  from ..volumes import volume, sparsemap, parcellationmap
30
30
  from ..volumes.providers.provider import VolumeProvider
@@ -302,25 +302,18 @@ class Factory:
302
302
  @build_type("siibra/map/v0.0.1")
303
303
  def build_map(cls, spec):
304
304
  # maps have no configured identifier - we require the spec filename to build one
305
- assert "filename" in spec
306
- basename = path.splitext(path.basename(spec["filename"]))[0]
307
- name = (
308
- basename.replace("-", " ")
309
- .replace("_", " ")
310
- .replace("continuous", "statistical")
311
- )
312
- identifier = f"{spec['@type'].replace('/', '-')}_{basename}"
305
+ identifier = spec.get("@id")
313
306
  volumes = cls.extract_volumes(
314
- spec, space_id=spec["space"].get("@id"), name_prefix=basename
307
+ spec, space_id=spec["space"].get("@id"), name_prefix=identifier
315
308
  )
316
309
 
317
- if spec.get("sparsemap", {}).get("is_sparsemap"):
310
+ if spec.get("represented_as_sparsemap", False):
318
311
  Maptype = sparsemap.SparseMap
319
312
  else:
320
313
  Maptype = parcellationmap.Map
321
314
  return Maptype(
322
- identifier=spec.get("@id", identifier),
323
- name=spec.get("name", name),
315
+ identifier=identifier,
316
+ name=spec.get("name"),
324
317
  space_spec=spec.get("space", {}),
325
318
  parcellation_spec=spec.get("parcellation", {}),
326
319
  indices=spec.get("indices", {}),
@@ -363,18 +356,18 @@ class Factory:
363
356
 
364
357
  @classmethod
365
358
  @build_type("tmp/poly")
366
- @build_type("siibra/location/pointset/v0.1")
367
- def build_pointset(cls, spec):
359
+ @build_type("siibra/location/pointcloud/v0.1")
360
+ def build_pointcloud(cls, spec):
368
361
  if spec.get("@type") == "tmp/poly":
369
362
  space_id = spec["coordinateSpace"]["@id"]
370
363
  coords = []
371
364
  for coord in spec["coordinates"]:
372
365
  assert all(c["unit"]["@id"] == "id.link/mm" for c in coord)
373
366
  coords.append(list(np.float16(c["value"]) for c in coord))
374
- elif spec.get("@type") == "siibra/location/pointset/v0.1":
367
+ elif spec.get("@type") == "siibra/location/pointcloud/v0.1":
375
368
  space_id = spec.get("space").get("@id")
376
369
  coords = [tuple(c) for c in spec.get("coordinates")]
377
- return pointset.PointSet(coords, space=space_id)
370
+ return pointcloud.PointCloud(coords, space=space_id)
378
371
 
379
372
  @classmethod
380
373
  @build_type("siibra/location/boundingbox/v0.1")
@@ -584,11 +577,8 @@ class Factory:
584
577
 
585
578
  if isinstance(spec, str):
586
579
  if path.isfile(spec):
587
- fname = spec
588
580
  with open(spec, "r") as f:
589
581
  spec = json.load(f)
590
- assert "filename" not in spec
591
- spec["filename"] = fname
592
582
  else:
593
583
  spec = json.loads(spec)
594
584
 
siibra/core/atlas.py CHANGED
@@ -197,27 +197,29 @@ class Atlas(concept.AtlasConcept, configuration_folder="atlases"):
197
197
 
198
198
  def find_regions(
199
199
  self,
200
- regionspec,
201
- all_versions=False,
202
- filter_children=True,
203
- **kwargs
200
+ regionspec: str,
201
+ all_versions: bool = False,
202
+ filter_children: bool = True,
203
+ find_topmost: bool = False
204
204
  ):
205
205
  """
206
- Find regions with the given specification in all
207
- parcellations offered by the atlas. Additional kwargs
208
- are passed on to Parcellation.find().
206
+ Find regions with the given specification in all parcellations offered
207
+ by the atlas.
209
208
 
210
209
  Parameters
211
210
  ----------
212
- regionspec: str, regex, int, Region, MapIndex
211
+ regionspec: str, regex
213
212
  - a string with a possibly inexact name (matched both against the name and the identifier key)
214
213
  - a string in '/pattern/flags' format to use regex search (acceptable flags: aiLmsux, see at https://docs.python.org/3/library/re.html#flags)
215
214
  - a regex applied to region names
216
- - a Region object
217
215
  all_versions : Bool, default: False
218
216
  If True, matched regions for all versions of a parcellation are returned.
219
217
  filter_children : bool, default: True
220
218
  If False, children of matched parents will be returned.
219
+ find_topmost : bool, default: False
220
+ If True (requires `filter_children=True`), will return parent
221
+ structures if all children are matched, even though the parent
222
+ itself might not match the specification.
221
223
 
222
224
  Returns
223
225
  -------
@@ -225,9 +227,13 @@ class Atlas(concept.AtlasConcept, configuration_folder="atlases"):
225
227
  list of regions matching to the regionspec
226
228
  """
227
229
  result = []
228
- for p in self._parcellation_ids:
229
- parcobj = _parcellation.Parcellation.get_instance(p)
230
- if parcobj.is_newest_version or all_versions:
231
- match = parcobj.find(regionspec, filter_children=filter_children, **kwargs)
232
- result.extend(match)
230
+ for p in self.parcellations:
231
+ if p.is_newest_version or all_versions:
232
+ result.extend(
233
+ p.find(
234
+ regionspec=regionspec,
235
+ filter_children=filter_children,
236
+ find_topmost=find_topmost
237
+ )
238
+ )
233
239
  return result
@@ -17,11 +17,21 @@ from . import region
17
17
 
18
18
  from ..commons import logger, MapType, Species
19
19
  from ..volumes import parcellationmap
20
+ from ..exceptions import NoMapMatchingValues
20
21
 
21
- from typing import Union, List, Dict
22
+ from functools import lru_cache
22
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
23
30
 
24
31
 
32
+ if TYPE_CHECKING:
33
+ from .space import Space
34
+
25
35
  # NOTE : such code could be used to automatically resolve
26
36
  # multiple matching parcellations for a short spec to the newset version:
27
37
  # try:
@@ -68,8 +78,6 @@ class ParcellationVersion:
68
78
 
69
79
  class Parcellation(region.Region, configuration_folder="parcellations"):
70
80
 
71
- _CACHED_REGION_SEARCHES: Dict[str, List[region.Region]] = {}
72
-
73
81
  def __init__(
74
82
  self,
75
83
  identifier: str,
@@ -140,7 +148,12 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
140
148
  self._CACHED_MATCHES[spec] = True
141
149
  return super().matches(spec)
142
150
 
143
- def get_map(self, space=None, maptype: Union[str, MapType] = MapType.LABELLED, spec: str = ""):
151
+ def get_map(
152
+ self,
153
+ space: Union[str, "Space"],
154
+ maptype: Union[Literal['labelled', 'statistical'], MapType] = MapType.LABELLED,
155
+ spec: str = ""
156
+ ):
144
157
  """
145
158
  Get the maps for the parcellation in the requested template space.
146
159
 
@@ -153,8 +166,8 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
153
166
  Parameters
154
167
  ----------
155
168
  space: Space or str
156
- template space specification
157
- maptype: MapType
169
+ reference space specification such as name, id, or a `Space` instance.
170
+ maptype: MapType or str
158
171
  Type of map requested (e.g., statistical or labelled).
159
172
  Use MapType.STATISTICAL to request probability maps.
160
173
  Defaults to MapType.LABELLED.
@@ -169,26 +182,24 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
169
182
  A ParcellationMap representing the volumetric map or
170
183
  a SparseMap representing the list of statistical maps.
171
184
  """
172
- if not isinstance(maptype, MapType):
185
+ if isinstance(maptype, str):
173
186
  maptype = MapType[maptype.upper()]
187
+ assert isinstance(maptype, MapType), "Possible values of `maptype` are `MapType`s, 'labelled', 'statistical'."
174
188
 
175
189
  candidates = [
176
190
  m for m in parcellationmap.Map.registry()
177
191
  if m.space.matches(space)
178
192
  and m.maptype == maptype
179
- and m.parcellation
180
193
  and m.parcellation.matches(self)
181
194
  ]
182
195
  if len(candidates) == 0:
183
- logger.error(f"No {maptype} map in {space} available for {str(self)}")
184
- return None
196
+ raise NoMapMatchingValues(f"No '{maptype}' map in '{space}' available for {str(self)}")
185
197
  if len(candidates) > 1:
186
198
  spec_candidates = [
187
- c for c in candidates if all(w.lower() in c.name.lower() for w in spec.split())
199
+ c for c in candidates if all(w.lower() in c.id.lower() for w in spec.split())
188
200
  ]
189
201
  if len(spec_candidates) == 0:
190
- logger.warning(f"'{spec}' does not match any options from {[c.name for c in candidates]}.")
191
- return None
202
+ raise NoMapMatchingValues(f"'{spec}' does not match any options from {[c.name for c in candidates]}.")
192
203
  if len(spec_candidates) > 1:
193
204
  logger.warning(
194
205
  f"Multiple maps are available in this specification of space, parcellation, and map type.\n"
@@ -197,43 +208,6 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
197
208
  return spec_candidates[0]
198
209
  return candidates[0]
199
210
 
200
- @staticmethod
201
- def find_regions(region_spec: str, parents_only=True):
202
- """
203
- Find regions that match the given region specification in the subtree
204
- headed by each parcellation in the registry.
205
- Note
206
- ----
207
- Use Region.find() to search for a region in an instance of a
208
- parcellation.
209
-
210
- Parameters
211
- ----------
212
- regionspec: str
213
- a string with a possibly inexact name, which is matched both
214
- against the name and the identifier key,
215
- parents_only: bool
216
- If true, children of matched parents will not be returned
217
- Returns
218
- -------
219
- List[Region]
220
- list of matching regions
221
- """
222
- MEM = Parcellation._CACHED_REGION_SEARCHES
223
- if region_spec not in MEM:
224
- MEM[region_spec] = [
225
- r
226
- for p in Parcellation.registry()
227
- for r in p.find(regionspec=region_spec)
228
- ]
229
- if parents_only:
230
- return [
231
- r for r in MEM[region_spec]
232
- if (r.parent is None) or (r.parent not in MEM[region_spec])
233
- ]
234
- else:
235
- return MEM[region_spec]
236
-
237
211
  @property
238
212
  def is_newest_version(self):
239
213
  return (self.version is None) or (self.version.next_id is None)
@@ -263,7 +237,7 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
263
237
  def get_region(
264
238
  self,
265
239
  regionspec: Union[str, region.Region],
266
- find_topmost: bool = True,
240
+ find_topmost: bool = False,
267
241
  allow_tuple: bool = False
268
242
  ):
269
243
  """
@@ -281,7 +255,7 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
281
255
  regionspec: str, Region
282
256
  - a string with a possibly inexact name (matched both against the name and the identifier key)
283
257
  - a Region object
284
- find_topmost: bool, default: True
258
+ find_topmost: bool, default: False
285
259
  If True, will automatically return the parent of a decoded region
286
260
  the decoded region is its only child.
287
261
  allow_tuple: bool, default: False
@@ -370,3 +344,44 @@ class Parcellation(region.Region, configuration_folder="parcellations"):
370
344
  )
371
345
  return self.name < other.name
372
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