siibra 1.0.1a2__tar.gz → 1.0.1a5__tar.gz

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 (89) hide show
  1. {siibra-1.0.1a2/siibra.egg-info → siibra-1.0.1a5}/PKG-INFO +28 -12
  2. {siibra-1.0.1a2 → siibra-1.0.1a5}/README.rst +25 -10
  3. siibra-1.0.1a5/siibra/VERSION +1 -0
  4. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/commons.py +10 -1
  5. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/configuration/configuration.py +0 -1
  6. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/configuration/factory.py +4 -0
  7. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/core/assignment.py +18 -7
  8. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/explorer/url.py +2 -2
  9. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/anchor.py +1 -3
  10. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/connectivity/regional_connectivity.py +41 -27
  11. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/feature.py +8 -2
  12. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/image/__init__.py +2 -1
  13. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/image/volume_of_interest.py +9 -0
  14. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/gene_expression.py +28 -11
  15. siibra-1.0.1a5/siibra/features/tabular/layerwise_cell_density.py +154 -0
  16. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/tabular.py +10 -2
  17. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/livequeries/allen.py +3 -0
  18. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/locations/boundingbox.py +4 -3
  19. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/locations/location.py +10 -4
  20. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/locations/point.py +7 -3
  21. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/locations/pointcloud.py +7 -3
  22. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/parcellationmap.py +3 -1
  23. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/sparsemap.py +13 -21
  24. {siibra-1.0.1a2 → siibra-1.0.1a5/siibra.egg-info}/PKG-INFO +28 -12
  25. siibra-1.0.1a2/siibra/VERSION +0 -1
  26. siibra-1.0.1a2/siibra/features/tabular/layerwise_cell_density.py +0 -93
  27. {siibra-1.0.1a2 → siibra-1.0.1a5}/LICENSE +0 -0
  28. {siibra-1.0.1a2 → siibra-1.0.1a5}/MANIFEST.in +0 -0
  29. {siibra-1.0.1a2 → siibra-1.0.1a5}/setup.cfg +0 -0
  30. {siibra-1.0.1a2 → siibra-1.0.1a5}/setup.py +0 -0
  31. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/__init__.py +0 -0
  32. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/configuration/__init__.py +0 -0
  33. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/core/__init__.py +0 -0
  34. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/core/atlas.py +0 -0
  35. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/core/concept.py +0 -0
  36. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/core/parcellation.py +0 -0
  37. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/core/region.py +0 -0
  38. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/core/space.py +0 -0
  39. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/core/structure.py +0 -0
  40. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/exceptions.py +0 -0
  41. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/explorer/__init__.py +0 -0
  42. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/explorer/util.py +0 -0
  43. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/__init__.py +0 -0
  44. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/connectivity/__init__.py +0 -0
  45. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/connectivity/functional_connectivity.py +0 -0
  46. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/connectivity/streamline_counts.py +0 -0
  47. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/connectivity/streamline_lengths.py +0 -0
  48. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/connectivity/tracing_connectivity.py +0 -0
  49. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/dataset/__init__.py +0 -0
  50. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/dataset/ebrains.py +0 -0
  51. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/image/image.py +0 -0
  52. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/image/sections.py +0 -0
  53. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/__init__.py +0 -0
  54. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/bigbrain_intensity_profile.py +0 -0
  55. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/cell_density_profile.py +0 -0
  56. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/cortical_profile.py +0 -0
  57. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/layerwise_bigbrain_intensities.py +0 -0
  58. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/receptor_density_fingerprint.py +0 -0
  59. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/receptor_density_profile.py +0 -0
  60. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/features/tabular/regional_timeseries_activity.py +0 -0
  61. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/livequeries/__init__.py +0 -0
  62. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/livequeries/bigbrain.py +0 -0
  63. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/livequeries/ebrains.py +0 -0
  64. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/livequeries/query.py +0 -0
  65. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/locations/__init__.py +0 -0
  66. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/locations/experimental.py +0 -0
  67. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/retrieval/__init__.py +0 -0
  68. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/retrieval/cache.py +0 -0
  69. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/retrieval/datasets.py +0 -0
  70. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/retrieval/exceptions/__init__.py +0 -0
  71. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/retrieval/repositories.py +0 -0
  72. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/retrieval/requests.py +0 -0
  73. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/vocabularies/__init__.py +0 -0
  74. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/vocabularies/gene_names.json +0 -0
  75. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/vocabularies/receptor_symbols.json +0 -0
  76. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/vocabularies/region_aliases.json +0 -0
  77. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/__init__.py +0 -0
  78. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/providers/__init__.py +0 -0
  79. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/providers/freesurfer.py +0 -0
  80. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/providers/gifti.py +0 -0
  81. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/providers/neuroglancer.py +0 -0
  82. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/providers/nifti.py +0 -0
  83. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/providers/provider.py +0 -0
  84. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra/volumes/volume.py +0 -0
  85. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra.egg-info/SOURCES.txt +0 -0
  86. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra.egg-info/dependency_links.txt +0 -0
  87. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra.egg-info/requires.txt +0 -0
  88. {siibra-1.0.1a2 → siibra-1.0.1a5}/siibra.egg-info/top_level.txt +0 -0
  89. {siibra-1.0.1a2 → siibra-1.0.1a5}/test/test_siibra.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: siibra
3
- Version: 1.0.1a2
3
+ Version: 1.0.1a5
4
4
  Summary: siibra - Software interfaces for interacting with brain atlases
5
5
  Home-page: https://github.com/FZJ-INM1-BDA/siibra-python
6
6
  Author: Big Data Analytics Group, Forschungszentrum Juelich, Institute of Neuroscience and Medicine (INM-1)
@@ -33,6 +33,7 @@ Dynamic: classifier
33
33
  Dynamic: description
34
34
  Dynamic: description-content-type
35
35
  Dynamic: home-page
36
+ Dynamic: license-file
36
37
  Dynamic: requires-dist
37
38
  Dynamic: requires-python
38
39
  Dynamic: summary
@@ -43,18 +44,16 @@ Dynamic: summary
43
44
  siibra - Software interface for interacting with brain atlases
44
45
  ==============================================================
45
46
 
46
- Copyright 2018-2024, Forschungszentrum Jülich GmbH
47
+ Copyright 2018-2025, Forschungszentrum Jülich GmbH
47
48
 
48
49
  *Authors: Big Data Analytics Group, Institute of Neuroscience and
49
50
  Medicine (INM-1), Forschungszentrum Jülich GmbH*
50
51
 
51
52
  .. intro-start
52
53
 
53
- ``siibra`` is a Python client to a brain atlas framework that integrates brain parcellations and reference spaces at different spatial scales, and connects them with a broad range of multimodal regional data features.
54
+ ``siibra-python`` is a Python client to a brain atlas framework that integrates brain parcellations and reference spaces at different spatial scales, and connects them with a broad range of multimodal regional data features.
54
55
  It aims to facilitate programmatic and reproducible incorporation of brain parcellations and brain region features from different sources into neuroscience workflows.
55
56
 
56
- **Note:** ``siibra-python`` *is still in development. While care is taken that it works reliably, its API is not yet stable and you may still encounter bugs when using it.*
57
-
58
57
  ``siibra`` provides structured access to parcellation schemes in different brain reference spaces, including volumetric reference templates at macroscopic and microscopic resolutions as well as surface representations.
59
58
  It supports both discretely labelled and statistical (probabilistic) parcellation maps, which can be used to assign brain regions to spatial locations and image signals, to retrieve region-specific neuroscience datasets from multiple online repositories, and to sample information from high-resolution image data.
60
59
  The datasets anchored to brain regions address features of molecular, cellular and architecture as well as connectivity, and are complemented with live queries to external repositories as well as dynamic extraction from "big" image volumes such as the 20 micrometer BigBrain model.
@@ -84,10 +83,26 @@ Installation
84
83
 
85
84
  ``siibra`` is available on pypi.
86
85
  To install the latest released version, simply run ``pip install siibra``.
86
+ The installation typically takes about 2 minutes on a standard computer where Python is already installed.
87
87
  In order to work with the latest version from github, use ``pip install git+https://github.com/FZJ-INM1-BDA/siibra-python.git@main``.
88
88
 
89
- There is also an image based on jupyter:scipy-notebook, which already includes ``siibra``.
90
-
89
+ ``siibra-python`` should be installable on recent versions of Windows, Linux and Mac OS in a recent Python 3 environment.
90
+ We run continuous integration tests for versions 3.7 - 3.12 on recent Ubuntu images.
91
+
92
+ The library requires a couple of open source packages, namely:
93
+ ```
94
+ anytree >= 2.12.1
95
+ nibabel >= 5.3.2
96
+ appdirs >= 1.4.4
97
+ scikit-image >= 0.25.0
98
+ requests >= 2.32.3
99
+ neuroglancer-scripts >= 1.2.0
100
+ nilearn >= 0.11.0
101
+ filelock >= 3.16.1
102
+ ebrains-drive >= 0.6.0
103
+ ```
104
+
105
+ You can also install a docker image with all dependencies included:
91
106
  .. code-block:: sh
92
107
 
93
108
  docker run -dit \
@@ -97,19 +112,20 @@ There is also an image based on jupyter:scipy-notebook, which already includes `
97
112
  docker-registry.ebrains.eu/siibra/siibra-python:latest
98
113
 
99
114
 
115
+
100
116
  Documentation & Help
101
117
  ====================
102
118
 
103
119
  ``siibra-python``\ ’s documentation is hosted on https://siibra-python.readthedocs.io.
104
120
  The documentation includes a catalogue of documented code examples that walk you through the different concepts and functionalities.
121
+ These examples use real data and include both the code and the produced expected outputs.
122
+ They can be accessed at https://siibra-python.readthedocs.io/en/latest/examples.html, and are
123
+ automatically tested and updated whenever a new version of ``siibra-python`` is published.
105
124
  As a new user, it is recommended to go through these examples - they are easy and will quickly provide you with the right code snippets that get you started.
106
- Furthermore, a set of jupyter notebooks demonstrating more extensive example use cases are maintained in the `siibra-tutorials <https://github.com/FZJ-INM1-BDA/siibra-tutorials>`__ repository.
107
- We are working on a full API documentation of the library. You find the current status on readthedocs, but be aware that it is not yet complete and as up-to-date as the code examples.
125
+ The documentation on readthedocs further includes introductory explanations and an API reference.
108
126
 
109
127
  If you run into issues, please open a ticket on `EBRAINS support <https://ebrains.eu/support/>`__ or file bugs and
110
128
  feature requests on `github <https://github.com/FZJ-INM1-BDA/siibra-python/issues>`__.
111
- Please keep in mind that ``siibra-python`` is still in development.
112
- While care is taken to make everything work reliably, the API of the library is not yet stable, and the software is not yet fully tested.
113
129
 
114
130
  .. getting-started-end
115
131
 
@@ -4,18 +4,16 @@
4
4
  siibra - Software interface for interacting with brain atlases
5
5
  ==============================================================
6
6
 
7
- Copyright 2018-2024, Forschungszentrum Jülich GmbH
7
+ Copyright 2018-2025, Forschungszentrum Jülich GmbH
8
8
 
9
9
  *Authors: Big Data Analytics Group, Institute of Neuroscience and
10
10
  Medicine (INM-1), Forschungszentrum Jülich GmbH*
11
11
 
12
12
  .. intro-start
13
13
 
14
- ``siibra`` is a Python client to a brain atlas framework that integrates brain parcellations and reference spaces at different spatial scales, and connects them with a broad range of multimodal regional data features.
14
+ ``siibra-python`` is a Python client to a brain atlas framework that integrates brain parcellations and reference spaces at different spatial scales, and connects them with a broad range of multimodal regional data features.
15
15
  It aims to facilitate programmatic and reproducible incorporation of brain parcellations and brain region features from different sources into neuroscience workflows.
16
16
 
17
- **Note:** ``siibra-python`` *is still in development. While care is taken that it works reliably, its API is not yet stable and you may still encounter bugs when using it.*
18
-
19
17
  ``siibra`` provides structured access to parcellation schemes in different brain reference spaces, including volumetric reference templates at macroscopic and microscopic resolutions as well as surface representations.
20
18
  It supports both discretely labelled and statistical (probabilistic) parcellation maps, which can be used to assign brain regions to spatial locations and image signals, to retrieve region-specific neuroscience datasets from multiple online repositories, and to sample information from high-resolution image data.
21
19
  The datasets anchored to brain regions address features of molecular, cellular and architecture as well as connectivity, and are complemented with live queries to external repositories as well as dynamic extraction from "big" image volumes such as the 20 micrometer BigBrain model.
@@ -45,10 +43,26 @@ Installation
45
43
 
46
44
  ``siibra`` is available on pypi.
47
45
  To install the latest released version, simply run ``pip install siibra``.
46
+ The installation typically takes about 2 minutes on a standard computer where Python is already installed.
48
47
  In order to work with the latest version from github, use ``pip install git+https://github.com/FZJ-INM1-BDA/siibra-python.git@main``.
49
48
 
50
- There is also an image based on jupyter:scipy-notebook, which already includes ``siibra``.
51
-
49
+ ``siibra-python`` should be installable on recent versions of Windows, Linux and Mac OS in a recent Python 3 environment.
50
+ We run continuous integration tests for versions 3.7 - 3.12 on recent Ubuntu images.
51
+
52
+ The library requires a couple of open source packages, namely:
53
+ ```
54
+ anytree >= 2.12.1
55
+ nibabel >= 5.3.2
56
+ appdirs >= 1.4.4
57
+ scikit-image >= 0.25.0
58
+ requests >= 2.32.3
59
+ neuroglancer-scripts >= 1.2.0
60
+ nilearn >= 0.11.0
61
+ filelock >= 3.16.1
62
+ ebrains-drive >= 0.6.0
63
+ ```
64
+
65
+ You can also install a docker image with all dependencies included:
52
66
  .. code-block:: sh
53
67
 
54
68
  docker run -dit \
@@ -58,19 +72,20 @@ There is also an image based on jupyter:scipy-notebook, which already includes `
58
72
  docker-registry.ebrains.eu/siibra/siibra-python:latest
59
73
 
60
74
 
75
+
61
76
  Documentation & Help
62
77
  ====================
63
78
 
64
79
  ``siibra-python``\ ’s documentation is hosted on https://siibra-python.readthedocs.io.
65
80
  The documentation includes a catalogue of documented code examples that walk you through the different concepts and functionalities.
81
+ These examples use real data and include both the code and the produced expected outputs.
82
+ They can be accessed at https://siibra-python.readthedocs.io/en/latest/examples.html, and are
83
+ automatically tested and updated whenever a new version of ``siibra-python`` is published.
66
84
  As a new user, it is recommended to go through these examples - they are easy and will quickly provide you with the right code snippets that get you started.
67
- Furthermore, a set of jupyter notebooks demonstrating more extensive example use cases are maintained in the `siibra-tutorials <https://github.com/FZJ-INM1-BDA/siibra-tutorials>`__ repository.
68
- We are working on a full API documentation of the library. You find the current status on readthedocs, but be aware that it is not yet complete and as up-to-date as the code examples.
85
+ The documentation on readthedocs further includes introductory explanations and an API reference.
69
86
 
70
87
  If you run into issues, please open a ticket on `EBRAINS support <https://ebrains.eu/support/>`__ or file bugs and
71
88
  feature requests on `github <https://github.com/FZJ-INM1-BDA/siibra-python/issues>`__.
72
- Please keep in mind that ``siibra-python`` is still in development.
73
- While care is taken to make everything work reliably, the API of the library is not yet stable, and the software is not yet fully tested.
74
89
 
75
90
  .. getting-started-end
76
91
 
@@ -0,0 +1 @@
1
+ 1.0.1-alpha.5
@@ -534,13 +534,21 @@ def resample_img_to_img(
534
534
  -------
535
535
  Nifti1Image
536
536
  """
537
+ from nilearn._version import version as nilearn_version
538
+ from packaging.version import Version
539
+
537
540
  interpolation = "nearest" if np.array_equal(np.unique(source_img.dataobj), [0, 1]) else "linear"
538
- resampled_img = resample_to_img(
541
+ kwargs = dict(
539
542
  source_img=source_img,
540
543
  target_img=target_img,
541
544
  interpolation=interpolation,
542
545
  force_resample=True, # False is intended for testing. see nilearn docs
543
546
  )
547
+ if Version(nilearn_version) >= Version("0.11.0"):
548
+ # because nilearn>=0.11.0 don't support "copy_header" and python <= 3.8
549
+ kwargs["copy_header"] = True # use new default in nilearn >= 0.11.0
550
+
551
+ resampled_img = resample_to_img(**kwargs)
544
552
  return resampled_img
545
553
 
546
554
 
@@ -725,6 +733,7 @@ class Species(Enum):
725
733
  MACACA_MULATTA = 5
726
734
  MACACA_FUSCATA = 6
727
735
  CHLOROCEBUS_AETHIOPS_SABAEUS = 7
736
+ CALLITHRIX_JACCHUS = 8
728
737
 
729
738
  UNSPECIFIED_SPECIES = 999
730
739
 
@@ -185,5 +185,4 @@ class Configuration:
185
185
 
186
186
 
187
187
  if SIIBRA_USE_CONFIGURATION:
188
- logger.warning(f"config.SIIBRA_USE_CONFIGURATION defined, use configuration at {SIIBRA_USE_CONFIGURATION}")
189
188
  Configuration.use_configuration(SIIBRA_USE_CONFIGURATION)
@@ -487,6 +487,10 @@ class Factory:
487
487
  return volume_of_interest.LSFMVolumeOfInterest(
488
488
  modality="Light Sheet Fluorescence Microscopy", **kwargs
489
489
  )
490
+ elif modality == "morphometry":
491
+ return volume_of_interest.MorphometryVolumeOfInterest(
492
+ modality="Morphometry", **kwargs
493
+ )
490
494
  else:
491
495
  raise ValueError(
492
496
  f"No method for building image section feature type {modality}."
@@ -26,12 +26,12 @@ T = TypeVar("T")
26
26
 
27
27
  class Qualification(Enum):
28
28
  EXACT = 1
29
- OVERLAPS = 2
30
- CONTAINED = 3
31
- CONTAINS = 4
32
- APPROXIMATE = 5
33
- HOMOLOGOUS = 6
34
- OTHER_VERSION = 7
29
+ APPROXIMATE = 2
30
+ OTHER_VERSION = 3
31
+ CONTAINED = 4
32
+ CONTAINS = 5
33
+ OVERLAPS = 6
34
+ HOMOLOGOUS = 7
35
35
 
36
36
  @property
37
37
  def verb(self):
@@ -67,6 +67,17 @@ class Qualification(Enum):
67
67
  assert self in inverses, f"{str(self)} inverses cannot be found."
68
68
  return inverses[self]
69
69
 
70
+ def __lt__(self, other: "Qualification"):
71
+ """
72
+ This is used to sort feature query results. Since it is very difficult
73
+ to determine a well-ordering principle and it is difficult to sort
74
+ without one, the enum values are used for sorting. This means
75
+ not all comparisons have logical basis but they are well-defined,
76
+ making it reproducible but also clearly distinguishes important
77
+ comparisons.
78
+ """
79
+ return self.value < other.value
80
+
70
81
  def __str__(self):
71
82
  return f"{self.__class__.__name__}={self.name.lower()}"
72
83
 
@@ -108,4 +119,4 @@ class AnatomicalAssignment(Generic[T]):
108
119
  def __lt__(self, other: 'AnatomicalAssignment'):
109
120
  if not isinstance(other, AnatomicalAssignment):
110
121
  raise ValueError(f"Cannot compare AnatomicalAssignment with instances of '{type(other)}'")
111
- return self.qualification.value < other.qualification.value
122
+ return self.qualification < other.qualification
@@ -136,7 +136,7 @@ def encode_url(
136
136
 
137
137
  try:
138
138
  result_props = region.spatial_props(space, maptype="labelled")
139
- if len(result_props.components) == 0:
139
+ if len(result_props) == 0:
140
140
  return return_url + nav_string.format(encoded_nav=encoded_position or "0.0.0", **zoom_kwargs)
141
141
  except Exception as e:
142
142
  print(f"Cannot get_spatial_props {str(e)}")
@@ -144,7 +144,7 @@ def encode_url(
144
144
  raise e
145
145
  return return_url + nav_string.format(encoded_nav=encoded_position or "0.0.0", **zoom_kwargs)
146
146
 
147
- centroid = result_props.components[0].centroid
147
+ centroid = result_props[0].centroid
148
148
 
149
149
  encoded_centroid = separator.join(
150
150
  [encode_number(math.floor(val * 1e6)) for val in centroid]
@@ -176,9 +176,7 @@ class AnatomicalAnchor:
176
176
  assignments.append(region.assign(concept))
177
177
  self._assignments[concept] = sorted(a for a in assignments if a is not None)
178
178
 
179
- self._last_matched_concept = concept \
180
- if len(self._assignments[concept]) > 0 \
181
- else None
179
+ self._last_matched_concept = concept if len(self._assignments[concept]) > 0 else None
182
180
  return self._assignments[concept]
183
181
 
184
182
  def matches(self, concept: Union[BrainStructure, Space]) -> bool:
@@ -104,6 +104,7 @@ class RegionalConnectivity(Feature, Compoundable):
104
104
  self._matrix = None
105
105
  self._subject = subject
106
106
  self._feature = feature
107
+ self._matrix_std = None # only used for compound feature
107
108
 
108
109
  @property
109
110
  def subject(self):
@@ -170,6 +171,9 @@ class RegionalConnectivity(Feature, Compoundable):
170
171
  merged._matrix = elements[0]._arraylike_to_dataframe(
171
172
  np.stack(all_arrays).mean(0)
172
173
  )
174
+ merged._matrix_std = elements[0]._arraylike_to_dataframe(
175
+ np.stack(all_arrays).std(0)
176
+ )
173
177
  return merged
174
178
 
175
179
  def _plot_matrix(
@@ -258,10 +262,14 @@ class RegionalConnectivity(Feature, Compoundable):
258
262
  non-symmetric matrices. ('column' or 'row')
259
263
  """
260
264
  matrix = self.data
265
+ assert isinstance(matrix, pd.DataFrame)
266
+ matrix_std = self._matrix_std
261
267
  if direction.lower() not in ['column', 'row']:
262
268
  raise ValueError("Direction can only be 'column' or 'row'")
263
269
  if direction.lower() == 'row':
264
270
  matrix = matrix.transpose()
271
+ if matrix_std is not None:
272
+ matrix_std = matrix_std.transpose()
265
273
 
266
274
  def matches(r1, r2):
267
275
  if isinstance(r1, tuple):
@@ -270,32 +278,38 @@ class RegionalConnectivity(Feature, Compoundable):
270
278
  assert isinstance(r1, _region.Region)
271
279
  return r1.matches(r2)
272
280
 
273
- regions = [r for r in matrix.index if matches(r, region)]
274
- if len(regions) == 0:
281
+ # decode region spec
282
+ region_candidates = [r for r in matrix.index if matches(r, region)]
283
+ if len(region_candidates) == 0:
275
284
  raise ValueError(f"Invalid region specification: {region}")
276
- elif len(regions) > 1:
277
- raise ValueError(f"Region specification {region} matched more than one profile: {regions}")
278
- else:
279
- name = self.modality
280
- series = matrix[regions[0]]
281
- last_index = len(series) - 1 if max_rows is None \
282
- else min(max_rows, len(series) - 1)
283
- return Tabular(
284
- description=self.description,
285
- modality=f"{self.modality} {self.cohort}",
286
- anchor=_anchor.AnatomicalAnchor(
287
- species=list(self.anchor.species)[0],
288
- region=regions[0]
289
- ),
290
- data=(
291
- series[:last_index]
292
- .to_frame(name=name)
293
- .query(f'`{name}` > {min_connectivity}')
294
- .sort_values(by=name, ascending=False)
295
- .rename_axis('Target regions')
296
- ),
297
- datasets=self.datasets
298
- )
285
+ if len(region_candidates) > 1:
286
+ raise ValueError(f"Region specification {region} matched more than one profile: {region_candidates}")
287
+ region = region_candidates[0]
288
+
289
+ # create DataFrame
290
+ data = matrix[region].to_frame('mean')
291
+ if matrix_std is not None:
292
+ data = pd.concat([data, matrix_std[region].rename('std')], axis=1)
293
+
294
+ last_index = len(data) if max_rows is None else min(max_rows, len(data))
295
+
296
+ data = (
297
+ data
298
+ .query(f'`mean` > {min_connectivity}')
299
+ .sort_values(by="mean", ascending=False)
300
+ .rename_axis('Target regions')
301
+ )[:last_index]
302
+
303
+ return Tabular(
304
+ description=self.description,
305
+ modality=f"{self.modality} {self.cohort}",
306
+ anchor=_anchor.AnatomicalAnchor(
307
+ species=list(self.anchor.species)[0],
308
+ region=region
309
+ ),
310
+ data=data,
311
+ datasets=self.datasets
312
+ )
299
313
 
300
314
  def plot(
301
315
  self,
@@ -336,8 +350,8 @@ class RegionalConnectivity(Feature, Compoundable):
336
350
  profile = self.get_profile(regions, min_connectivity, max_rows, direction)
337
351
  kwargs["kind"] = kwargs.get("kind", "barh")
338
352
  if backend == "matplotlib":
339
- kwargs["logx"] = kwargs.get("logx", logscale)
340
- return profile.data.plot(*args, backend=backend, **kwargs)
353
+ kwargs["logy"] = kwargs.get("logy", logscale)
354
+ return profile.plot(*args, backend=backend, **kwargs)
341
355
  elif backend == "plotly":
342
356
  kwargs.update({
343
357
  "color": kwargs.get("color", profile.data.columns[0]),
@@ -232,7 +232,10 @@ class Feature:
232
232
  from ..configuration.configuration import Configuration
233
233
  conf = Configuration()
234
234
  Configuration.register_cleanup(cls._clean_instances)
235
- assert cls._configuration_folder in conf.folders
235
+ if cls._configuration_folder not in conf.folders:
236
+ logger.debug(f"{cls._configuration_folder} is not in current configuration")
237
+ return []
238
+
236
239
  cls._preconfigured_instances = [
237
240
  o for o in conf.build_objects(cls._configuration_folder)
238
241
  if isinstance(o, cls)
@@ -580,7 +583,10 @@ class Feature:
580
583
  # with the query concept.
581
584
  live_instances = feature_type._livequery(concept, **kwargs)
582
585
 
583
- results = list(dict.fromkeys(preconfigured_instances + live_instances))
586
+ results = sorted(
587
+ dict.fromkeys(preconfigured_instances + live_instances), # to remove duplicates
588
+ key=lambda f: min(f.last_match_result) if f.last_match_result else False, # to order according to assignmnent ranking
589
+ )
584
590
  return CompoundFeature._compound(results, concept)
585
591
 
586
592
  @classmethod
@@ -21,7 +21,8 @@ from .volume_of_interest import (
21
21
  MRIVolumeOfInterest,
22
22
  XPCTVolumeOfInterest,
23
23
  LSFMVolumeOfInterest,
24
- DTIVolumeOfInterest
24
+ DTIVolumeOfInterest,
25
+ MorphometryVolumeOfInterest,
25
26
  )
26
27
  from .sections import (
27
28
  CellbodyStainedSection,
@@ -78,3 +78,12 @@ class LSFMVolumeOfInterest(
78
78
  ):
79
79
  def __init__(self, modality, **kwargs):
80
80
  image.Image.__init__(self, **kwargs, modality=modality)
81
+
82
+
83
+ class MorphometryVolumeOfInterest(
84
+ image.Image,
85
+ configuration_folder="features/images/vois/morphometry",
86
+ category="macrostructural"
87
+ ):
88
+ def __init__(self, modality, **kwargs):
89
+ image.Image.__init__(self, **kwargs, modality=modality)
@@ -225,6 +225,7 @@ class GeneExpressions(
225
225
  datasets=datasets
226
226
  )
227
227
  self.unit = "expression level"
228
+ self._genes = list(set(genes))
228
229
 
229
230
  def plot(self, *args, backend="matplotlib", **kwargs):
230
231
  """
@@ -239,18 +240,34 @@ class GeneExpressions(
239
240
  Keyword arguments are passed on to the plot command.
240
241
  """
241
242
  wrapwidth = kwargs.pop("textwrap") if "textwrap" in kwargs else 40
242
- kwargs["title"] = kwargs.pop("title", None) \
243
- or "\n".join(wrap(f"{self.modality} measured in {self.anchor._regionspec or self.anchor.location}", wrapwidth))
244
- kwargs["kind"] = "box"
243
+ kwargs["title"] = kwargs.pop(
244
+ "title",
245
+ "\n".join(wrap(
246
+ f"{self.modality}\n{self.anchor._regionspec or self.anchor.location}",
247
+ wrapwidth
248
+ ))
249
+ )
250
+ kwargs["kind"] = kwargs.get("kind", "box")
245
251
  if backend == "matplotlib":
246
- for arg in ['yerr', 'y', 'ylabel', 'xlabel', 'width']:
247
- assert arg not in kwargs
248
- default_kwargs = {
249
- "grid": True, "legend": False, 'by': "gene",
250
- 'column': ['level'], 'showfliers': False, 'ax': None,
251
- 'ylabel': 'expression level'
252
- }
253
- return self.data.plot(*args, **{**default_kwargs, **kwargs}, backend=backend)
252
+ if kwargs["kind"] == "box":
253
+ from matplotlib.pyplot import tight_layout
254
+
255
+ title = kwargs.pop("title")
256
+ default_kwargs = {
257
+ "grid": True,
258
+ 'by': "gene",
259
+ 'column': ['level'],
260
+ 'showfliers': False,
261
+ 'ylabel': 'expression level',
262
+ 'xlabel': 'gene',
263
+ 'color': 'dimgray',
264
+ 'rot': 90 if len(self._genes) > 1 else 0,
265
+ }
266
+ ax, *_ = self.data.plot(*args, backend=backend, **{**default_kwargs, **kwargs})
267
+ ax.set_title(title)
268
+ tight_layout()
269
+ return ax
270
+ return self.data.plot(*args, backend=backend, **kwargs)
254
271
  elif backend == "plotly":
255
272
  kwargs["title"] = kwargs["title"].replace('\n', "<br>")
256
273
  return self.data.plot(y='level', x='gene', backend=backend, **kwargs)