junifer 0.0.7.dev105__py3-none-any.whl → 0.0.7.dev111__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.
junifer/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.0.7.dev105'
21
- __version_tuple__ = version_tuple = (0, 0, 7, 'dev105')
20
+ __version__ = version = '0.0.7.dev111'
21
+ __version_tuple__ = version_tuple = (0, 0, 7, 'dev111')
@@ -5,6 +5,7 @@
5
5
 
6
6
  from typing import Optional
7
7
 
8
+ import numpy as np
8
9
  import pandas as pd
9
10
 
10
11
  from ..typing import StorageLike
@@ -19,6 +20,7 @@ def read_transform(
19
20
  transform: str,
20
21
  feature_name: Optional[str] = None,
21
22
  feature_md5: Optional[str] = None,
23
+ nan_policy: Optional[str] = "bypass",
22
24
  transform_args: Optional[tuple] = None,
23
25
  transform_kw_args: Optional[dict] = None,
24
26
  ) -> pd.DataFrame:
@@ -35,6 +37,16 @@ def read_transform(
35
37
  Name of the feature to read (default None).
36
38
  feature_md5 : str, optional
37
39
  MD5 hash of the feature to read (default None).
40
+ nan_policy : str, optional
41
+ The policy to handle NaN values (default "ignore").
42
+ Options are:
43
+
44
+ * "bypass": Do nothing and pass NaN values to the transform function.
45
+ * "drop_element": Drop (skip) elements with NaN values.
46
+ * "drop_rows": Drop (skip) rows with NaN values.
47
+ * "drop_columns": Drop (skip) columns with NaN values.
48
+ * "drop_symmetric": Drop (skip) symmetric pairs with NaN values.
49
+
38
50
  transform_args : tuple, optional
39
51
  The positional arguments for the callable of ``transform``
40
52
  (default None).
@@ -47,6 +59,18 @@ def read_transform(
47
59
  pandas.DataFrame
48
60
  The transformed feature as a dataframe.
49
61
 
62
+ Raises
63
+ ------
64
+ ValueError
65
+ If ``nan_policy`` is invalid or
66
+ if *package* is invalid.
67
+ RuntimeError
68
+ If *package* is ``bctpy`` and stored data kind is not ``matrix``.
69
+ ImportError
70
+ If ``bctpy`` cannot be imported.
71
+ AttributeError
72
+ If *function* to be invoked in invalid.
73
+
50
74
  Notes
51
75
  -----
52
76
  This function has been only tested for:
@@ -63,6 +87,18 @@ def read_transform(
63
87
  transform_args = transform_args or ()
64
88
  transform_kw_args = transform_kw_args or {}
65
89
 
90
+ if nan_policy not in [
91
+ "bypass",
92
+ "drop_element",
93
+ "drop_rows",
94
+ "drop_columns",
95
+ "drop_symmetric",
96
+ ]:
97
+ raise_error(
98
+ f"Unknown nan_policy: {nan_policy}",
99
+ klass=ValueError,
100
+ )
101
+
66
102
  # Read storage
67
103
  stored_data = storage.read(
68
104
  feature_name=feature_name, feature_md5=feature_md5
@@ -106,22 +142,52 @@ def read_transform(
106
142
  except AttributeError as err:
107
143
  raise_error(msg=str(err), klass=AttributeError)
108
144
 
109
- # Apply function and store subject-wise
145
+ # Apply function and store element-wise
110
146
  output_list = []
147
+ element_list = []
111
148
  logger.debug(
112
149
  f"Computing '{package}.{func_str}' for feature "
113
150
  f"{feature_name or feature_md5} ..."
114
151
  )
115
- for subject in range(stored_data["data"].shape[2]):
152
+ for i_element, element in enumerate(stored_data["element"]):
153
+ t_data = stored_data["data"][:, :, i_element]
154
+ has_nan = np.isnan(np.min(t_data))
155
+ if nan_policy == "drop_element" and has_nan:
156
+ logger.debug(
157
+ f"Skipping element {element} due to NaN values ..."
158
+ )
159
+ continue
160
+ elif nan_policy == "drop_rows" and has_nan:
161
+ logger.debug(
162
+ f"Skipping rows with NaN values in element {element} ..."
163
+ )
164
+ t_data = t_data[~np.isnan(t_data).any(axis=1)]
165
+ elif nan_policy == "drop_columns" and has_nan:
166
+ logger.debug(
167
+ f"Skipping columns with NaN values in element {element} "
168
+ "..."
169
+ )
170
+ t_data = t_data[:, ~np.isnan(t_data).any(axis=0)]
171
+ elif nan_policy == "drop_symmetric":
172
+ logger.debug(
173
+ f"Skipping pairs of rows/columns with NaN values in "
174
+ f"element {element}..."
175
+ )
176
+ good_rows = ~np.isnan(t_data).any(axis=1)
177
+ good_columns = ~np.isnan(t_data).any(axis=0)
178
+ good_idx = np.logical_and(good_rows, good_columns)
179
+ t_data = t_data[good_idx][:, good_idx]
180
+
116
181
  output = func(
117
- stored_data["data"][:, :, subject],
182
+ t_data,
118
183
  *transform_args,
119
184
  **transform_kw_args,
120
185
  )
121
186
  output_list.append(output)
187
+ element_list.append(element)
122
188
 
123
189
  # Create dataframe for index
124
- idx_df = pd.DataFrame(data=stored_data["element"])
190
+ idx_df = pd.DataFrame(data=element_list)
125
191
  # Create multiindex from dataframe
126
192
  logger.debug(
127
193
  "Generating pandas.MultiIndex for feature "
@@ -64,6 +64,36 @@ def matrix_storage(tmp_path: Path) -> HDF5FeatureStorage:
64
64
  return storage
65
65
 
66
66
 
67
+ @pytest.fixture
68
+ def matrix_storage_with_nan(tmp_path: Path) -> HDF5FeatureStorage:
69
+ """Return a HDF5FeatureStorage with matrix data.
70
+
71
+ Parameters
72
+ ----------
73
+ tmp_path : pathlib.Path
74
+ The path to the test directory.
75
+
76
+ """
77
+ storage = HDF5FeatureStorage(tmp_path / "matrix_store_nan.hdf5")
78
+ data = np.arange(36).reshape(3, 3, 4).astype(float)
79
+ data[1, 1, 2] = np.nan
80
+ data[1, 2, 2] = np.nan
81
+ for i in range(4):
82
+ storage.store(
83
+ kind="matrix",
84
+ meta={
85
+ "element": {"subject": f"test{i + 1}"},
86
+ "dependencies": [],
87
+ "marker": {"name": "matrix"},
88
+ "type": "BOLD",
89
+ },
90
+ data=data[:, :, i],
91
+ col_names=["f1", "f2", "f3"],
92
+ row_names=["g1", "g2", "g3"],
93
+ )
94
+ return storage
95
+
96
+
67
97
  def test_incorrect_package(matrix_storage: HDF5FeatureStorage) -> None:
68
98
  """Test error check for incorrect package name.
69
99
 
@@ -176,3 +206,57 @@ def test_bctpy_function(
176
206
  )
177
207
  assert "Computing" in caplog.text
178
208
  assert "Generating" in caplog.text
209
+
210
+
211
+ @pytest.mark.parametrize(
212
+ "nan_policy, error_msg",
213
+ [
214
+ ("drop_element", None),
215
+ ("drop_rows", "square"),
216
+ ("drop_columns", "square"),
217
+ ("drop_symmetric", None),
218
+ ("bypass", "NaNs"),
219
+ ("wrong", "Unknown"),
220
+ ],
221
+ )
222
+ def test_bctpy_nans(
223
+ matrix_storage_with_nan: HDF5FeatureStorage,
224
+ caplog: pytest.LogCaptureFixture,
225
+ nan_policy: str,
226
+ error_msg: str,
227
+ ) -> None:
228
+ """Test working function of bctpy.
229
+
230
+ Parameters
231
+ ----------
232
+ matrix_storage_with_nan : HDF5FeatureStorage
233
+ The HDF5FeatureStorage with matrix data, as fixture.
234
+ caplog : pytest.LogCaptureFixture
235
+ The pytest.LogCaptureFixture object.
236
+ nan_policy : str
237
+ The NAN policy to test.
238
+ error_msg : str
239
+ The expected error message snippet. If None, no error should be raised.
240
+
241
+ """
242
+ # Skip test if import fails
243
+ pytest.importorskip("bct")
244
+
245
+ with caplog.at_level(logging.DEBUG):
246
+ if error_msg is None:
247
+ read_transform(
248
+ storage=matrix_storage_with_nan, # type: ignore
249
+ feature_name="BOLD_matrix",
250
+ transform="bctpy_eigenvector_centrality_und",
251
+ nan_policy=nan_policy,
252
+ )
253
+ assert "Computing" in caplog.text
254
+ assert "Generating" in caplog.text
255
+ else:
256
+ with pytest.raises(ValueError, match=error_msg):
257
+ read_transform(
258
+ storage=matrix_storage_with_nan, # type: ignore
259
+ feature_name="BOLD_matrix",
260
+ transform="bctpy_eigenvector_centrality_und",
261
+ nan_policy=nan_policy,
262
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: junifer
3
- Version: 0.0.7.dev105
3
+ Version: 0.0.7.dev111
4
4
  Summary: JUelich NeuroImaging FEature extractoR
5
5
  Author-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  Maintainer-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
@@ -1,6 +1,6 @@
1
1
  junifer/__init__.py,sha256=2McgH1yNue6Z1V26-uN_mfMjbTcx4CLhym-DMBl5xA4,266
2
2
  junifer/__init__.pyi,sha256=SsTvgq2Dod6UqJN96GH1lCphH6hJQQurEJHGNhHjGUI,508
3
- junifer/_version.py,sha256=jFH3nrzs5AspealBCIydRQVFvENTPE2XjxPHECcRoME,528
3
+ junifer/_version.py,sha256=LnYX032f40evsucLrn5L758qPtoWuq5rSV96EwT5oTA,528
4
4
  junifer/conftest.py,sha256=PWYkkRDU8ly2lYwv7VBKMHje4et6HX7Yey3Md_I2KbA,613
5
5
  junifer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  junifer/stats.py,sha256=e9aaagMGtgpRfW3Wdpz9ocpnYld1IWylCDcjFUgX9Mk,6225
@@ -237,8 +237,8 @@ junifer/markers/tests/test_parcel_aggregation.py,sha256=04OqtY_Z-KW4W1jU5K6GeWnL
237
237
  junifer/markers/tests/test_sphere_aggregation.py,sha256=HPaLD6xKdewTt0iANz3nYOD7ZI-g7BqMTiRdV-4sM8M,10669
238
238
  junifer/onthefly/__init__.py,sha256=TA6tPuw54ynDlumb9Ii-2p59hw2rGoCMe1-vQ89JzZ8,238
239
239
  junifer/onthefly/_brainprint.py,sha256=-BswaAV9SLHU8mmWJ2KbPL7FgERJzIQIbSdV-NYiiYI,3802
240
- junifer/onthefly/read_transform.py,sha256=JfTJIiZnautkJ6DzsjeWQ7AEDRHV4omfinvDkow3FFM,4272
241
- junifer/onthefly/tests/test_read_transform.py,sha256=Ed6gtj8bsD11fe0Y1AxG2JndtITDSBje4g3hx3wkbAo,4718
240
+ junifer/onthefly/read_transform.py,sha256=pUwwsO4oBwq6u4ybRpnQ5s6MujtwD_1AOMv-RdavAFg,6690
241
+ junifer/onthefly/tests/test_read_transform.py,sha256=U8BwImmgH9e2eA_WXVWyKgGzFQNEoD0teCNv2Udlhok,7246
242
242
  junifer/pipeline/__init__.py,sha256=rxKQGRwc6_sts1KhVIcVVpuXeiFABf11mQQ2h5jgA3U,194
243
243
  junifer/pipeline/__init__.pyi,sha256=hhcvNcABhtLaUQiZdTjo5sMWC3rtDkwVshL0sxD5JAE,399
244
244
  junifer/pipeline/marker_collection.py,sha256=1Kmf5f0E2MFhDpO9OBui046b_6h1u9U64AdEqrxso-o,5377
@@ -324,10 +324,10 @@ junifer/utils/tests/test_config.py,sha256=7ltIXuwb_W4Mv_1dxQWyiyM10XgUAfsWKV6D_i
324
324
  junifer/utils/tests/test_fs.py,sha256=WQS7cKlKEZ742CIuiOYYpueeAhY9PqlastfDVpVVtvE,923
325
325
  junifer/utils/tests/test_helpers.py,sha256=k5qqfxK8dFyuewTJyR1Qn6-nFaYNuVr0ysc18bfPjyU,929
326
326
  junifer/utils/tests/test_logging.py,sha256=W4tFKmaf8_CxnWZ-o_-XxM7DQbhGG18RsLZJk8bZelI,8163
327
- junifer-0.0.7.dev105.dist-info/licenses/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
328
- junifer-0.0.7.dev105.dist-info/licenses/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
329
- junifer-0.0.7.dev105.dist-info/METADATA,sha256=y9qwZWakS8Oc_XurJ3dN1hQ-ndxPyy5BdikbsZsaIG8,8388
330
- junifer-0.0.7.dev105.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
331
- junifer-0.0.7.dev105.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
332
- junifer-0.0.7.dev105.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
333
- junifer-0.0.7.dev105.dist-info/RECORD,,
327
+ junifer-0.0.7.dev111.dist-info/licenses/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
328
+ junifer-0.0.7.dev111.dist-info/licenses/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
329
+ junifer-0.0.7.dev111.dist-info/METADATA,sha256=ZPvs0K5JekuTwe2VzukElpA4wD4nwWpYjOFqENx3AcQ,8388
330
+ junifer-0.0.7.dev111.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
331
+ junifer-0.0.7.dev111.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
332
+ junifer-0.0.7.dev111.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
333
+ junifer-0.0.7.dev111.dist-info/RECORD,,