siibra 0.4a77__py3-none-any.whl → 0.5a1__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.

@@ -14,19 +14,21 @@
14
14
  # limitations under the License.
15
15
 
16
16
  from zipfile import ZipFile
17
- from ..feature import Feature
17
+ from ..feature import Feature, Compoundable
18
18
  from ..tabular.tabular import Tabular
19
19
 
20
20
  from .. import anchor as _anchor
21
21
 
22
- from ...commons import logger, QUIET, siibra_tqdm
22
+ from ...commons import logger, QUIET
23
23
  from ...core import region as _region
24
24
  from ...locations import pointset
25
25
  from ...retrieval.repositories import RepositoryConnector
26
+ from ...retrieval.requests import HttpRequest
27
+
26
28
 
27
29
  import pandas as pd
28
30
  import numpy as np
29
- from typing import Callable, Dict, Union, List
31
+ from typing import Callable, Union, List
30
32
 
31
33
  try:
32
34
  from typing import Literal
@@ -34,12 +36,15 @@ except ImportError: # support python 3.7
34
36
  from typing_extensions import Literal
35
37
 
36
38
 
37
- class RegionalConnectivity(Feature):
39
+ class RegionalConnectivity(Feature, Compoundable):
38
40
  """
39
41
  Parcellation-averaged connectivity, providing one or more matrices of a
40
42
  given modality for a given parcellation.
41
43
  """
42
44
 
45
+ _filter_attrs = ["modality", "cohort", "subject"]
46
+ _compound_attrs = ["modality", "cohort"]
47
+
43
48
  def __init__(
44
49
  self,
45
50
  cohort: str,
@@ -47,10 +52,12 @@ class RegionalConnectivity(Feature):
47
52
  regions: list,
48
53
  connector: RepositoryConnector,
49
54
  decode_func: Callable,
50
- files: Dict[str, str],
55
+ filename: str,
51
56
  anchor: _anchor.AnatomicalAnchor,
52
57
  description: str = "",
53
58
  datasets: list = [],
59
+ subject: str = "average",
60
+ feature: str = None
54
61
  ):
55
62
  """
56
63
  Construct a parcellation-averaged connectivity matrix.
@@ -81,71 +88,52 @@ class RegionalConnectivity(Feature):
81
88
  Feature.__init__(
82
89
  self,
83
90
  modality=modality,
84
- description=description or '\n'.join({ds.description for ds in datasets}),
91
+ description=description,
85
92
  anchor=anchor,
86
93
  datasets=datasets,
87
94
  )
88
95
  self.cohort = cohort.upper()
89
- self._connector = connector
90
- self._files = files
96
+ if isinstance(connector, str) and connector:
97
+ self._connector = HttpRequest(connector, decode_func)
98
+ else:
99
+ self._connector = connector
100
+ self._filename = filename
91
101
  self._decode_func = decode_func
92
102
  self.regions = regions
93
- self._matrices = {}
103
+ self._matrix = None
104
+ self._subject = subject
105
+ self._feature = feature
94
106
 
95
107
  @property
96
- def subjects(self):
97
- """
98
- Returns the subject identifiers for which matrices are available.
99
- """
100
- return list(self._files.keys())
108
+ def subject(self):
109
+ """Returns the subject identifiers for which the matrix represents."""
110
+ return self._subject
111
+
112
+ @property
113
+ def feature(self):
114
+ """If applicable, returns the type of feature for which the matrix represents."""
115
+ return self._feature
101
116
 
102
117
  @property
103
118
  def name(self):
104
- supername = super().name
105
- return f"{supername} with cohort {self.cohort}"
119
+ return f"{super().name} with cohort {self.cohort} - {self.feature or self.subject}"
106
120
 
107
- def get_matrix(self, subject: str = None):
121
+ @property
122
+ def data(self) -> pd.DataFrame:
108
123
  """
109
124
  Returns a matrix as a pandas dataframe.
110
125
 
111
- Parameters
112
- ----------
113
- subject: str, default: None
114
- Name of the subject (see ConnectivityMatrix.subjects for available names).
115
- If None, the mean is taken in case of multiple available matrices.
116
126
  Returns
117
127
  -------
118
128
  pd.DataFrame
119
129
  A square matrix with region names as the column and row names.
120
130
  """
121
- assert len(self) > 0
122
- if (subject is None) and (len(self) > 1):
123
- # multiple matrices available, but no subject given - return mean matrix
124
- logger.info(
125
- f"No subject name supplied, returning mean connectivity across {len(self)} subjects. "
126
- "You might alternatively specify an individual subject."
127
- )
128
- if "mean" not in self._matrices:
129
- all_arrays = [
130
- self._connector.get(fname, decode_func=self._decode_func)
131
- for fname in siibra_tqdm(
132
- self._files.values(),
133
- total=len(self),
134
- desc=f"Averaging {len(self)} connectivity matrices"
135
- )
136
- ]
137
- self._matrices['mean'] = self._arraylike_to_dataframe(np.stack(all_arrays).mean(0))
138
- return self._matrices['mean'].copy()
139
- if subject is None:
140
- subject = next(iter(self._files.keys()))
141
- if subject not in self._files:
142
- raise ValueError(f"Subject name '{subject}' not known, use one of: {', '.join(self._files)}")
143
- if subject not in self._matrices:
144
- self._matrices[subject] = self._load_matrix(subject)
145
- return self._matrices[subject].copy()
146
-
147
- def plot_matrix(
148
- self, subject: str = None, regions: List[str] = None,
131
+ if self._matrix is None:
132
+ self._load_matrix()
133
+ return self._matrix.copy()
134
+
135
+ def _plot_matrix(
136
+ self, regions: List[str] = None,
149
137
  logscale: bool = False, *args, backend="nilearn", **kwargs
150
138
  ):
151
139
  """
@@ -153,10 +141,6 @@ class RegionalConnectivity(Feature):
153
141
 
154
142
  Parameters
155
143
  ----------
156
- subject: str
157
- Name of the subject (see ConnectivityMatrix.subjects for available names).
158
- If "mean" or None is given, the mean is taken in case of multiple
159
- available matrices.
160
144
  regions: list[str]
161
145
  Display the matrix only for selected regions. By default, shows all the regions.
162
146
  It can only be a subset of regions of the feature.
@@ -171,16 +155,19 @@ class RegionalConnectivity(Feature):
171
155
  if regions is None:
172
156
  regions = self.regions
173
157
  indices = [self.regions.index(r) for r in regions]
174
- matrix = self.get_matrix(subject=subject).iloc[indices, indices].to_numpy() # nilearn.plotting.plot_matrix works better with a numpy array
158
+ matrix = self.data.iloc[indices, indices].to_numpy() # nilearn.plotting.plot_matrix works better with a numpy array
175
159
 
176
160
  if logscale:
177
161
  matrix = np.log10(matrix)
178
162
 
179
163
  # default kwargs
180
- subject_title = subject or ""
181
164
  kwargs["title"] = kwargs.get(
182
165
  "title",
183
- f"{subject_title} - {self.modality} in {', '.join({_.name for _ in self.anchor.regions})}"
166
+ "".join([
167
+ f"{self.feature if self.feature else ''} - {self.subject} - ",
168
+ f"{self.modality} in ",
169
+ f"{', '.join({_.name for _ in self.anchor.regions})}"
170
+ ])
184
171
  )
185
172
 
186
173
  if kwargs.get("reorder") or (backend == "nilearn"):
@@ -199,19 +186,16 @@ class RegionalConnectivity(Feature):
199
186
  f"Plotting connectivity matrices with {backend} is not supported."
200
187
  )
201
188
 
202
- def __iter__(self):
203
- return ((sid, self.get_matrix(sid)) for sid in self._files)
204
-
205
189
  def _export(self, fh: ZipFile):
206
190
  super()._export(fh)
207
- for sub in self.subjects:
208
- df = self.get_matrix(sub)
209
- fh.writestr(f"sub/{sub}/matrix.csv", df.to_csv())
191
+ if self.feature is None:
192
+ fh.writestr(f"sub/{self._filename}/matrix.csv", self.data.to_csv())
193
+ else:
194
+ fh.writestr(f"feature/{self._filename}/matrix.csv", self.data.to_csv())
210
195
 
211
196
  def get_profile(
212
197
  self,
213
198
  region: Union[str, _region.Region],
214
- subject: str = None,
215
199
  min_connectivity: float = 0,
216
200
  max_rows: int = None,
217
201
  direction: Literal['column', 'row'] = 'column'
@@ -233,7 +217,7 @@ class RegionalConnectivity(Feature):
233
217
  Choose the direction of profile extraction particularly for
234
218
  non-symmetric matrices. ('column' or 'row')
235
219
  """
236
- matrix = self.get_matrix(subject)
220
+ matrix = self.data
237
221
  if direction.lower() not in ['column', 'row']:
238
222
  raise ValueError("Direction can only be 'column' or 'row'")
239
223
  if direction.lower() == 'row':
@@ -252,9 +236,7 @@ class RegionalConnectivity(Feature):
252
236
  elif len(regions) > 1:
253
237
  raise ValueError(f"Region specification {region} matched more than one profile: {regions}")
254
238
  else:
255
- name = \
256
- f"Averaged {self.modality}" if subject is None \
257
- else f"{self.modality}"
239
+ name = self.modality
258
240
  series = matrix[regions[0]]
259
241
  last_index = len(series) - 1 if max_rows is None \
260
242
  else min(max_rows, len(series) - 1)
@@ -275,10 +257,9 @@ class RegionalConnectivity(Feature):
275
257
  datasets=self.datasets
276
258
  )
277
259
 
278
- def plot_profile(
260
+ def plot(
279
261
  self,
280
- region: Union[str, _region.Region],
281
- subject: str = None,
262
+ regions: Union[str, _region.Region, List[Union[str, _region.Region]]] = None,
282
263
  min_connectivity: float = 0,
283
264
  max_rows: int = None,
284
265
  direction: Literal['column', 'row'] = 'column',
@@ -287,7 +268,32 @@ class RegionalConnectivity(Feature):
287
268
  backend="matplotlib",
288
269
  **kwargs
289
270
  ):
290
- profile = self.get_profile(region, subject, min_connectivity, max_rows, direction)
271
+ """
272
+ Parameters
273
+ ----------
274
+ regions: Union[str, _region.Region], None
275
+ If None, returns the full connectivity matrix.
276
+ If a region is provided, returns the profile for that region.
277
+ If list of regions is provided, returns the matrix for the selected
278
+ regions.
279
+ min_connectivity: float, default 0
280
+ Only for region profile.
281
+ max_rows: int, default None
282
+ Only for region profile.
283
+ direction: 'column' or 'row', default: 'column'
284
+ Only for matrix.
285
+ logscale: bool, default: False
286
+ backend: str, default: "matplotlib" for profiles and "nilearn" for matrices
287
+ """
288
+ if regions is None or isinstance(regions, list):
289
+ plot_matrix_backend = "nilearn" if backend == "matplotlib" else backend
290
+ return self._plot_matrix(
291
+ regions=regions, logscale=logscale, *args,
292
+ backend=plot_matrix_backend,
293
+ **kwargs
294
+ )
295
+
296
+ profile = self.get_profile(regions, min_connectivity, max_rows, direction)
291
297
  kwargs["kind"] = kwargs.get("kind", "barh")
292
298
  if backend == "matplotlib":
293
299
  kwargs["logx"] = kwargs.get("logx", logscale)
@@ -299,7 +305,7 @@ class RegionalConnectivity(Feature):
299
305
  "log_x": logscale,
300
306
  "labels": {"y": " ", "x": ""},
301
307
  "color_continuous_scale": "jet",
302
- "width": 600, "height": 3800
308
+ "width": 600, "height": 15 * len(profile.data)
303
309
  })
304
310
  fig = profile.data.plot(*args, backend=backend, **kwargs)
305
311
  fig.update_layout({
@@ -311,18 +317,14 @@ class RegionalConnectivity(Feature):
311
317
  "margin": dict(l=0, r=0, b=0, t=0, pad=0)
312
318
  })
313
319
  return fig
314
- return profile.plot(*args, backend=backend, **kwargs)
320
+ else:
321
+ return profile.data.plot(*args, backend=backend, **kwargs)
315
322
 
316
323
  def __len__(self):
317
- return len(self._files)
324
+ return len(self._filename)
318
325
 
319
326
  def __str__(self):
320
- return "{} connectivity for {} from {} cohort ({} matrices)".format(
321
- self.paradigm if hasattr(self, "paradigm") else self.modality,
322
- "_".join(p.name for p in self.anchor.parcellations),
323
- self.cohort,
324
- len(self._files),
325
- )
327
+ return self.name
326
328
 
327
329
  def compute_centroids(self, space):
328
330
  """
@@ -362,6 +364,7 @@ class RegionalConnectivity(Feature):
362
364
  """
363
365
  if not isinstance(array, np.ndarray):
364
366
  array = array.to_numpy()
367
+ assert array.shape[0] == array.shape[1], f"Connectivity matrices must be square but found {array.shape}"
365
368
  if not (array == array.T).all():
366
369
  logger.warning("The connectivity matrix is not symmetric.")
367
370
  df = pd.DataFrame(array)
@@ -385,16 +388,18 @@ class RegionalConnectivity(Feature):
385
388
  raise RuntimeError("Could not decode connectivity matrix regions.")
386
389
  return df
387
390
 
388
- def _load_matrix(self, subject: str):
391
+ def _load_matrix(self):
389
392
  """
390
393
  Extract connectivity matrix.
391
394
  """
392
- assert subject in self.subjects
393
- array = self._connector.get(self._files[subject], decode_func=self._decode_func)
395
+ if isinstance(self._connector, HttpRequest):
396
+ array = self._connector.data
397
+ else:
398
+ array = self._connector.get(self._filename, decode_func=self._decode_func)
394
399
  nrows = array.shape[0]
395
400
  if array.shape[1] != nrows:
396
401
  raise RuntimeError(
397
402
  f"Non-quadratic connectivity matrix {nrows}x{array.shape[1]} "
398
- f"from {self._files[subject]} in {str(self._connector)}"
403
+ f"from {self._filename} in {str(self._connector)}"
399
404
  )
400
- return self._arraylike_to_dataframe(array)
405
+ self._matrix = self._arraylike_to_dataframe(array)
@@ -71,7 +71,7 @@ class EbrainsDataFeature(feature.Feature, category="other"):
71
71
 
72
72
  @property
73
73
  def version_history(self):
74
- return self._dataset.version_changes
74
+ return self._dataset.version_changelog
75
75
 
76
76
  @property
77
77
  def url(self):