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,322 @@
|
|
|
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
|
+
|
|
16
|
+
from . import tabular
|
|
17
|
+
from ..feature import Compoundable
|
|
18
|
+
|
|
19
|
+
from .. import anchor as _anchor
|
|
20
|
+
|
|
21
|
+
import pandas as pd
|
|
22
|
+
from typing import Union, Dict, Tuple, List
|
|
23
|
+
from textwrap import wrap
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CorticalProfile(tabular.Tabular, Compoundable):
|
|
28
|
+
"""
|
|
29
|
+
Represents a 1-dimensional profile of measurements along cortical depth,
|
|
30
|
+
measured at relative depths between 0 representing the pial surface,
|
|
31
|
+
and 1 corresponding to the gray/white matter boundary.
|
|
32
|
+
|
|
33
|
+
Mandatory attributes are the list of depth coordinates and the list of
|
|
34
|
+
corresponding measurement values, which have to be of equal length,
|
|
35
|
+
as well as a unit and description of the measurements.
|
|
36
|
+
|
|
37
|
+
Optionally, the depth coordinates of layer boundaries can be specified.
|
|
38
|
+
|
|
39
|
+
Most attributes are modelled as properties, so dervide classes are able
|
|
40
|
+
to implement lazy loading instead of direct initialiation.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
LAYERS = {0: "0", 1: "I", 2: "II", 3: "III", 4: "IV", 5: "V", 6: "VI", 7: "WM"}
|
|
45
|
+
BOUNDARIES = list(zip(list(LAYERS.keys())[:-1], list(LAYERS.keys())[1:]))
|
|
46
|
+
|
|
47
|
+
_filter_attrs = ["modality"]
|
|
48
|
+
_compound_attrs = ["modality"]
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
description: str,
|
|
53
|
+
modality: str,
|
|
54
|
+
anchor: _anchor.AnatomicalAnchor,
|
|
55
|
+
depths: Union[list, np.ndarray] = None,
|
|
56
|
+
values: Union[list, np.ndarray] = None,
|
|
57
|
+
unit: str = None,
|
|
58
|
+
boundary_positions: Dict[Tuple[int, int], float] = None,
|
|
59
|
+
datasets: list = [],
|
|
60
|
+
id: str = None,
|
|
61
|
+
prerelease: bool = False,
|
|
62
|
+
):
|
|
63
|
+
"""Initialize profile.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
description: str
|
|
68
|
+
Human-readable of the modality of the measurements.
|
|
69
|
+
modality: str
|
|
70
|
+
Short textual description of the modality of measurements.
|
|
71
|
+
anchor: AnatomicalAnchor
|
|
72
|
+
depths: list, default: None
|
|
73
|
+
List of cortical depth positions corresponding to each
|
|
74
|
+
measurement, all in the range [0..1]
|
|
75
|
+
values: list, default: None
|
|
76
|
+
List of the actual measurements at each depth position.
|
|
77
|
+
Length must correspond to 'depths'.
|
|
78
|
+
unit: str, default: None
|
|
79
|
+
Textual identifier for the unit of measurements.
|
|
80
|
+
boundary_positions: dict, default: None
|
|
81
|
+
Dictionary of depths at which layer boundaries were identified.
|
|
82
|
+
Keys are tuples of layer numbers, e.g. (1,2), and values are
|
|
83
|
+
cortical depth positions in the range [0..1].
|
|
84
|
+
datasets : list[Dataset]
|
|
85
|
+
list of datasets corresponding to this feature
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
# cached properties will be revealed as property functions,
|
|
89
|
+
# so derived classes may choose to override for lazy loading.
|
|
90
|
+
self._unit = unit
|
|
91
|
+
self._depths_cached = depths
|
|
92
|
+
self._values_cached = values
|
|
93
|
+
self._boundary_positions = boundary_positions
|
|
94
|
+
|
|
95
|
+
tabular.Tabular.__init__(
|
|
96
|
+
self,
|
|
97
|
+
modality=modality,
|
|
98
|
+
description=description,
|
|
99
|
+
anchor=anchor,
|
|
100
|
+
data=None, # lazy loader below
|
|
101
|
+
datasets=datasets,
|
|
102
|
+
id=id,
|
|
103
|
+
prerelease=prerelease,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def _check_sanity(self):
|
|
107
|
+
# check plausibility of the profile
|
|
108
|
+
assert isinstance(self._depths, (list, np.ndarray)), "Some depths are not valid"
|
|
109
|
+
assert isinstance(self._values, (list, np.ndarray)), "Some values are not valid"
|
|
110
|
+
assert len(self._values) == len(self._depths), "There exist uneven number of depths and values"
|
|
111
|
+
assert all(0 <= d <= 1 for d in self._depths), "Some depths is not between 0 and 1"
|
|
112
|
+
if self.boundaries_mapped:
|
|
113
|
+
assert all(0 <= d <= 1 for d in self.boundary_positions.values()), "Some boundary positions are not between 0 and 1"
|
|
114
|
+
assert all(
|
|
115
|
+
layerpair in self.BOUNDARIES
|
|
116
|
+
for layerpair in self.boundary_positions.keys()
|
|
117
|
+
), "Some value in BOUNDARIES are not mapped in boundary_positions"
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def unit(self) -> str:
|
|
121
|
+
"""Optionally overridden in derived classes."""
|
|
122
|
+
if self._unit is None:
|
|
123
|
+
raise NotImplementedError(f"'unit' not set for {self.__class__.__name__}.")
|
|
124
|
+
return self._unit
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def boundary_positions(self) -> Dict[Tuple[int, int], float]:
|
|
128
|
+
if self._boundary_positions is None:
|
|
129
|
+
return {}
|
|
130
|
+
else:
|
|
131
|
+
return self._boundary_positions
|
|
132
|
+
|
|
133
|
+
def assign_layer(self, depth: float):
|
|
134
|
+
"""Compute the cortical layer for a given depth from the
|
|
135
|
+
layer boundary positions. If no positions are available
|
|
136
|
+
for this profile, return None."""
|
|
137
|
+
assert 0 <= depth <= 1
|
|
138
|
+
if len(self.boundary_positions) == 0:
|
|
139
|
+
return None
|
|
140
|
+
else:
|
|
141
|
+
return max(
|
|
142
|
+
[l2 for (l1, l2), d in self.boundary_positions.items() if d < depth]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def boundaries_mapped(self) -> bool:
|
|
147
|
+
if self.boundary_positions is None:
|
|
148
|
+
return False
|
|
149
|
+
else:
|
|
150
|
+
return all((b in self.boundary_positions) for b in self.BOUNDARIES)
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def _layers(self):
|
|
154
|
+
"""List of layers assigned to each measurments,
|
|
155
|
+
if layer boundaries are available for this features.
|
|
156
|
+
"""
|
|
157
|
+
if self.boundaries_mapped:
|
|
158
|
+
return [self.assign_layer(d) for d in self._depths]
|
|
159
|
+
else:
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def data(self):
|
|
164
|
+
"""Return a pandas Series representing the profile."""
|
|
165
|
+
self._check_sanity()
|
|
166
|
+
iscompound = len(self._values.shape) > 1 and self._values.shape[1] == 2
|
|
167
|
+
if iscompound:
|
|
168
|
+
columns = [f"{self.modality} mean ({self.unit})", "std"]
|
|
169
|
+
else:
|
|
170
|
+
columns = [f"{self.modality} ({self.unit})"]
|
|
171
|
+
return pd.DataFrame(self._values, index=self._depths, columns=columns)
|
|
172
|
+
|
|
173
|
+
@classmethod
|
|
174
|
+
def _merge_elements(
|
|
175
|
+
cls,
|
|
176
|
+
elements: List["CorticalProfile"],
|
|
177
|
+
description: str,
|
|
178
|
+
modality: str,
|
|
179
|
+
anchor: _anchor.AnatomicalAnchor,
|
|
180
|
+
):
|
|
181
|
+
assert all(np.array_equal(elements[0]._depths, f._depths) for f in elements)
|
|
182
|
+
assert len({f.unit for f in elements}) == 1
|
|
183
|
+
values_stacked = np.stack([f._values for f in elements])
|
|
184
|
+
return CorticalProfile(
|
|
185
|
+
description=description,
|
|
186
|
+
modality=modality,
|
|
187
|
+
anchor=anchor,
|
|
188
|
+
depths=np.stack([f._depths for f in elements]).mean(0),
|
|
189
|
+
values=np.stack([values_stacked.mean(0), values_stacked.std(0)]).T,
|
|
190
|
+
unit=elements[0].unit,
|
|
191
|
+
boundary_positions=None,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def plot(self, *args, backend="matplotlib", **kwargs):
|
|
195
|
+
"""
|
|
196
|
+
Plot the profile.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
backend: str
|
|
201
|
+
"matplotlib", "plotly", or others supported by pandas DataFrame
|
|
202
|
+
plotting backend.
|
|
203
|
+
**kwargs
|
|
204
|
+
Keyword arguments are passed on to the plot command. 'layercolor'
|
|
205
|
+
can be used to specify a color for cortical layer shading.
|
|
206
|
+
"""
|
|
207
|
+
wrapwidth = kwargs.pop("textwrap") if "textwrap" in kwargs else 40
|
|
208
|
+
kwargs["title"] = kwargs.get("title", "\n".join(wrap(self.name, wrapwidth)))
|
|
209
|
+
layercolor = kwargs.pop("layercolor", "gray")
|
|
210
|
+
|
|
211
|
+
iscompound = len(self._values.shape) > 1 and self._values.shape[1] == 2
|
|
212
|
+
ymax = max(
|
|
213
|
+
0,
|
|
214
|
+
sum(self._values.max(axis=0)) if iscompound else self._values.max()
|
|
215
|
+
)
|
|
216
|
+
if backend == "matplotlib":
|
|
217
|
+
kwargs["xlabel"] = kwargs.get("xlabel", "Cortical depth")
|
|
218
|
+
kwargs["ylabel"] = kwargs.get("ylabel", self.unit)
|
|
219
|
+
kwargs["grid"] = kwargs.get("grid", True)
|
|
220
|
+
axs = self.data.iloc[:, 0].plot(*args, **kwargs, backend=backend)
|
|
221
|
+
axs.set_ylim(kwargs.get("ylim", (0, ymax)))
|
|
222
|
+
|
|
223
|
+
if self.boundaries_mapped:
|
|
224
|
+
bvals = list(self.boundary_positions.values())
|
|
225
|
+
for i, (d1, d2) in enumerate(list(zip(bvals[:-1], bvals[1:]))):
|
|
226
|
+
axs.text(
|
|
227
|
+
d1 + (d2 - d1) / 2.0,
|
|
228
|
+
10,
|
|
229
|
+
self.LAYERS[i + 1],
|
|
230
|
+
weight="normal",
|
|
231
|
+
ha="center",
|
|
232
|
+
)
|
|
233
|
+
if i % 2 == 0:
|
|
234
|
+
axs.axvspan(d1, d2, color=layercolor, alpha=0.3)
|
|
235
|
+
|
|
236
|
+
axs.set_title(axs.get_title(), fontsize="medium")
|
|
237
|
+
|
|
238
|
+
if iscompound:
|
|
239
|
+
axs.set_ylabel(f"average {kwargs['ylabel']} \u00b1 std")
|
|
240
|
+
av = self.data.values[:, 0]
|
|
241
|
+
std = self.data.values[:, 1]
|
|
242
|
+
axs.fill_between(self.data.index.values, av - std, av + std, alpha=0.5)
|
|
243
|
+
|
|
244
|
+
return axs
|
|
245
|
+
|
|
246
|
+
elif backend == "plotly":
|
|
247
|
+
kwargs["title"] = kwargs["title"].replace("\n", "<br>")
|
|
248
|
+
kwargs["labels"] = {
|
|
249
|
+
"index": kwargs.pop("xlabel", None) or kwargs.pop("index", "Cortical depth"),
|
|
250
|
+
"value": kwargs.pop("ylabel", None) or kwargs.pop("value", self.unit)
|
|
251
|
+
}
|
|
252
|
+
fig = self.data.iloc[:, 0].plot(*args, **kwargs, backend=backend)
|
|
253
|
+
if self.boundaries_mapped:
|
|
254
|
+
bvals = list(self.boundary_positions.values())
|
|
255
|
+
for i, (d1, d2) in enumerate(list(zip(bvals[:-1], bvals[1:]))):
|
|
256
|
+
fig.add_vrect(
|
|
257
|
+
x0=d1, x1=d2, line_width=0, fillcolor=layercolor,
|
|
258
|
+
opacity=0.2 if i % 2 == 0 else 0.0,
|
|
259
|
+
label=dict(text=self.LAYERS[i + 1], textposition="bottom center")
|
|
260
|
+
)
|
|
261
|
+
fig.update_layout(
|
|
262
|
+
showlegend=False,
|
|
263
|
+
yaxis_range=(0, ymax),
|
|
264
|
+
title=dict(
|
|
265
|
+
automargin=True, yref="container", xref="container",
|
|
266
|
+
pad=dict(t=40), xanchor="left", yanchor="top"
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
if iscompound:
|
|
270
|
+
from plotly.graph_objects import Scatter
|
|
271
|
+
x = self.data.index.values
|
|
272
|
+
av = self.data.values[:, 0]
|
|
273
|
+
std = self.data.values[:, 1]
|
|
274
|
+
fig.update_layout(yaxis_title=f"average {kwargs['labels']['value']} ± std")
|
|
275
|
+
fig.add_traces(
|
|
276
|
+
Scatter(
|
|
277
|
+
x=np.concatenate((x, x[::-1])), # x, then x reversed
|
|
278
|
+
y=np.concatenate((av + std, (av - std)[::-1])), # upper, then lower reversed
|
|
279
|
+
fill='toself',
|
|
280
|
+
fillcolor='rgba(0,100,80,0.5)',
|
|
281
|
+
line=dict(color='rgba(255,255,255,0)'),
|
|
282
|
+
hoverinfo="skip",
|
|
283
|
+
showlegend=False
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
return fig
|
|
287
|
+
else:
|
|
288
|
+
return self.data.plot(*args, **kwargs, backend=backend)
|
|
289
|
+
|
|
290
|
+
@property
|
|
291
|
+
def _depths(self):
|
|
292
|
+
"""
|
|
293
|
+
Returns a list of the relative cortical depths of the measured values in the range [0..1].
|
|
294
|
+
|
|
295
|
+
To be implemented in derived class.
|
|
296
|
+
"""
|
|
297
|
+
if self._depths_cached is None:
|
|
298
|
+
raise NotImplementedError(
|
|
299
|
+
f"'_depths' not available for {self.__class__.__name__}."
|
|
300
|
+
)
|
|
301
|
+
return self._depths_cached
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def _values(self):
|
|
305
|
+
"""
|
|
306
|
+
Returns a list of the measured values per depth.
|
|
307
|
+
|
|
308
|
+
To be implemented in derived class.
|
|
309
|
+
"""
|
|
310
|
+
if self._values_cached is None:
|
|
311
|
+
raise NotImplementedError(
|
|
312
|
+
f"'_values' not available for {self.__class__.__name__}."
|
|
313
|
+
)
|
|
314
|
+
return self._values_cached
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def name(self):
|
|
318
|
+
if hasattr(self, "receptor"):
|
|
319
|
+
return super().name + f": {self.receptor}"
|
|
320
|
+
if hasattr(self, "location"):
|
|
321
|
+
return super().name + f": {self.location.coordinate}"
|
|
322
|
+
return super().name
|
|
@@ -0,0 +1,257 @@
|
|
|
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
|
+
|
|
16
|
+
from .. import anchor as _anchor
|
|
17
|
+
from . import tabular
|
|
18
|
+
from ...retrieval.datasets import GenericDataset
|
|
19
|
+
|
|
20
|
+
import pandas as pd
|
|
21
|
+
from textwrap import wrap
|
|
22
|
+
from typing import List
|
|
23
|
+
try:
|
|
24
|
+
from typing import TypedDict
|
|
25
|
+
except ImportError:
|
|
26
|
+
from typing_extensions import TypedDict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class GeneExpressions(
|
|
30
|
+
tabular.Tabular,
|
|
31
|
+
category='molecular'
|
|
32
|
+
):
|
|
33
|
+
"""
|
|
34
|
+
A set gene expressions for different candidate genes
|
|
35
|
+
measured inside a brain structure.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
DESCRIPTION = """
|
|
39
|
+
Gene expressions extracted from microarray data in the Allen Atlas.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
ALLEN_ATLAS_NOTIFICATION = """
|
|
43
|
+
For retrieving microarray data, siibra connects to the web API of
|
|
44
|
+
the Allen Brain Atlas (© 2015 Allen Institute for Brain Science),
|
|
45
|
+
available from https://brain-map.org/api/index.html. Any use of the
|
|
46
|
+
microarray data needs to be in accordance with their terms of use,
|
|
47
|
+
as specified at https://alleninstitute.org/legal/terms-use/.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
DATASET = GenericDataset(
|
|
51
|
+
name="An anatomically comprehensive atlas of the adult human brain transcriptome",
|
|
52
|
+
contributors=[
|
|
53
|
+
'Michael J. Hawrylycz',
|
|
54
|
+
'Ed S. Lein',
|
|
55
|
+
'Angela L. Guillozet-Bongaarts',
|
|
56
|
+
'Elaine H. Shen',
|
|
57
|
+
'Lydia Ng',
|
|
58
|
+
'Jeremy A. Miller',
|
|
59
|
+
'Louie N. van de Lagemaat',
|
|
60
|
+
'Kimberly A. Smith',
|
|
61
|
+
'Amanda Ebbert',
|
|
62
|
+
'Zackery L. Riley',
|
|
63
|
+
'Chris Abajian',
|
|
64
|
+
'Christian F. Beckmann',
|
|
65
|
+
'Amy Bernard',
|
|
66
|
+
'Darren Bertagnolli',
|
|
67
|
+
'Andrew F. Boe',
|
|
68
|
+
'Preston M. Cartagena',
|
|
69
|
+
'M. Mallar Chakravarty',
|
|
70
|
+
'Mike Chapin',
|
|
71
|
+
'Jimmy Chong',
|
|
72
|
+
'Rachel A. Dalley',
|
|
73
|
+
'Barry David Daly',
|
|
74
|
+
'Chinh Dang',
|
|
75
|
+
'Suvro Datta',
|
|
76
|
+
'Nick Dee',
|
|
77
|
+
'Tim A. Dolbeare',
|
|
78
|
+
'Vance Faber',
|
|
79
|
+
'David Feng',
|
|
80
|
+
'David R. Fowler',
|
|
81
|
+
'Jeff Goldy',
|
|
82
|
+
'Benjamin W. Gregor',
|
|
83
|
+
'Zeb Haradon',
|
|
84
|
+
'David R. Haynor',
|
|
85
|
+
'John G. Hohmann',
|
|
86
|
+
'Steve Horvath',
|
|
87
|
+
'Robert E. Howard',
|
|
88
|
+
'Andreas Jeromin',
|
|
89
|
+
'Jayson M. Jochim',
|
|
90
|
+
'Marty Kinnunen',
|
|
91
|
+
'Christopher Lau',
|
|
92
|
+
'Evan T. Lazarz',
|
|
93
|
+
'Changkyu Lee',
|
|
94
|
+
'Tracy A. Lemon',
|
|
95
|
+
'Ling Li',
|
|
96
|
+
'Yang Li',
|
|
97
|
+
'John A. Morris',
|
|
98
|
+
'Caroline C. Overly',
|
|
99
|
+
'Patrick D. Parker',
|
|
100
|
+
'Sheana E. Parry',
|
|
101
|
+
'Melissa Reding',
|
|
102
|
+
'Joshua J. Royall',
|
|
103
|
+
'Jay Schulkin',
|
|
104
|
+
'Pedro Adolfo Sequeira',
|
|
105
|
+
'Clifford R. Slaughterbeck',
|
|
106
|
+
'Simon C. Smith',
|
|
107
|
+
'Andy J. Sodt',
|
|
108
|
+
'Susan M. Sunkin',
|
|
109
|
+
'Beryl E. Swanson',
|
|
110
|
+
'Marquis P. Vawter',
|
|
111
|
+
'Derric Williams',
|
|
112
|
+
'Paul Wohnoutka',
|
|
113
|
+
'H. Ronald Zielke',
|
|
114
|
+
'Daniel H. Geschwind',
|
|
115
|
+
'Patrick R. Hof',
|
|
116
|
+
'Stephen M. Smith',
|
|
117
|
+
'Christof Koch',
|
|
118
|
+
'Seth G. N. Grant',
|
|
119
|
+
'Allan R. Jones'
|
|
120
|
+
],
|
|
121
|
+
url="https://doi.org/10.1038%2Fnature11405",
|
|
122
|
+
description='Neuroanatomically precise, genome-wide maps of transcript '
|
|
123
|
+
'distributions are critical resources to complement genomic '
|
|
124
|
+
'sequence data and to correlate functional and genetic brain '
|
|
125
|
+
'architecture. Here we describe the generation and analysis '
|
|
126
|
+
'of a transcriptional atlas of the adult human brain, '
|
|
127
|
+
'comprising extensive histological analysis and comprehensive '
|
|
128
|
+
'microarray profiling of ~900 neuroanatomically precise '
|
|
129
|
+
'subdivisions in two individuals. Transcriptional regulation '
|
|
130
|
+
'varies enormously by anatomical location, with different '
|
|
131
|
+
'regions and their constituent cell types displaying robust '
|
|
132
|
+
'molecular signatures that are highly conserved between '
|
|
133
|
+
'individuals. Analysis of differential gene expression and '
|
|
134
|
+
'gene co-expression relationships demonstrates that brain-'
|
|
135
|
+
'wide variation strongly reflects the distributions of major '
|
|
136
|
+
'cell classes such as neurons, oligodendrocytes, astrocytes '
|
|
137
|
+
'and microglia. Local neighbourhood relationships between '
|
|
138
|
+
'fine anatomical subdivisions are associated with discrete '
|
|
139
|
+
'neuronal subtypes and genes involved with synaptic '
|
|
140
|
+
'transmission. The neocortex displays a relatively '
|
|
141
|
+
'homogeneous transcriptional pattern, but with distinct '
|
|
142
|
+
'features associated selectively with primary sensorimotor '
|
|
143
|
+
'cortices and with enriched frontal lobe expression. Notably, '
|
|
144
|
+
'the spatial topography of the neocortex is strongly '
|
|
145
|
+
'reflected in its molecular topography— the closer two '
|
|
146
|
+
'cortical regions, the more similar their transcriptomes. '
|
|
147
|
+
'This freely accessible online data resource forms a high-'
|
|
148
|
+
'resolution transcriptional baseline for neurogenetic studies '
|
|
149
|
+
'of normal and abnormal human brain function.'
|
|
150
|
+
""
|
|
151
|
+
"For retrieving microarray data, siibra connects to the web API of "
|
|
152
|
+
"the Allen Brain Atlas (© 2015 Allen Institute for Brain Science), "
|
|
153
|
+
"available from https://brain-map.org/api/index.html. Any use of the "
|
|
154
|
+
"microarray data needs to be in accordance with their terms of use, "
|
|
155
|
+
"as specified at https://alleninstitute.org/legal/terms-use/."
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
class _DonorDict(TypedDict):
|
|
159
|
+
id: int
|
|
160
|
+
name: str
|
|
161
|
+
race: str
|
|
162
|
+
age: int
|
|
163
|
+
gender: str
|
|
164
|
+
|
|
165
|
+
class _SampleStructure(TypedDict):
|
|
166
|
+
id: int
|
|
167
|
+
name: str
|
|
168
|
+
abbreviation: str
|
|
169
|
+
color: str
|
|
170
|
+
|
|
171
|
+
def __init__(
|
|
172
|
+
self,
|
|
173
|
+
levels: List[float],
|
|
174
|
+
z_scores: List[float],
|
|
175
|
+
genes: List[str],
|
|
176
|
+
additional_columns: dict,
|
|
177
|
+
anchor: _anchor.AnatomicalAnchor,
|
|
178
|
+
datasets: List = [DATASET]
|
|
179
|
+
):
|
|
180
|
+
"""
|
|
181
|
+
Construct gene expression table.
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
levels : list of float
|
|
186
|
+
Expression levels measured
|
|
187
|
+
z_scores : list of float
|
|
188
|
+
corresponding z scores measured
|
|
189
|
+
genes : list of str
|
|
190
|
+
Name of the gene corresponding to each measurement
|
|
191
|
+
additional_columns : dict of list
|
|
192
|
+
columns with additional data to be added to the tabular feature.
|
|
193
|
+
Keys give column names, values are lists with the column data.
|
|
194
|
+
Each list given needs to have the same length as expression_levels
|
|
195
|
+
anchor: AnatomicalAnchor
|
|
196
|
+
datasets : list
|
|
197
|
+
list of datasets corresponding to this feature
|
|
198
|
+
"""
|
|
199
|
+
assert len(z_scores) == len(levels)
|
|
200
|
+
assert len(genes) == len(levels)
|
|
201
|
+
if additional_columns is not None:
|
|
202
|
+
assert all(len(lst) == len(levels) for lst in additional_columns.values())
|
|
203
|
+
else:
|
|
204
|
+
additional_columns = {}
|
|
205
|
+
|
|
206
|
+
_data_cahced = pd.DataFrame(
|
|
207
|
+
dict(
|
|
208
|
+
**{'level': levels, 'zscore': z_scores, 'gene': genes},
|
|
209
|
+
**additional_columns
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
# data.index.name = 'probe_id'
|
|
213
|
+
tabular.Tabular.__init__(
|
|
214
|
+
self,
|
|
215
|
+
description=(
|
|
216
|
+
(self.DESCRIPTION + self.ALLEN_ATLAS_NOTIFICATION)
|
|
217
|
+
.replace('\n', ' ')
|
|
218
|
+
.replace('\t', '')
|
|
219
|
+
.strip()
|
|
220
|
+
),
|
|
221
|
+
modality="Gene expression",
|
|
222
|
+
anchor=anchor,
|
|
223
|
+
data=_data_cahced,
|
|
224
|
+
datasets=datasets
|
|
225
|
+
)
|
|
226
|
+
self.unit = "expression level"
|
|
227
|
+
|
|
228
|
+
def plot(self, *args, backend="matplotlib", **kwargs):
|
|
229
|
+
"""
|
|
230
|
+
Create a box plot per gene.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
backend: str
|
|
235
|
+
"matplotlib", "plotly", or others supported by pandas DataFrame
|
|
236
|
+
plotting backend.
|
|
237
|
+
**kwargs
|
|
238
|
+
Keyword arguments are passed on to the plot command.
|
|
239
|
+
"""
|
|
240
|
+
wrapwidth = kwargs.pop("textwrap") if "textwrap" in kwargs else 40
|
|
241
|
+
kwargs["title"] = kwargs.pop("title", None) \
|
|
242
|
+
or "\n".join(wrap(f"{self.modality} measured in {self.anchor._regionspec or self.anchor.location}", wrapwidth))
|
|
243
|
+
kwargs["kind"] = "box"
|
|
244
|
+
if backend == "matplotlib":
|
|
245
|
+
for arg in ['yerr', 'y', 'ylabel', 'xlabel', 'width']:
|
|
246
|
+
assert arg not in kwargs
|
|
247
|
+
default_kwargs = {
|
|
248
|
+
"grid": True, "legend": False, 'by': "gene",
|
|
249
|
+
'column': ['level'], 'showfliers': False, 'ax': None,
|
|
250
|
+
'ylabel': 'expression level'
|
|
251
|
+
}
|
|
252
|
+
return self.data.plot(*args, **{**default_kwargs, **kwargs}, backend=backend)
|
|
253
|
+
elif backend == "plotly":
|
|
254
|
+
kwargs["title"] = kwargs["title"].replace('\n', "<br>")
|
|
255
|
+
return self.data.plot(y='level', x='gene', backend=backend, **kwargs)
|
|
256
|
+
else:
|
|
257
|
+
return self.data.plot(*args, backend=backend, **kwargs)
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
|
|
16
|
+
from . import cortical_profile
|
|
17
|
+
from . import tabular
|
|
18
|
+
|
|
19
|
+
import pandas as pd
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from ...features.anchor import AnatomicalAnchor
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LayerwiseBigBrainIntensities(
|
|
28
|
+
tabular.Tabular,
|
|
29
|
+
category='cellular'
|
|
30
|
+
):
|
|
31
|
+
|
|
32
|
+
DESCRIPTION = (
|
|
33
|
+
"Layerwise averages and standard deviations of of BigBrain staining intensities "
|
|
34
|
+
"computed by Konrad Wagstyl, as described in the publication "
|
|
35
|
+
"'Wagstyl, K., et al (2020). BigBrain 3D atlas of "
|
|
36
|
+
"cortical layers: Cortical and laminar thickness gradients diverge in sensory and "
|
|
37
|
+
"motor cortices. PLoS Biology, 18(4), e3000678. "
|
|
38
|
+
"http://dx.doi.org/10.1371/journal.pbio.3000678."
|
|
39
|
+
"The data is taken from the tutorial at "
|
|
40
|
+
"https://github.com/kwagstyl/cortical_layers_tutorial. Each vertex is "
|
|
41
|
+
"assigned to the regional map when queried."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
anchor: "AnatomicalAnchor",
|
|
47
|
+
means: list,
|
|
48
|
+
stds: list,
|
|
49
|
+
):
|
|
50
|
+
data = pd.DataFrame(
|
|
51
|
+
np.array([means, stds]).T,
|
|
52
|
+
columns=['mean', 'std'],
|
|
53
|
+
index=list(cortical_profile.CorticalProfile.LAYERS.values())[1: -1]
|
|
54
|
+
)
|
|
55
|
+
data.index.name = "layer"
|
|
56
|
+
tabular.Tabular.__init__(
|
|
57
|
+
self,
|
|
58
|
+
description=self.DESCRIPTION,
|
|
59
|
+
modality="Modified silver staining",
|
|
60
|
+
anchor=anchor,
|
|
61
|
+
data=data
|
|
62
|
+
)
|