junifer 0.0.6.dev412__py3-none-any.whl → 0.0.6.dev418__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 +2 -2
- junifer/storage/base.py +33 -0
- junifer/storage/hdf5.py +74 -13
- junifer/storage/tests/test_hdf5.py +85 -2
- junifer/storage/tests/test_storage_base.py +5 -2
- junifer/storage/tests/test_utils.py +25 -0
- junifer/storage/utils.py +86 -0
- {junifer-0.0.6.dev412.dist-info → junifer-0.0.6.dev418.dist-info}/METADATA +1 -1
- {junifer-0.0.6.dev412.dist-info → junifer-0.0.6.dev418.dist-info}/RECORD +14 -14
- {junifer-0.0.6.dev412.dist-info → junifer-0.0.6.dev418.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.6.dev412.dist-info → junifer-0.0.6.dev418.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.6.dev412.dist-info → junifer-0.0.6.dev418.dist-info}/WHEEL +0 -0
- {junifer-0.0.6.dev412.dist-info → junifer-0.0.6.dev418.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.6.dev412.dist-info → junifer-0.0.6.dev418.dist-info}/top_level.txt +0 -0
junifer/_version.py
CHANGED
@@ -12,5 +12,5 @@ __version__: str
|
|
12
12
|
__version_tuple__: VERSION_TUPLE
|
13
13
|
version_tuple: VERSION_TUPLE
|
14
14
|
|
15
|
-
__version__ = version = '0.0.6.
|
16
|
-
__version_tuple__ = version_tuple = (0, 0, 6, '
|
15
|
+
__version__ = version = '0.0.6.dev418'
|
16
|
+
__version_tuple__ = version_tuple = (0, 0, 6, 'dev418')
|
junifer/storage/base.py
CHANGED
@@ -226,6 +226,10 @@ class BaseFeatureStorage(ABC):
|
|
226
226
|
self.store_scalar_table(
|
227
227
|
meta_md5=meta_md5, element=t_element, **kwargs
|
228
228
|
)
|
229
|
+
elif kind == "timeseries_2d":
|
230
|
+
self.store_timeseries_2d(
|
231
|
+
meta_md5=meta_md5, element=t_element, **kwargs
|
232
|
+
)
|
229
233
|
|
230
234
|
def store_matrix(
|
231
235
|
self,
|
@@ -321,6 +325,35 @@ class BaseFeatureStorage(ABC):
|
|
321
325
|
klass=NotImplementedError,
|
322
326
|
)
|
323
327
|
|
328
|
+
def store_timeseries_2d(
|
329
|
+
self,
|
330
|
+
meta_md5: str,
|
331
|
+
element: dict,
|
332
|
+
data: np.ndarray,
|
333
|
+
col_names: Optional[Iterable[str]] = None,
|
334
|
+
row_names: Optional[Iterable[str]] = None,
|
335
|
+
) -> None:
|
336
|
+
"""Store 2D timeseries.
|
337
|
+
|
338
|
+
Parameters
|
339
|
+
----------
|
340
|
+
meta_md5 : str
|
341
|
+
The metadata MD5 hash.
|
342
|
+
element : dict
|
343
|
+
The element as a dictionary.
|
344
|
+
data : numpy.ndarray
|
345
|
+
The timeseries data to store.
|
346
|
+
col_names : list or tuple of str, optional
|
347
|
+
The column labels (default None).
|
348
|
+
row_names : list or tuple of str, optional
|
349
|
+
The row labels (default None).
|
350
|
+
|
351
|
+
"""
|
352
|
+
raise_error(
|
353
|
+
msg="Concrete classes need to implement store_timeseries_2d().",
|
354
|
+
klass=NotImplementedError,
|
355
|
+
)
|
356
|
+
|
324
357
|
def store_scalar_table(
|
325
358
|
self,
|
326
359
|
meta_md5: str,
|
junifer/storage/hdf5.py
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
# Federico Raimondo <f.raimondo@fz-juelich.de>
|
5
5
|
# License: AGPL
|
6
6
|
|
7
|
-
|
8
7
|
from collections import defaultdict
|
9
8
|
from collections.abc import Iterable
|
10
9
|
from pathlib import Path
|
@@ -24,7 +23,13 @@ from ..external.h5io.h5io import (
|
|
24
23
|
)
|
25
24
|
from ..utils import logger, raise_error
|
26
25
|
from .base import BaseFeatureStorage
|
27
|
-
from .utils import
|
26
|
+
from .utils import (
|
27
|
+
element_to_prefix,
|
28
|
+
matrix_to_vector,
|
29
|
+
store_matrix_checks,
|
30
|
+
store_timeseries_2d_checks,
|
31
|
+
timeseries2d_to_vector,
|
32
|
+
)
|
28
33
|
|
29
34
|
|
30
35
|
__all__ = ["HDF5FeatureStorage"]
|
@@ -82,7 +87,7 @@ def _create_chunk(
|
|
82
87
|
chunk_size=tuple(array_chunk_size),
|
83
88
|
n_chunk=i_chunk,
|
84
89
|
)
|
85
|
-
elif kind in ["timeseries", "scalar_table"]:
|
90
|
+
elif kind in ["timeseries", "scalar_table", "timeseries_2d"]:
|
86
91
|
out = ChunkedList(
|
87
92
|
data=chunk_data,
|
88
93
|
size=element_count,
|
@@ -91,8 +96,8 @@ def _create_chunk(
|
|
91
96
|
else:
|
92
97
|
raise_error(
|
93
98
|
f"Invalid kind: {kind}. "
|
94
|
-
"Must be one of ['vector', 'matrix', 'timeseries',"
|
95
|
-
"'scalar_table']."
|
99
|
+
"Must be one of ['vector', 'matrix', 'timeseries', "
|
100
|
+
"'timeseries_2d', 'scalar_table']."
|
96
101
|
)
|
97
102
|
return out
|
98
103
|
|
@@ -196,8 +201,7 @@ class HDF5FeatureStorage(BaseFeatureStorage):
|
|
196
201
|
if not self.single_output and not element:
|
197
202
|
raise_error(
|
198
203
|
msg=(
|
199
|
-
"`element` must be provided when `single_output` "
|
200
|
-
"is False"
|
204
|
+
"`element` must be provided when `single_output` is False"
|
201
205
|
),
|
202
206
|
klass=RuntimeError,
|
203
207
|
)
|
@@ -514,6 +518,27 @@ class HDF5FeatureStorage(BaseFeatureStorage):
|
|
514
518
|
columns = hdf_data["column_headers"]
|
515
519
|
# Convert data from 3D to 2D
|
516
520
|
reshaped_data = np.concatenate(all_data, axis=0)
|
521
|
+
elif hdf_data["kind"] == "timeseries_2d":
|
522
|
+
# Create dictionary for aggregating index data
|
523
|
+
element_idx = defaultdict(list)
|
524
|
+
all_data = []
|
525
|
+
for idx, element in enumerate(hdf_data["element"]):
|
526
|
+
# Get row count for the element
|
527
|
+
t_data = hdf_data["data"][idx]
|
528
|
+
flat_data, columns = timeseries2d_to_vector(
|
529
|
+
data=t_data,
|
530
|
+
col_names=hdf_data["column_headers"],
|
531
|
+
row_names=hdf_data["row_headers"],
|
532
|
+
)
|
533
|
+
all_data.append(flat_data)
|
534
|
+
n_timepoints = flat_data.shape[0]
|
535
|
+
# Set rows for the index
|
536
|
+
for key, val in element.items():
|
537
|
+
element_idx[key].extend([val] * n_timepoints)
|
538
|
+
# Add extra column for timepoints
|
539
|
+
element_idx["timepoint"].extend(np.arange(n_timepoints))
|
540
|
+
# Convert data from 3D to 2D
|
541
|
+
reshaped_data = np.concatenate(all_data, axis=0)
|
517
542
|
elif hdf_data["kind"] == "scalar_table":
|
518
543
|
# Create dictionary for aggregating index data
|
519
544
|
element_idx = defaultdict(list)
|
@@ -765,7 +790,7 @@ class HDF5FeatureStorage(BaseFeatureStorage):
|
|
765
790
|
)
|
766
791
|
|
767
792
|
t_data = stored_data["data"]
|
768
|
-
if kind in ["timeseries", "scalar_table"]:
|
793
|
+
if kind in ["timeseries", "scalar_table", "timeseries_2d"]:
|
769
794
|
t_data += data
|
770
795
|
else:
|
771
796
|
t_data = np.concatenate((t_data, data), axis=-1)
|
@@ -947,6 +972,44 @@ class HDF5FeatureStorage(BaseFeatureStorage):
|
|
947
972
|
row_header_column_name="timepoint",
|
948
973
|
)
|
949
974
|
|
975
|
+
def store_timeseries_2d(
|
976
|
+
self,
|
977
|
+
meta_md5: str,
|
978
|
+
element: dict[str, str],
|
979
|
+
data: np.ndarray,
|
980
|
+
col_names: Optional[Iterable[str]] = None,
|
981
|
+
row_names: Optional[Iterable[str]] = None,
|
982
|
+
) -> None:
|
983
|
+
"""Store a 2D timeseries.
|
984
|
+
|
985
|
+
Parameters
|
986
|
+
----------
|
987
|
+
meta_md5 : str
|
988
|
+
The metadata MD5 hash.
|
989
|
+
element : dict
|
990
|
+
The element as dictionary.
|
991
|
+
data : numpy.ndarray
|
992
|
+
The 2D timeseries data to store.
|
993
|
+
col_names : list or tuple of str, optional
|
994
|
+
The column labels (default None).
|
995
|
+
row_names : list or tuple of str, optional
|
996
|
+
The row labels (default None).
|
997
|
+
|
998
|
+
"""
|
999
|
+
store_timeseries_2d_checks(
|
1000
|
+
data_shape=data.shape,
|
1001
|
+
row_names_len=len(row_names), # type: ignore
|
1002
|
+
col_names_len=len(col_names), # type: ignore
|
1003
|
+
)
|
1004
|
+
self._store_data(
|
1005
|
+
kind="timeseries_2d",
|
1006
|
+
meta_md5=meta_md5,
|
1007
|
+
element=[element], # convert to list
|
1008
|
+
data=[data], # convert to list
|
1009
|
+
column_headers=col_names,
|
1010
|
+
row_headers=row_names,
|
1011
|
+
)
|
1012
|
+
|
950
1013
|
def store_scalar_table(
|
951
1014
|
self,
|
952
1015
|
meta_md5: str,
|
@@ -1014,8 +1077,7 @@ class HDF5FeatureStorage(BaseFeatureStorage):
|
|
1014
1077
|
|
1015
1078
|
# Run loop to collect metadata
|
1016
1079
|
logger.info(
|
1017
|
-
"Collecting metadata from "
|
1018
|
-
f"{self.uri.parent}/*_{self.uri.name}" # type: ignore
|
1080
|
+
f"Collecting metadata from {self.uri.parent}/*_{self.uri.name}" # type: ignore
|
1019
1081
|
)
|
1020
1082
|
# Collect element files per feature MD5
|
1021
1083
|
elements_per_feature_md5 = defaultdict(list)
|
@@ -1046,8 +1108,7 @@ class HDF5FeatureStorage(BaseFeatureStorage):
|
|
1046
1108
|
|
1047
1109
|
# Run loop to collect data per feature per file
|
1048
1110
|
logger.info(
|
1049
|
-
"Collecting data from "
|
1050
|
-
f"{self.uri.parent}/*_{self.uri.name}" # type: ignore
|
1111
|
+
f"Collecting data from {self.uri.parent}/*_{self.uri.name}" # type: ignore
|
1051
1112
|
)
|
1052
1113
|
logger.info(f"Will collect {len(elements_per_feature_md5)} features.")
|
1053
1114
|
|
@@ -1092,7 +1153,7 @@ class HDF5FeatureStorage(BaseFeatureStorage):
|
|
1092
1153
|
kind = static_data["kind"]
|
1093
1154
|
|
1094
1155
|
# Append the "dynamic" data
|
1095
|
-
if kind in ["timeseries", "scalar_table"]:
|
1156
|
+
if kind in ["timeseries", "scalar_table", "timeseries_2d"]:
|
1096
1157
|
chunk_data.extend(t_data["data"])
|
1097
1158
|
else:
|
1098
1159
|
chunk_data.append(t_data["data"])
|
@@ -826,6 +826,70 @@ def test_store_timeseries(tmp_path: Path) -> None:
|
|
826
826
|
assert_array_equal(read_df.values, data)
|
827
827
|
|
828
828
|
|
829
|
+
def test_store_timeseries2d(tmp_path: Path) -> None:
|
830
|
+
"""Test 2D timeseries store.
|
831
|
+
|
832
|
+
Parameters
|
833
|
+
----------
|
834
|
+
tmp_path : pathlib.Path
|
835
|
+
The path to the test directory.
|
836
|
+
|
837
|
+
"""
|
838
|
+
uri = tmp_path / "test_store_timeseries_2d.hdf5"
|
839
|
+
storage = HDF5FeatureStorage(uri=uri)
|
840
|
+
# Metadata to store
|
841
|
+
element = {"subject": "test"}
|
842
|
+
meta = {
|
843
|
+
"element": element,
|
844
|
+
"dependencies": ["numpy"],
|
845
|
+
"marker": {"name": "fc"},
|
846
|
+
"type": "BOLD",
|
847
|
+
}
|
848
|
+
# Process the metadata
|
849
|
+
meta_md5, meta_to_store, element_to_store = process_meta(meta)
|
850
|
+
# Store metadata
|
851
|
+
storage.store_metadata(
|
852
|
+
meta_md5=meta_md5, element=element_to_store, meta=meta_to_store
|
853
|
+
)
|
854
|
+
|
855
|
+
# Data to store
|
856
|
+
data = np.array(
|
857
|
+
[[10, 11, 12], [20, 21, 22], [30, 31, 32], [40, 41, 42], [50, 51, 52]]
|
858
|
+
)
|
859
|
+
data = np.c_[[data + (i * 100) for i in range(4)]] # Generate timeseries
|
860
|
+
|
861
|
+
col_names = ["roi1", "roi2", "roi3"]
|
862
|
+
row_names = ["ev1", "ev2", "ev3", "ev4", "ev5"]
|
863
|
+
|
864
|
+
# Store 2D timeseries
|
865
|
+
storage.store_timeseries_2d(
|
866
|
+
meta_md5=meta_md5,
|
867
|
+
element=element_to_store,
|
868
|
+
data=data,
|
869
|
+
col_names=col_names,
|
870
|
+
row_names=row_names,
|
871
|
+
)
|
872
|
+
|
873
|
+
# Read into dataframe
|
874
|
+
read_data = storage.read(feature_md5=meta_md5)
|
875
|
+
# Check if data are equal
|
876
|
+
assert_array_equal(read_data["data"][0], data)
|
877
|
+
assert read_data["column_headers"] == col_names
|
878
|
+
assert read_data["row_headers"], row_names
|
879
|
+
|
880
|
+
read_df = storage.read_df(feature_md5=meta_md5)
|
881
|
+
flatted_names = [f"{row}~{col}" for row in row_names for col in col_names]
|
882
|
+
|
883
|
+
expected_flat_data = np.array(
|
884
|
+
[10, 11, 12, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52]
|
885
|
+
)
|
886
|
+
expected_flat_data = np.c_[
|
887
|
+
[expected_flat_data + (i * 100) for i in range(4)]
|
888
|
+
] # Generate timeseries
|
889
|
+
assert_array_equal(read_df.values, expected_flat_data)
|
890
|
+
assert read_df.columns.to_list() == flatted_names
|
891
|
+
|
892
|
+
|
829
893
|
def test_store_scalar_table(tmp_path: Path) -> None:
|
830
894
|
"""Test scalar table store.
|
831
895
|
|
@@ -857,7 +921,7 @@ def test_store_scalar_table(tmp_path: Path) -> None:
|
|
857
921
|
col_names = ["roi1", "roi2"]
|
858
922
|
row_names = ["ev1", "ev2", "ev3"]
|
859
923
|
|
860
|
-
# Store
|
924
|
+
# Store scalar table
|
861
925
|
storage.store_scalar_table(
|
862
926
|
meta_md5=meta_md5,
|
863
927
|
element=element_to_store,
|
@@ -910,6 +974,12 @@ def _create_data_to_store(n_elements: int, kind: str) -> tuple[str, dict]:
|
|
910
974
|
"data": np.arange(20).reshape(2, 10),
|
911
975
|
"col_names": [f"col-{i}" for i in range(10)],
|
912
976
|
}
|
977
|
+
elif kind in "timeseries_2d":
|
978
|
+
data_to_store = {
|
979
|
+
"data": np.arange(120).reshape(6, 5, 4),
|
980
|
+
"row_names": [f"row-{i}" for i in range(5)],
|
981
|
+
"col_names": [f"col-{i}" for i in range(4)],
|
982
|
+
}
|
913
983
|
elif kind in "scalar_table":
|
914
984
|
data_to_store = {
|
915
985
|
"data": np.arange(50).reshape(5, 10),
|
@@ -961,6 +1031,7 @@ def _create_data_to_store(n_elements: int, kind: str) -> tuple[str, dict]:
|
|
961
1031
|
(10, 5, "matrix"),
|
962
1032
|
(10, 5, "timeseries"),
|
963
1033
|
(10, 5, "scalar_table"),
|
1034
|
+
(10, 5, "timeseries_2d"),
|
964
1035
|
],
|
965
1036
|
)
|
966
1037
|
def test_multi_output_store_and_collect(
|
@@ -982,7 +1053,9 @@ def test_multi_output_store_and_collect(
|
|
982
1053
|
"""
|
983
1054
|
uri = tmp_path / "test_multi_output_store_and_collect.hdf5"
|
984
1055
|
storage = HDF5FeatureStorage(
|
985
|
-
uri=uri,
|
1056
|
+
uri=uri,
|
1057
|
+
single_output=False,
|
1058
|
+
chunk_size=chunk_size,
|
986
1059
|
)
|
987
1060
|
|
988
1061
|
meta_md5, all_data = _create_data_to_store(n_elements, kind)
|
@@ -1013,6 +1086,12 @@ def test_multi_output_store_and_collect(
|
|
1013
1086
|
element=t_data["element"],
|
1014
1087
|
**t_data["data"],
|
1015
1088
|
)
|
1089
|
+
elif kind == "timeseries_2d":
|
1090
|
+
storage.store_timeseries_2d(
|
1091
|
+
meta_md5=meta_md5,
|
1092
|
+
element=t_data["element"],
|
1093
|
+
**t_data["data"],
|
1094
|
+
)
|
1016
1095
|
elif kind == "scalar_table":
|
1017
1096
|
storage.store_scalar_table(
|
1018
1097
|
meta_md5=meta_md5,
|
@@ -1052,6 +1131,10 @@ def test_multi_output_store_and_collect(
|
|
1052
1131
|
data_size = np.sum([x["data"]["data"].shape[0] for x in all_data])
|
1053
1132
|
assert len(all_df) == data_size
|
1054
1133
|
idx_names = [x for x in all_df.index.names if x != "timepoint"]
|
1134
|
+
elif kind == "timeseries_2d":
|
1135
|
+
data_size = np.sum([x["data"]["data"].shape[0] for x in all_data])
|
1136
|
+
assert len(all_df) == data_size
|
1137
|
+
idx_names = [x for x in all_df.index.names if x != "timepoint"]
|
1055
1138
|
elif kind == "scalar_table":
|
1056
1139
|
data_size = np.sum([x["data"]["data"].shape[0] for x in all_data])
|
1057
1140
|
assert len(all_df) == data_size
|
@@ -25,7 +25,7 @@ def test_BaseFeatureStorage() -> None:
|
|
25
25
|
"""Implement concrete class."""
|
26
26
|
|
27
27
|
def __init__(self, uri, single_output=True):
|
28
|
-
storage_types = ["matrix", "vector", "timeseries"]
|
28
|
+
storage_types = ["matrix", "vector", "timeseries", "timeseries_2d"]
|
29
29
|
super().__init__(
|
30
30
|
uri=uri,
|
31
31
|
storage_types=storage_types,
|
@@ -33,7 +33,7 @@ def test_BaseFeatureStorage() -> None:
|
|
33
33
|
)
|
34
34
|
|
35
35
|
def get_valid_inputs(self):
|
36
|
-
return ["matrix", "vector", "timeseries"]
|
36
|
+
return ["matrix", "vector", "timeseries", "timeseries_2d"]
|
37
37
|
|
38
38
|
def list_features(self):
|
39
39
|
super().list_features()
|
@@ -97,6 +97,9 @@ def test_BaseFeatureStorage() -> None:
|
|
97
97
|
with pytest.raises(NotImplementedError):
|
98
98
|
st.store(kind="timeseries", meta=meta)
|
99
99
|
|
100
|
+
with pytest.raises(NotImplementedError):
|
101
|
+
st.store(kind="timeseries_2d", meta=meta)
|
102
|
+
|
100
103
|
with pytest.raises(NotImplementedError):
|
101
104
|
st.store(kind="vector", meta=meta)
|
102
105
|
|
@@ -17,6 +17,7 @@ from junifer.storage.utils import (
|
|
17
17
|
matrix_to_vector,
|
18
18
|
process_meta,
|
19
19
|
store_matrix_checks,
|
20
|
+
timeseries2d_to_vector,
|
20
21
|
)
|
21
22
|
|
22
23
|
|
@@ -421,3 +422,27 @@ def test_matrix_to_vector(
|
|
421
422
|
data, columns = matrix_to_vector(**params) # type: ignore
|
422
423
|
assert_array_equal(data, expected_data)
|
423
424
|
assert columns == expected_columns
|
425
|
+
|
426
|
+
|
427
|
+
def test_timeseries2d_to_vector() -> None:
|
428
|
+
"""Test timeseries2d to vector."""
|
429
|
+
data = np.array(
|
430
|
+
[[10, 11, 12], [20, 21, 22], [30, 31, 32], [40, 41, 42], [50, 51, 52]]
|
431
|
+
)
|
432
|
+
data = np.c_[[data + (i * 100) for i in range(4)]] # Generate timeseries
|
433
|
+
col_names = ["c0", "c1", "c2"]
|
434
|
+
row_names = ["r0", "r1", "r2", "r3", "r4"]
|
435
|
+
flat_data, columns = timeseries2d_to_vector(
|
436
|
+
data=data,
|
437
|
+
col_names=col_names,
|
438
|
+
row_names=row_names,
|
439
|
+
)
|
440
|
+
|
441
|
+
expected_flat_data = np.array(
|
442
|
+
[10, 11, 12, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52]
|
443
|
+
)
|
444
|
+
expected_flat_data = np.c_[
|
445
|
+
[expected_flat_data + (i * 100) for i in range(4)]
|
446
|
+
] # Generate timeseries
|
447
|
+
assert_array_equal(flat_data, expected_flat_data)
|
448
|
+
assert columns == [f"{r}~{c}" for r in row_names for c in col_names]
|
junifer/storage/utils.py
CHANGED
@@ -181,6 +181,15 @@ def store_matrix_checks(
|
|
181
181
|
col_names_len : int
|
182
182
|
The length of column labels.
|
183
183
|
|
184
|
+
Raises
|
185
|
+
------
|
186
|
+
ValueError
|
187
|
+
If the matrix kind is invalid
|
188
|
+
If the diagonal is False and the matrix kind is "full"
|
189
|
+
If the matrix kind is "triu" or "tril" and the matrix is not square
|
190
|
+
If the number of row names does not match the number of rows
|
191
|
+
If the number of column names does not match the number of columns
|
192
|
+
|
184
193
|
"""
|
185
194
|
# Matrix kind validation
|
186
195
|
if matrix_kind not in ("triu", "tril", "full"):
|
@@ -212,6 +221,51 @@ def store_matrix_checks(
|
|
212
221
|
)
|
213
222
|
|
214
223
|
|
224
|
+
def store_timeseries_2d_checks(
|
225
|
+
data_shape: tuple[int, int, int],
|
226
|
+
row_names_len: int,
|
227
|
+
col_names_len: int,
|
228
|
+
) -> None:
|
229
|
+
"""Run parameter checks for store_timeseries_2d() methods.
|
230
|
+
|
231
|
+
Parameters
|
232
|
+
----------
|
233
|
+
data_shape : tuple of int and int
|
234
|
+
The shape of the matrix data to store.
|
235
|
+
row_names_len : int
|
236
|
+
The length of row labels.
|
237
|
+
col_names_len : int
|
238
|
+
The length of column labels.
|
239
|
+
|
240
|
+
Raises
|
241
|
+
------
|
242
|
+
ValueError
|
243
|
+
If the data is not a 3D array (timepoints, rows, columns)
|
244
|
+
If the number of row names does not match the number of rows
|
245
|
+
If the number of column names does not match the number of columns
|
246
|
+
|
247
|
+
"""
|
248
|
+
# data validation
|
249
|
+
if len(data_shape) != 3:
|
250
|
+
raise_error(
|
251
|
+
msg="Data must be a 3D array",
|
252
|
+
klass=ValueError,
|
253
|
+
)
|
254
|
+
|
255
|
+
# Row label validation
|
256
|
+
if row_names_len != data_shape[1]: # type: ignore
|
257
|
+
raise_error(
|
258
|
+
msg="Number of row names does not match number of rows",
|
259
|
+
klass=ValueError,
|
260
|
+
)
|
261
|
+
# Column label validation
|
262
|
+
if col_names_len != data_shape[2]: # type: ignore
|
263
|
+
raise_error(
|
264
|
+
msg="Number of column names does not match number of columns",
|
265
|
+
klass=ValueError,
|
266
|
+
)
|
267
|
+
|
268
|
+
|
215
269
|
def matrix_to_vector(
|
216
270
|
data: np.ndarray,
|
217
271
|
col_names: Iterable[str],
|
@@ -268,3 +322,35 @@ def matrix_to_vector(
|
|
268
322
|
]
|
269
323
|
|
270
324
|
return flat_data, columns
|
325
|
+
|
326
|
+
|
327
|
+
def timeseries2d_to_vector(
|
328
|
+
data: np.ndarray,
|
329
|
+
col_names: Iterable[str],
|
330
|
+
row_names: Iterable[str],
|
331
|
+
) -> tuple[np.ndarray, list[str]]:
|
332
|
+
"""Convert matrix to vector based on parameters.
|
333
|
+
|
334
|
+
Parameters
|
335
|
+
----------
|
336
|
+
data : 2D / 3D numpy.ndarray
|
337
|
+
The matrix / tensor data to store / read.
|
338
|
+
col_names : list or tuple of str
|
339
|
+
The column labels.
|
340
|
+
row_names : list or tuple of str
|
341
|
+
The row labels.
|
342
|
+
|
343
|
+
Returns
|
344
|
+
-------
|
345
|
+
2D numpy.ndarray
|
346
|
+
The vector / matrix data.
|
347
|
+
list of str
|
348
|
+
The column labels.
|
349
|
+
|
350
|
+
"""
|
351
|
+
# Reshape data to 2D
|
352
|
+
flat_data = data.reshape(data.shape[0], -1)
|
353
|
+
# Generate flat 1D row X column names
|
354
|
+
columns = [f"{r}~{c}" for r in row_names for c in col_names]
|
355
|
+
|
356
|
+
return flat_data, columns
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: junifer
|
3
|
-
Version: 0.0.6.
|
3
|
+
Version: 0.0.6.dev418
|
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=
|
3
|
+
junifer/_version.py,sha256=OMl11BkdBDrB0BmEbZRQU35sDln0nRvukbppZ7DGaBQ,428
|
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
|
@@ -301,17 +301,17 @@ junifer/preprocess/warping/space_warper.py,sha256=mf7SDu574R3TXNt82fqGl_hcEYx7Sj
|
|
301
301
|
junifer/preprocess/warping/tests/test_space_warper.py,sha256=amFHtt-q7L7v9uL4cOvrmHEbUOGDhmoMHkLnKJ0dF7A,5543
|
302
302
|
junifer/storage/__init__.py,sha256=aPGBFPPsTcZYMdkC_o5HIrzRIIwp-bc5bJDoh_GuQmo,270
|
303
303
|
junifer/storage/__init__.pyi,sha256=MHC-R129z_WuXVQuKBrFu8H1wqmUPAl5ZOQT_WZaXek,292
|
304
|
-
junifer/storage/base.py,sha256=
|
305
|
-
junifer/storage/hdf5.py,sha256=
|
304
|
+
junifer/storage/base.py,sha256=zHvrRK62uzXo7C8FsOghbgcYdRluoO_imOuxgc_kncg,11887
|
305
|
+
junifer/storage/hdf5.py,sha256=P8k2KasLIukJ71piPwF9uU7-PCWmQ46xO1hkPylIeBQ,40309
|
306
306
|
junifer/storage/pandas_base.py,sha256=v3iRuoXJzBChZYkjR4OHJp99NM0BPTpYkw1TAX52Nto,7529
|
307
307
|
junifer/storage/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
308
308
|
junifer/storage/sqlite.py,sha256=QzkKB1pD4qNjrMe0bB7ATHnPgNjteTrP3ULCE_XuwD0,21269
|
309
|
-
junifer/storage/utils.py,sha256=
|
310
|
-
junifer/storage/tests/test_hdf5.py,sha256=
|
309
|
+
junifer/storage/utils.py,sha256=dgSbYjpwzy7dutcsdacd-5ZLR8kIucRcdlXw99aAjFI,9806
|
310
|
+
junifer/storage/tests/test_hdf5.py,sha256=yEh5jw_6i37bch27FXIZmDPCA8vEfc-0TehXp9sYX0Y,34144
|
311
311
|
junifer/storage/tests/test_pandas_base.py,sha256=y_TfUGpuXkj_39yVon3rMDxMeBrZXs58ZW6OSty5LNw,4058
|
312
312
|
junifer/storage/tests/test_sqlite.py,sha256=0TQIcqHPgk67ALsR-98CA73ulDPsR2t9wGXYaem983w,28312
|
313
|
-
junifer/storage/tests/test_storage_base.py,sha256=
|
314
|
-
junifer/storage/tests/test_utils.py,sha256=
|
313
|
+
junifer/storage/tests/test_storage_base.py,sha256=o2CbklSxaq3XDX8XPf5aG5hiXzpBCmc5XXckCIsLVOo,3201
|
314
|
+
junifer/storage/tests/test_utils.py,sha256=tdQbgA7FOO4_qdCuo2ZiXBeD_-9IKc-CsYDfhA_rCI4,12730
|
315
315
|
junifer/testing/__init__.py,sha256=gqfrX2c7I31VYBmH9hCUERO-61NwubT1cvy1bKM0NqU,249
|
316
316
|
junifer/testing/__init__.pyi,sha256=OFqGc5GCjoD4hPVOYNWvnvvP_RVF-oO-UQR8n9HDVtM,133
|
317
317
|
junifer/testing/datagrabbers.py,sha256=ui2VwArMjx4KUD2Cf8PRJOExvDHfPntuuuhEEwWwTZ4,6571
|
@@ -341,10 +341,10 @@ junifer/utils/tests/test_config.py,sha256=7ltIXuwb_W4Mv_1dxQWyiyM10XgUAfsWKV6D_i
|
|
341
341
|
junifer/utils/tests/test_fs.py,sha256=WQS7cKlKEZ742CIuiOYYpueeAhY9PqlastfDVpVVtvE,923
|
342
342
|
junifer/utils/tests/test_helpers.py,sha256=k5qqfxK8dFyuewTJyR1Qn6-nFaYNuVr0ysc18bfPjyU,929
|
343
343
|
junifer/utils/tests/test_logging.py,sha256=duO4ou365hxwa_kwihFtKPLaL6LC5XHiyhOijrrngbA,8009
|
344
|
-
junifer-0.0.6.
|
345
|
-
junifer-0.0.6.
|
346
|
-
junifer-0.0.6.
|
347
|
-
junifer-0.0.6.
|
348
|
-
junifer-0.0.6.
|
349
|
-
junifer-0.0.6.
|
350
|
-
junifer-0.0.6.
|
344
|
+
junifer-0.0.6.dev418.dist-info/AUTHORS.rst,sha256=rmULKpchpSol4ExWFdm-qu4fkpSZPYqIESVJBZtGb6E,163
|
345
|
+
junifer-0.0.6.dev418.dist-info/LICENSE.md,sha256=MqCnOBu8uXsEOzRZWh9EBVfVz-kE9NkXcLCrtGXo2yU,34354
|
346
|
+
junifer-0.0.6.dev418.dist-info/METADATA,sha256=MzAouL0FarJWSZvtphozKmMIpgp4QIKt22JJMF2rqNU,8429
|
347
|
+
junifer-0.0.6.dev418.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
348
|
+
junifer-0.0.6.dev418.dist-info/entry_points.txt,sha256=6O8ru0BP-SP7YMUZiizFNoaZ2HvJpadO2G7nKk4PwjI,48
|
349
|
+
junifer-0.0.6.dev418.dist-info/top_level.txt,sha256=4bAq1R2QFQ4b3hohjys2JBvxrl0GKk5LNFzYvz9VGcA,8
|
350
|
+
junifer-0.0.6.dev418.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|