junifer 0.0.7.dev332__py3-none-any.whl → 0.0.8__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.
Files changed (293) hide show
  1. junifer/_version.py +9 -19
  2. junifer/api/__init__.pyi +17 -2
  3. junifer/api/functions.py +222 -39
  4. junifer/api/queue_context/__init__.pyi +11 -3
  5. junifer/api/queue_context/gnu_parallel_local_adapter.py +81 -120
  6. junifer/api/queue_context/htcondor_adapter.py +118 -181
  7. junifer/api/queue_context/queue_context_adapter.py +51 -7
  8. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +10 -35
  9. junifer/api/queue_context/tests/test_htcondor_adapter.py +13 -50
  10. junifer/api/res/fsl/run_fsl_docker.sh +4 -0
  11. junifer/api/tests/test_functions.py +863 -36
  12. junifer/cli/__init__.py +1 -7
  13. junifer/cli/cli.py +41 -22
  14. junifer/cli/parser.py +23 -147
  15. junifer/cli/tests/data/gmd_mean.yaml +6 -6
  16. junifer/cli/tests/data/gmd_mean_htcondor.yaml +6 -5
  17. junifer/cli/tests/data/partly_cloudy_agg_mean_tian.yml +1 -0
  18. junifer/cli/tests/test_cli.py +120 -4
  19. junifer/cli/tests/test_cli_utils.py +23 -6
  20. junifer/cli/utils.py +14 -9
  21. junifer/configs/__init__.py +5 -0
  22. junifer/configs/__init__.pyi +0 -0
  23. junifer/configs/juseless/datagrabbers/__init__.pyi +4 -2
  24. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +18 -24
  25. junifer/configs/juseless/datagrabbers/camcan_vbm.py +20 -27
  26. junifer/configs/juseless/datagrabbers/ixi_vbm.py +44 -52
  27. junifer/configs/juseless/datagrabbers/tests/test_ixi_vbm.py +0 -7
  28. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -19
  29. junifer/configs/juseless/datagrabbers/ucla.py +111 -104
  30. junifer/configs/juseless/datagrabbers/ukb_vbm.py +19 -25
  31. junifer/configs/py.typed +0 -0
  32. junifer/conftest.py +63 -24
  33. junifer/data/_dispatch.py +9 -19
  34. junifer/data/coordinates/_ants_coordinates_warper.py +4 -1
  35. junifer/data/coordinates/_coordinates.py +51 -6
  36. junifer/data/coordinates/_fsl_coordinates_warper.py +4 -1
  37. junifer/data/coordinates/tests/test_coordinates.py +15 -3
  38. junifer/data/coordinates/tests/test_coordinates_names.py +37 -0
  39. junifer/data/maps/_ants_maps_warper.py +18 -12
  40. junifer/data/maps/_fsl_maps_warper.py +7 -3
  41. junifer/data/maps/_maps.py +12 -8
  42. junifer/data/maps/tests/test_maps.py +13 -21
  43. junifer/data/masks/_ants_mask_warper.py +18 -12
  44. junifer/data/masks/_fsl_mask_warper.py +7 -3
  45. junifer/data/masks/_masks.py +15 -12
  46. junifer/data/masks/tests/test_masks.py +20 -17
  47. junifer/data/parcellations/_ants_parcellation_warper.py +18 -12
  48. junifer/data/parcellations/_fsl_parcellation_warper.py +7 -5
  49. junifer/data/parcellations/_parcellations.py +125 -91
  50. junifer/data/parcellations/tests/test_parcellations.py +122 -1
  51. junifer/data/template_spaces.py +117 -50
  52. junifer/data/tests/test_template_spaces.py +10 -7
  53. junifer/data/utils.py +10 -8
  54. junifer/datagrabber/__init__.pyi +38 -5
  55. junifer/datagrabber/aomic/__init__.pyi +8 -1
  56. junifer/datagrabber/aomic/_types.py +22 -0
  57. junifer/datagrabber/aomic/id1000.py +193 -183
  58. junifer/datagrabber/aomic/piop1.py +215 -259
  59. junifer/datagrabber/aomic/piop2.py +211 -244
  60. junifer/datagrabber/aomic/tests/test_id1000.py +70 -53
  61. junifer/datagrabber/aomic/tests/test_piop1.py +98 -102
  62. junifer/datagrabber/aomic/tests/test_piop2.py +88 -85
  63. junifer/datagrabber/base.py +253 -79
  64. junifer/datagrabber/datalad_base.py +127 -76
  65. junifer/datagrabber/dmcc13_benchmark.py +204 -289
  66. junifer/datagrabber/hcp1200/__init__.pyi +7 -2
  67. junifer/datagrabber/hcp1200/datalad_hcp1200.py +61 -40
  68. junifer/datagrabber/hcp1200/hcp1200.py +151 -164
  69. junifer/datagrabber/hcp1200/tests/test_hcp1200.py +116 -128
  70. junifer/datagrabber/multiple.py +183 -63
  71. junifer/datagrabber/pattern.py +358 -227
  72. junifer/datagrabber/pattern_datalad.py +84 -124
  73. junifer/datagrabber/pattern_validation_mixin.py +22 -50
  74. junifer/datagrabber/tests/test_base.py +17 -4
  75. junifer/datagrabber/tests/test_datalad_base.py +20 -27
  76. junifer/datagrabber/tests/test_dmcc13_benchmark.py +110 -122
  77. junifer/datagrabber/tests/test_multiple.py +293 -31
  78. junifer/datagrabber/tests/test_pattern.py +577 -18
  79. junifer/datagrabber/tests/test_pattern_datalad.py +177 -107
  80. junifer/datagrabber/tests/test_pattern_validation_mixin.py +9 -51
  81. junifer/datareader/default.py +8 -5
  82. junifer/datareader/tests/test_default_reader.py +1 -1
  83. junifer/external/BrainPrint/.codecov.yml +2 -0
  84. junifer/external/BrainPrint/.codespellignore +2 -0
  85. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/bug-report.md +45 -0
  86. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/documentation.md +12 -0
  87. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/feature-request.md +24 -0
  88. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/questions-help-support.md +28 -0
  89. junifer/external/BrainPrint/.github/dependabot.yml +6 -0
  90. junifer/external/BrainPrint/.github/workflows/build.yml +53 -0
  91. junifer/external/BrainPrint/.github/workflows/code-style.yml +44 -0
  92. junifer/external/BrainPrint/.github/workflows/doc.yml +65 -0
  93. junifer/external/BrainPrint/.github/workflows/publish.yml +31 -0
  94. junifer/external/BrainPrint/.github/workflows/pytest.yml +54 -0
  95. junifer/external/BrainPrint/.gitignore +123 -0
  96. junifer/external/BrainPrint/LICENSE +21 -0
  97. junifer/external/BrainPrint/README.md +108 -0
  98. junifer/external/BrainPrint/brainprint/cli/__init__.py +11 -0
  99. junifer/external/BrainPrint/brainprint/cli/help_text.py +169 -0
  100. junifer/external/BrainPrint/brainprint/cli/parser.py +150 -0
  101. junifer/external/BrainPrint/brainprint/cli/utils.py +14 -0
  102. junifer/external/BrainPrint/brainprint/commands/__init__.py +0 -0
  103. junifer/external/BrainPrint/brainprint/commands/sys_info.py +18 -0
  104. junifer/external/BrainPrint/brainprint/utils/tests/__init__.py +0 -0
  105. junifer/external/BrainPrint/brainprint/utils/tests/test_config.py +33 -0
  106. junifer/external/BrainPrint/doc/Makefile +20 -0
  107. junifer/external/BrainPrint/doc/_static/.gitkeep +0 -0
  108. junifer/external/BrainPrint/doc/_templates/autosummary/class.rst +10 -0
  109. junifer/external/BrainPrint/doc/_templates/autosummary/function.rst +8 -0
  110. junifer/external/BrainPrint/doc/_templates/autosummary/module.rst +6 -0
  111. junifer/external/BrainPrint/doc/api/index.rst +11 -0
  112. junifer/external/BrainPrint/doc/conf.py +272 -0
  113. junifer/external/BrainPrint/doc/index.rst +140 -0
  114. junifer/external/BrainPrint/doc/links.inc +36 -0
  115. junifer/external/BrainPrint/doc/make.bat +35 -0
  116. junifer/external/BrainPrint/doc/references.bib +23 -0
  117. junifer/external/BrainPrint/pyproject.toml +170 -0
  118. junifer/external/BrainPrint/setup.py +3 -0
  119. junifer/external/__init__.py +5 -0
  120. junifer/external/__init__.pyi +0 -0
  121. junifer/external/nilearn/junifer_connectivity_measure.py +95 -39
  122. junifer/external/nilearn/junifer_nifti_spheres_masker.py +226 -67
  123. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +39 -27
  124. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +231 -21
  125. junifer/external/py.typed +0 -0
  126. junifer/markers/__init__.pyi +6 -3
  127. junifer/markers/base.py +126 -76
  128. junifer/markers/brainprint.py +30 -39
  129. junifer/markers/complexity/__init__.py +20 -3
  130. junifer/markers/complexity/complexity_base.py +40 -41
  131. junifer/markers/complexity/hurst_exponent.py +22 -35
  132. junifer/markers/complexity/multiscale_entropy_auc.py +53 -41
  133. junifer/markers/complexity/perm_entropy.py +22 -36
  134. junifer/markers/complexity/range_entropy.py +22 -35
  135. junifer/markers/complexity/range_entropy_auc.py +27 -36
  136. junifer/markers/complexity/sample_entropy.py +22 -36
  137. junifer/markers/complexity/tests/test_complexity_registry.py +48 -0
  138. junifer/markers/complexity/tests/test_hurst_exponent.py +47 -36
  139. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +50 -36
  140. junifer/markers/complexity/tests/test_perm_entropy.py +48 -36
  141. junifer/markers/complexity/tests/test_range_entropy.py +47 -36
  142. junifer/markers/complexity/tests/test_range_entropy_auc.py +47 -36
  143. junifer/markers/complexity/tests/test_sample_entropy.py +48 -36
  144. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +50 -36
  145. junifer/markers/complexity/weighted_perm_entropy.py +22 -36
  146. junifer/markers/ets_rss.py +35 -31
  147. junifer/markers/falff/__init__.pyi +7 -1
  148. junifer/markers/falff/_afni_falff.py +22 -9
  149. junifer/markers/falff/_junifer_falff.py +47 -31
  150. junifer/markers/falff/falff_base.py +80 -60
  151. junifer/markers/falff/falff_maps.py +17 -41
  152. junifer/markers/falff/falff_parcels.py +26 -51
  153. junifer/markers/falff/falff_spheres.py +34 -61
  154. junifer/markers/falff/tests/test_falff_maps.py +20 -23
  155. junifer/markers/falff/tests/test_falff_mask.py +99 -0
  156. junifer/markers/falff/tests/test_falff_parcels.py +14 -10
  157. junifer/markers/falff/tests/test_falff_spheres.py +14 -10
  158. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +32 -34
  159. junifer/markers/functional_connectivity/edge_functional_connectivity_maps.py +12 -25
  160. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +19 -34
  161. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +24 -44
  162. junifer/markers/functional_connectivity/functional_connectivity_base.py +50 -40
  163. junifer/markers/functional_connectivity/functional_connectivity_maps.py +12 -25
  164. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +19 -34
  165. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +24 -45
  166. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +21 -22
  167. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_maps.py +8 -7
  168. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +8 -4
  169. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +8 -4
  170. junifer/markers/functional_connectivity/tests/test_functional_connectivity_maps.py +8 -7
  171. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +9 -5
  172. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +8 -12
  173. junifer/markers/maps_aggregation.py +77 -55
  174. junifer/markers/parcel_aggregation.py +91 -70
  175. junifer/markers/reho/__init__.pyi +7 -1
  176. junifer/markers/reho/_afni_reho.py +23 -14
  177. junifer/markers/reho/_junifer_reho.py +112 -123
  178. junifer/markers/reho/reho_base.py +115 -37
  179. junifer/markers/reho/reho_maps.py +18 -32
  180. junifer/markers/reho/reho_parcels.py +26 -41
  181. junifer/markers/reho/reho_spheres.py +33 -54
  182. junifer/markers/reho/tests/test_junifer_reho.py +178 -0
  183. junifer/markers/reho/tests/test_reho_maps.py +27 -25
  184. junifer/markers/reho/tests/test_reho_parcels.py +28 -22
  185. junifer/markers/reho/tests/test_reho_spheres.py +16 -12
  186. junifer/markers/sphere_aggregation.py +88 -72
  187. junifer/markers/temporal_snr/temporal_snr_base.py +32 -30
  188. junifer/markers/temporal_snr/temporal_snr_maps.py +9 -18
  189. junifer/markers/temporal_snr/temporal_snr_parcels.py +19 -27
  190. junifer/markers/temporal_snr/temporal_snr_spheres.py +27 -41
  191. junifer/markers/temporal_snr/tests/test_temporal_snr_base.py +2 -2
  192. junifer/markers/temporal_snr/tests/test_temporal_snr_maps.py +17 -22
  193. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +11 -3
  194. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +9 -9
  195. junifer/markers/tests/test_brainprint.py +10 -9
  196. junifer/markers/tests/test_ets_rss.py +7 -3
  197. junifer/markers/tests/test_lost_regions.py +279 -0
  198. junifer/markers/tests/test_maps_aggregation.py +67 -114
  199. junifer/markers/tests/test_marker_utils.py +40 -1
  200. junifer/markers/tests/test_markers_base.py +275 -6
  201. junifer/markers/tests/test_parcel_aggregation.py +91 -53
  202. junifer/markers/tests/test_sphere_aggregation.py +53 -47
  203. junifer/markers/utils.py +267 -5
  204. junifer/onthefly/__init__.py +2 -3
  205. junifer/onthefly/__init__.pyi +4 -0
  206. junifer/onthefly/_brainprint.py +3 -5
  207. junifer/onthefly/py.typed +0 -0
  208. junifer/onthefly/read_transform.py +11 -9
  209. junifer/onthefly/tests/test_read_transform.py +7 -7
  210. junifer/pipeline/__init__.pyi +4 -2
  211. junifer/pipeline/marker_collection.py +14 -10
  212. junifer/pipeline/pipeline_component_registry.py +25 -6
  213. junifer/pipeline/pipeline_step_mixin.py +5 -12
  214. junifer/pipeline/tests/test_data_object_dumper.py +10 -10
  215. junifer/pipeline/tests/test_marker_collection.py +145 -168
  216. junifer/pipeline/tests/test_pipeline_component_registry.py +52 -8
  217. junifer/pipeline/tests/test_pipeline_step_mixin.py +12 -26
  218. junifer/pipeline/tests/test_update_meta_mixin.py +4 -5
  219. junifer/pipeline/tests/test_workdir_manager.py +39 -0
  220. junifer/pipeline/update_meta_mixin.py +13 -10
  221. junifer/pipeline/utils.py +22 -17
  222. junifer/pipeline/workdir_manager.py +41 -32
  223. junifer/preprocess/__init__.pyi +9 -4
  224. junifer/preprocess/_temporal_filter.py +34 -33
  225. junifer/preprocess/_temporal_slicer.py +17 -30
  226. junifer/preprocess/base.py +99 -55
  227. junifer/preprocess/confounds/__init__.pyi +10 -2
  228. junifer/preprocess/confounds/fmriprep_confound_remover.py +87 -109
  229. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +64 -45
  230. junifer/preprocess/smoothing/__init__.pyi +2 -2
  231. junifer/preprocess/smoothing/_afni_smoothing.py +8 -7
  232. junifer/preprocess/smoothing/_fsl_smoothing.py +6 -4
  233. junifer/preprocess/smoothing/_nilearn_smoothing.py +7 -5
  234. junifer/preprocess/smoothing/smoothing.py +59 -40
  235. junifer/preprocess/smoothing/tests/test_smoothing.py +28 -8
  236. junifer/preprocess/tests/test_preprocess_base.py +17 -9
  237. junifer/preprocess/tests/test_temporal_filter.py +37 -16
  238. junifer/preprocess/tests/test_temporal_slicer.py +12 -12
  239. junifer/preprocess/warping/__init__.pyi +2 -2
  240. junifer/preprocess/warping/_ants_warper.py +56 -40
  241. junifer/preprocess/warping/_fsl_warper.py +49 -36
  242. junifer/preprocess/warping/_utils.py +106 -0
  243. junifer/preprocess/warping/space_warper.py +99 -98
  244. junifer/preprocess/warping/tests/test_space_warper.py +46 -41
  245. junifer/preprocess/warping/tests/test_warping_utils.py +163 -0
  246. junifer/stats.py +46 -34
  247. junifer/storage/__init__.pyi +8 -4
  248. junifer/storage/base.py +129 -64
  249. junifer/storage/hdf5.py +307 -199
  250. junifer/storage/pandas_base.py +46 -24
  251. junifer/storage/sqlite.py +99 -62
  252. junifer/storage/tests/data/collected_h5io_fork.hdf5 +0 -0
  253. junifer/storage/tests/test_hdf5.py +147 -12
  254. junifer/storage/tests/test_repeated_storage.py +293 -0
  255. junifer/storage/tests/test_sqlite.py +9 -20
  256. junifer/storage/tests/test_storage_base.py +18 -21
  257. junifer/storage/tests/test_utils.py +83 -22
  258. junifer/storage/utils.py +58 -38
  259. junifer/testing/__init__.pyi +2 -2
  260. junifer/testing/datagrabbers.py +480 -44
  261. junifer/testing/registry.py +12 -0
  262. junifer/testing/tests/test_adhdtesting_datagrabber.py +24 -0
  263. junifer/testing/tests/test_oasisvmbtesting_datagrabber.py +5 -0
  264. junifer/testing/tests/test_partlycloudytesting_datagrabber.py +52 -0
  265. junifer/testing/tests/test_spmauditory_datagrabber.py +16 -0
  266. junifer/testing/tests/test_testing_registry.py +31 -0
  267. junifer/testing/tests/test_testing_utils.py +35 -0
  268. junifer/testing/tests/test_uclacnpvbmtesting_datagrabber.py +81 -0
  269. junifer/testing/utils.py +33 -1
  270. junifer/tests/test_stats.py +1 -5
  271. junifer/typing/__init__.pyi +2 -0
  272. junifer/typing/_typing.py +34 -19
  273. junifer/typing/py.typed +0 -0
  274. junifer/utils/__init__.pyi +10 -1
  275. junifer/utils/_config.py +1 -2
  276. junifer/utils/helpers.py +68 -2
  277. junifer/utils/logging.py +31 -7
  278. junifer/utils/tests/test_helpers.py +36 -1
  279. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/METADATA +43 -35
  280. junifer-0.0.8.dist-info/RECORD +402 -0
  281. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/WHEEL +1 -1
  282. junifer/cli/tests/test_parser.py +0 -280
  283. junifer/external/h5io/h5io/__init__.py +0 -15
  284. junifer/external/h5io/h5io/_h5io.py +0 -767
  285. junifer/external/h5io/h5io/_version.py +0 -1
  286. junifer/external/h5io/h5io/chunked_array.py +0 -94
  287. junifer/external/h5io/h5io/chunked_list.py +0 -65
  288. junifer/markers/complexity/__init__.pyi +0 -17
  289. junifer-0.0.7.dev332.dist-info/RECORD +0 -353
  290. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/entry_points.txt +0 -0
  291. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/AUTHORS.rst +0 -0
  292. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/LICENSE.md +0 -0
  293. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/top_level.txt +0 -0
@@ -1,767 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # Authors: Eric Larson <larson.eric.d@gmail.com>
3
- #
4
- # License: BSD (3-clause)
5
-
6
- import datetime
7
- import json
8
- import tempfile
9
- from shutil import rmtree
10
- from os import path as op
11
- from io import UnsupportedOperation
12
-
13
- import numpy as np
14
-
15
- from .chunked_array import ChunkedArray
16
- from .chunked_list import ChunkedList
17
-
18
- special_chars = {'{FWDSLASH}': '/'}
19
- tab_str = '----'
20
-
21
-
22
- def _import_sparse():
23
- try:
24
- from scipy import sparse
25
- except ImportError:
26
- sparse = None
27
- return sparse
28
-
29
-
30
- ##############################################################################
31
- # WRITING
32
-
33
- def _check_h5py():
34
- """Helper to check if h5py is installed"""
35
- try:
36
- import h5py
37
- except ImportError:
38
- raise ImportError('the h5py module is required to use HDF5 I/O')
39
- return h5py
40
-
41
-
42
- def _create_titled_group(root, key, title):
43
- """Helper to create a titled group in h5py"""
44
- out = root.create_group(key)
45
- out.attrs['TITLE'] = title
46
- return out
47
-
48
-
49
- def _create_titled_dataset(root, key, title, data, comp_kw=None):
50
- """Helper to create a titled dataset in h5py"""
51
- comp_kw = {} if comp_kw is None else comp_kw
52
- out = root.create_dataset(key, data=data, **comp_kw)
53
- out.attrs['TITLE'] = title
54
- return out
55
-
56
-
57
- def _create_pandas_dataset(fname, root, key, title, data):
58
- h5py = _check_h5py()
59
- rootpath = '/'.join([root, key])
60
- data.to_hdf(fname, rootpath)
61
- with h5py.File(fname, mode='a') as fid:
62
- fid[rootpath].attrs['TITLE'] = 'pd_dataframe'
63
-
64
-
65
- def _create_write_chunked_array(root, key, name, value):
66
- if key not in root:
67
- dset = root.create_dataset(key, value.shape, chunks=value.chunk_size)
68
- dset.attrs['TITLE'] = name
69
- else:
70
- dset = root[key]
71
- stored_shape = dset.shape
72
- stored_chunk_size = dset.chunks
73
- if np.all(stored_shape != value.shape):
74
- raise ValueError(
75
- f"The stored shape ({stored_shape}) does not match the given "
76
- f"shape ({value.shape})."
77
- )
78
- if np.all(stored_chunk_size != value.chunk_size):
79
- raise ValueError(
80
- f"The stored chunk size ({stored_chunk_size}) does not match "
81
- f"the given chunk size ({value.chunk_size})."
82
- )
83
- dset[value.chunkslice] = value.chunkdata
84
-
85
-
86
- def write_hdf5(fname, data, overwrite=False, compression=4,
87
- title='h5io', slash='error', use_json=False):
88
- """Write python object to HDF5 format using h5py.
89
-
90
- Parameters
91
- ----------
92
- fname : str
93
- Filename to use.
94
- data : object
95
- Object to write. Can be of any of these types:
96
-
97
- {ndarray, dict, list, tuple, int, float, str, datetime, timezone}
98
-
99
- Note that dict objects must only have ``str`` keys. It is recommended
100
- to use ndarrays where possible, as it is handled most efficiently.
101
- overwrite : True | False | 'update'
102
- If True, overwrite file (if it exists). If 'update', appends the title
103
- to the file (or replace value if title exists).
104
- compression : int
105
- Compression level to use (0-9) to compress data using gzip.
106
- title : str
107
- The top-level directory name to use. Typically it is useful to make
108
- this your package name, e.g. ``'mnepython'``.
109
- slash : 'error' | 'replace'
110
- Whether to replace forward-slashes ('/') in any key found nested within
111
- keys in data. This does not apply to the top level name (title).
112
- If 'error', '/' is not allowed in any lower-level keys.
113
- use_json : bool
114
- To accelerate the read and write performance of small dictionaries and
115
- lists they can be combined to JSON objects and stored as strings.
116
- """
117
- h5py = _check_h5py()
118
- if isinstance(fname, str):
119
- mode = 'w'
120
- if op.isfile(fname):
121
- if isinstance(overwrite, str):
122
- if overwrite != 'update':
123
- raise ValueError('overwrite must be "update" or a bool')
124
- mode = 'a'
125
- elif not overwrite:
126
- raise IOError(
127
- 'file "%s" exists, use overwrite=True to overwrite' % fname
128
- )
129
- elif isinstance(fname, h5py.File):
130
- if fname.mode == 'r':
131
- raise UnsupportedOperation('not writable')
132
- else:
133
- raise ValueError(f'fname must be str or h5py.File, got {type(fname)}')
134
- if not isinstance(title, str):
135
- raise ValueError('title must be a string')
136
- comp_kw = dict()
137
- if compression > 0:
138
- comp_kw = dict(compression='gzip', compression_opts=compression)
139
-
140
- def _write(fid, cleanup_data):
141
- # if title in fid and not isinstance(data, ChunkedArray):
142
- # del fid[title]
143
- _triage_write(title, data, fid, comp_kw, str(type(data)),
144
- cleanup_data, slash=slash, title=title,
145
- use_json=use_json)
146
- cleanup_data = []
147
- if isinstance(fname, h5py.File):
148
- _write(fname, cleanup_data)
149
- else:
150
- with h5py.File(fname, mode=mode) as fid:
151
- _write(fid, cleanup_data)
152
-
153
- # Will not be empty if any extra data to be written
154
- for data in cleanup_data:
155
- # In case different extra I/O needs different inputs
156
- title = list(data.keys())[0]
157
- if title in ['pd_dataframe', 'pd_series']:
158
- rootname, key, value = data[title]
159
- _create_pandas_dataset(fname, rootname, key, title, value)
160
-
161
-
162
- def _get_sub_root(root, key, title):
163
- """Helper to get a sub-root in h5py"""
164
- if key not in root:
165
- sub_root = _create_titled_group(root, key, title)
166
- else:
167
- sub_root = root[key]
168
- if sub_root.attrs["TITLE"] != title:
169
- # If the title is wrong, delete the old one and create a new one
170
- del root[key]
171
- sub_root = _create_titled_group(root, key, title)
172
- return sub_root
173
-
174
-
175
- def _triage_write(key, value, root, comp_kw, where,
176
- cleanup_data, slash='error', title=None,
177
- use_json=False):
178
- sparse = _import_sparse()
179
- if key != title and '/' in key:
180
- if slash == 'error':
181
- raise ValueError('Found a key with "/", '
182
- 'this is not allowed if slash == error')
183
- elif slash == 'replace':
184
- # Auto-replace keys with proper values
185
- for key_spec, val_spec in special_chars.items():
186
- key = key.replace(val_spec, key_spec)
187
- else:
188
- raise ValueError("slash must be one of ['error', 'replace'")
189
-
190
- if use_json and isinstance(value, (list, dict)) and \
191
- _json_compatible(value, slash=slash):
192
- value = np.frombuffer(json.dumps(value).encode('utf-8'), np.uint8)
193
- if key in root:
194
- del root[key]
195
- _create_titled_dataset(root, key, 'json', value, comp_kw)
196
- elif isinstance(value, dict):
197
- sub_root = _get_sub_root(root, key, 'dict')
198
- for key, sub_value in value.items():
199
- if not isinstance(key, str):
200
- raise TypeError('All dict keys must be strings')
201
- _triage_write(
202
- 'key_{0}'.format(key), sub_value, sub_root, comp_kw,
203
- where + '["%s"]' % key, cleanup_data=cleanup_data, slash=slash)
204
- # Delete any keys that are no longer in the dict
205
- for key, subnode in sub_root.items():
206
- if slash == 'replace':
207
- for key_spec, val_spec in special_chars.items():
208
- key = key.replace(key_spec, val_spec)
209
- if key[4:] not in value:
210
- del sub_root[key]
211
- elif isinstance(value, (list, tuple)):
212
- title = 'list' if isinstance(value, list) else 'tuple'
213
- sub_root = _get_sub_root(root, key, title)
214
- for vi, sub_value in enumerate(value):
215
- _triage_write(
216
- 'idx_{0}'.format(vi), sub_value, sub_root, comp_kw,
217
- where + '[%s]' % vi, cleanup_data=cleanup_data, slash=slash)
218
- # Delete any keys that are no longer in the list
219
- ii = len(value)
220
- while True:
221
- t_key = 'idx_{0}'.format(ii)
222
- subnode = sub_root.get(t_key, None)
223
- if subnode is None:
224
- break
225
- del sub_root[t_key]
226
- elif isinstance(value, ChunkedList):
227
- sub_root = _get_sub_root(root, key, 'chunkedlist')
228
- if 'SIZE' not in sub_root.attrs:
229
- sub_root.attrs["SIZE"] = value.size
230
- elif sub_root.attrs["SIZE"] != value.size:
231
- # Size mismatch, asume it's a new list
232
- del root[key]
233
- sub_root = _get_sub_root(root, key, 'chunkedlist')
234
- sub_root.attrs["SIZE"] = value.size
235
-
236
- for vi, sub_value in value.enumerate:
237
- _triage_write(
238
- 'idx_{0}'.format(vi), sub_value, sub_root, comp_kw,
239
- where + '[%s]' % vi, cleanup_data=cleanup_data, slash=slash)
240
-
241
- elif isinstance(value, type(None)):
242
- if key in root:
243
- del root[key]
244
- _create_titled_dataset(root, key, 'None', [False])
245
- elif isinstance(value, (int, float)):
246
- if key in root:
247
- del root[key]
248
- if isinstance(value, int):
249
- title = 'int'
250
- else: # isinstance(value, float):
251
- title = 'float'
252
- _create_titled_dataset(root, key, title, np.atleast_1d(value))
253
- elif isinstance(value, datetime.datetime):
254
- title = 'datetime'
255
- value = np.frombuffer(value.isoformat().encode('utf-8'), np.uint8)
256
- if key in root:
257
- del root[key]
258
- _create_titled_dataset(root, key, title, value)
259
- elif isinstance(value, datetime.timezone):
260
- title = 'timezone' # the __repr__ is complete
261
- value = np.frombuffer(repr(value).encode('utf-8'), np.uint8)
262
- if key in root:
263
- del root[key]
264
- _create_titled_dataset(root, key, title, value)
265
- elif isinstance(value, (np.integer, np.floating, np.bool_)):
266
- title = 'np_{0}'.format(value.__class__.__name__)
267
- if key in root:
268
- del root[key]
269
- _create_titled_dataset(root, key, title, np.atleast_1d(value))
270
- elif isinstance(value, str):
271
- if isinstance(value, str): # unicode
272
- value = np.frombuffer(value.encode('utf-8'), np.uint8)
273
- title = 'unicode'
274
- else:
275
- value = np.frombuffer(value.encode('ASCII'), np.uint8)
276
- title = 'ascii'
277
- if key in root:
278
- del root[key]
279
- _create_titled_dataset(root, key, title, value, comp_kw)
280
- elif isinstance(value, np.ndarray):
281
- if not (value.dtype == np.dtype('object') and
282
- len(set([sub.dtype for sub in value])) == 1):
283
- if key in root:
284
- del root[key]
285
- _create_titled_dataset(root, key, 'ndarray', value)
286
- else:
287
- ma_index, ma_data = multiarray_dump(value)
288
- if key in root:
289
- del root[key]
290
- sub_root = _create_titled_group(root, key, 'multiarray')
291
- _create_titled_dataset(sub_root, 'index', 'ndarray', ma_index)
292
- _create_titled_dataset(sub_root, 'data', 'ndarray', ma_data)
293
- elif sparse is not None and isinstance(value, sparse.csc_matrix):
294
- if key in root:
295
- del root[key]
296
- sub_root = _create_titled_group(root, key, 'csc_matrix')
297
- _triage_write('data', value.data, sub_root, comp_kw,
298
- where + '.csc_matrix_data', cleanup_data=cleanup_data,
299
- slash=slash)
300
- _triage_write('indices', value.indices, sub_root, comp_kw,
301
- where + '.csc_matrix_indices', cleanup_data=cleanup_data,
302
- slash=slash)
303
- _triage_write('indptr', value.indptr, sub_root, comp_kw,
304
- where + '.csc_matrix_indptr', cleanup_data=cleanup_data,
305
- slash=slash)
306
- elif sparse is not None and isinstance(value, sparse.csr_matrix):
307
- if key in root:
308
- del root[key]
309
- sub_root = _create_titled_group(root, key, 'csr_matrix')
310
- _triage_write('data', value.data, sub_root, comp_kw,
311
- where + '.csr_matrix_data', cleanup_data=cleanup_data,
312
- slash=slash)
313
- _triage_write('indices', value.indices, sub_root, comp_kw,
314
- where + '.csr_matrix_indices', cleanup_data=cleanup_data,
315
- slash=slash)
316
- _triage_write('indptr', value.indptr, sub_root, comp_kw,
317
- where + '.csr_matrix_indptr', cleanup_data=cleanup_data,
318
- slash=slash)
319
- _triage_write('shape', value.shape, sub_root, comp_kw,
320
- where + '.csr_matrix_shape', cleanup_data=cleanup_data,
321
- slash=slash)
322
- elif isinstance(value, ChunkedArray):
323
- _create_write_chunked_array(root, key, 'chunkedarray', value)
324
- else:
325
- try:
326
- from pandas import DataFrame, Series
327
- except ImportError:
328
- pass
329
- else:
330
- if isinstance(value, (DataFrame, Series)):
331
- if isinstance(value, DataFrame):
332
- title = 'pd_dataframe'
333
- else:
334
- title = 'pd_series'
335
- rootname = root.name
336
- cleanup_data.append({title: (rootname, key, value)})
337
- return
338
-
339
- err_str = 'unsupported type %s (in %s)' % (type(value), where)
340
- raise TypeError(err_str)
341
-
342
- ##############################################################################
343
- # READING
344
-
345
-
346
- def read_hdf5(fname, title='h5io', slash='ignore'):
347
- """Read python object from HDF5 format using h5py
348
-
349
- Parameters
350
- ----------
351
- fname : str
352
- File to load.
353
- title : str
354
- The top-level directory name to use. Typically it is useful to make
355
- this your package name, e.g. ``'mnepython'``.
356
- slash : 'ignore' | 'replace'
357
- Whether to replace the string {FWDSLASH} with the value /. This does
358
- not apply to the top level name (title). If 'ignore', nothing will be
359
- replaced.
360
-
361
- Returns
362
- -------
363
- data : object
364
- The loaded data. Can be of any type supported by ``write_hdf5``.
365
- """
366
- h5py = _check_h5py()
367
- if isinstance(fname, str):
368
- if not op.isfile(fname):
369
- raise IOError('file "%s" not found' % fname)
370
- elif isinstance(fname, h5py.File):
371
- if fname.mode == 'w':
372
- raise UnsupportedOperation(
373
- 'file must not be opened be opened with "w"'
374
- )
375
- print(fname.mode)
376
- else:
377
- raise ValueError(f'fname must be str or h5py.File, got {type(fname)}')
378
- if not isinstance(title, str):
379
- raise ValueError('title must be a string')
380
-
381
- def _read(fid):
382
- if title not in fid:
383
- raise ValueError('no "%s" data found' % title)
384
- if isinstance(fid[title], h5py.Group):
385
- if 'TITLE' not in fid[title].attrs:
386
- raise ValueError('no "%s" data found' % title)
387
- return _triage_read(fid[title], slash=slash)
388
- if isinstance(fname, h5py.File):
389
- return _read(fname)
390
- else:
391
- with h5py.File(fname, mode='r') as fid:
392
- return _read(fid)
393
-
394
-
395
- def _triage_read(node, slash='ignore'):
396
- if slash not in ['ignore', 'replace']:
397
- raise ValueError("slash must be one of 'replace', 'ignore'")
398
- h5py = _check_h5py()
399
- sparse = _import_sparse()
400
- type_str = node.attrs['TITLE']
401
- if isinstance(type_str, bytes):
402
- type_str = type_str.decode()
403
- if isinstance(node, h5py.Group):
404
- if type_str == 'dict':
405
- data = dict()
406
- for key, subnode in node.items():
407
- if slash == 'replace':
408
- for key_spec, val_spec in special_chars.items():
409
- key = key.replace(key_spec, val_spec)
410
- data[key[4:]] = _triage_read(subnode, slash=slash)
411
- elif type_str in ['list', 'tuple']:
412
- data = list()
413
- ii = 0
414
- while True:
415
- subnode = node.get('idx_{0}'.format(ii), None)
416
- if subnode is None:
417
- break
418
- data.append(_triage_read(subnode, slash=slash))
419
- ii += 1
420
- assert len(data) == ii
421
- data = tuple(data) if type_str == 'tuple' else data
422
- return data
423
- elif type_str == 'chunkedlist':
424
- data = list()
425
- ii = 0
426
- n_elems = node.attrs['SIZE']
427
- for ii in range(n_elems):
428
- subnode = node.get('idx_{0}'.format(ii), None)
429
- if subnode is None:
430
- data.append(None)
431
- else:
432
- data.append(_triage_read(subnode, slash=slash))
433
- assert len(data) == n_elems
434
- elif type_str == 'csc_matrix':
435
- if sparse is None:
436
- raise RuntimeError('scipy must be installed to read this data')
437
- data = sparse.csc_matrix((_triage_read(node['data'], slash=slash),
438
- _triage_read(node['indices'],
439
- slash=slash),
440
- _triage_read(node['indptr'],
441
- slash=slash)))
442
- elif type_str == 'csr_matrix':
443
- if sparse is None:
444
- raise RuntimeError('scipy must be installed to read this data')
445
- data = sparse.csr_matrix((_triage_read(node['data'], slash=slash),
446
- _triage_read(node['indices'],
447
- slash=slash),
448
- _triage_read(node['indptr'],
449
- slash=slash)),
450
- shape=_triage_read(node['shape']))
451
- elif type_str in ['pd_dataframe', 'pd_series']:
452
- from pandas import read_hdf, HDFStore
453
- rootname = node.name
454
- filename = node.file.filename
455
- with HDFStore(filename, 'r') as tmpf:
456
- data = read_hdf(tmpf, rootname)
457
- elif type_str == 'multiarray':
458
- ma_index = _triage_read(node.get('index', None), slash=slash)
459
- ma_data = _triage_read(node.get('data', None), slash=slash)
460
- data = multiarray_load(ma_index, ma_data)
461
- else:
462
- raise NotImplementedError('Unknown group type: {0}'
463
- ''.format(type_str))
464
- elif type_str in ('ndarray', 'chunkedarray'):
465
- data = np.array(node)
466
- elif type_str in ('int', 'float'):
467
- cast = int if type_str == 'int' else float
468
- data = cast(np.array(node)[0])
469
- elif type_str == 'datetime':
470
- data = str(np.array(node).tobytes().decode('utf-8'))
471
- data = datetime.datetime.fromisoformat(data)
472
- elif type_str == 'timezone':
473
- data = eval(str(np.array(node).tobytes().decode('utf-8')),
474
- {'datetime': datetime})
475
- elif type_str.startswith('np_'):
476
- np_type = type_str.split('_')[1]
477
- cast = getattr(np, np_type) if np_type != 'bool' else bool
478
- data = np.array(node)[0].astype(cast)
479
- elif type_str in ('unicode', 'ascii', 'str'): # 'str' for backward compat
480
- decoder = 'utf-8' if type_str == 'unicode' else 'ASCII'
481
- data = str(np.array(node).tobytes().decode(decoder))
482
- elif type_str == 'json':
483
- node_unicode = str(np.array(node).tobytes().decode('utf-8'))
484
- data = json.loads(node_unicode)
485
- elif type_str == 'None':
486
- data = None
487
- else:
488
- raise TypeError('Unknown node type: {0}'.format(type_str))
489
- return data
490
-
491
-
492
- # ############################################################################
493
- # UTILITIES
494
-
495
- def _sort_keys(x):
496
- """Sort and return keys of dict"""
497
- keys = list(x.keys()) # note: not thread-safe
498
- idx = np.argsort([str(k) for k in keys])
499
- keys = [keys[ii] for ii in idx]
500
- return keys
501
-
502
-
503
- def object_diff(a, b, pre=''):
504
- """Compute all differences between two python variables
505
-
506
- Parameters
507
- ----------
508
- a : object
509
- Currently supported: dict, list, tuple, ndarray, int, str, bytes,
510
- float.
511
- b : object
512
- Must be same type as x1.
513
- pre : str
514
- String to prepend to each line.
515
-
516
- Returns
517
- -------
518
- diffs : str
519
- A string representation of the differences.
520
- """
521
- sparse = _import_sparse()
522
- try:
523
- from pandas import DataFrame, Series
524
- except ImportError:
525
- DataFrame = Series = type(None)
526
-
527
- out = ''
528
- if type(a) != type(b):
529
- out += pre + ' type mismatch (%s, %s)\n' % (type(a), type(b))
530
- elif isinstance(a, dict):
531
- k1s = _sort_keys(a)
532
- k2s = _sort_keys(b)
533
- m1 = set(k2s) - set(k1s)
534
- if len(m1):
535
- out += pre + ' x1 missing keys %s\n' % (m1)
536
- for key in k1s:
537
- if key not in k2s:
538
- out += pre + ' x2 missing key %s\n' % key
539
- else:
540
- out += object_diff(a[key], b[key], pre + 'd1[%s]' % repr(key))
541
- elif isinstance(a, (list, tuple)):
542
- if len(a) != len(b):
543
- out += pre + ' length mismatch (%s, %s)\n' % (len(a), len(b))
544
- else:
545
- for xx1, xx2 in zip(a, b):
546
- out += object_diff(xx1, xx2, pre='')
547
- elif isinstance(a, (str, int, float, bytes)):
548
- if a != b:
549
- out += pre + ' value mismatch (%s, %s)\n' % (a, b)
550
- elif a is None:
551
- pass # b must be None due to our type checking
552
- elif isinstance(a, np.ndarray):
553
- if not np.array_equal(a, b):
554
- out += pre + ' array mismatch\n'
555
- elif sparse is not None and sparse.isspmatrix(a):
556
- # sparsity and sparse type of b vs a already checked above by type()
557
- if b.shape != a.shape:
558
- out += pre + (' sparse matrix a and b shape mismatch'
559
- '(%s vs %s)' % (a.shape, b.shape))
560
- else:
561
- c = a - b
562
- c.eliminate_zeros()
563
- if c.nnz > 0:
564
- out += pre + (' sparse matrix a and b differ on %s '
565
- 'elements' % c.nnz)
566
- elif isinstance(a, (DataFrame, Series)):
567
- if b.shape != a.shape:
568
- out += pre + (' pandas values a and b shape mismatch'
569
- '(%s vs %s)' % (a.shape, b.shape))
570
- else:
571
- c = a.values - b.values
572
- nzeros = np.sum(c != 0)
573
- if nzeros > 0:
574
- out += pre + (' pandas values a and b differ on %s '
575
- 'elements' % nzeros)
576
- else:
577
- raise RuntimeError(pre + ': unsupported type %s (%s)' % (type(a), a))
578
- return out
579
-
580
-
581
- class _TempDir(str):
582
- """Class for creating and auto-destroying temp dir
583
-
584
- This is designed to be used with testing modules. Instances should be
585
- defined inside test functions. Instances defined at module level can not
586
- guarantee proper destruction of the temporary directory.
587
-
588
- When used at module level, the current use of the __del__() method for
589
- cleanup can fail because the rmtree function may be cleaned up before this
590
- object (an alternative could be using the atexit module instead).
591
- """
592
- def __new__(self):
593
- new = str.__new__(self, tempfile.mkdtemp())
594
- return new
595
-
596
- def __init__(self):
597
- self._path = self.__str__()
598
-
599
- def __del__(self):
600
- rmtree(self._path, ignore_errors=True)
601
-
602
-
603
- def _list_file_contents(h5file):
604
- if 'h5io' not in h5file.keys():
605
- raise ValueError('h5file must contain h5io data')
606
-
607
- # Set up useful variables for later
608
- h5file = h5file['h5io']
609
- root_title = h5file.attrs['TITLE']
610
- n_space = np.max([(len(key), len(val.attrs['TITLE']))
611
- for key, val in h5file.items()]) + 2
612
-
613
- # Create print strings
614
- strs = ['Root type: %s | Items: %s\n' % (root_title, len(h5file))]
615
- for key, data in h5file.items():
616
- type_str = data.attrs['TITLE']
617
- str_format = '%%-%ss' % n_space
618
- if type_str == 'ndarray':
619
- desc = 'Shape: %s'
620
- desc_val = data.shape
621
- elif type_str in ['pd_dataframe', 'pd_series']:
622
- desc = 'Shape: %s'
623
- desc_val = data['values'].shape
624
- elif type_str in ('unicode', 'ascii', 'str'):
625
- desc = 'Text: %s'
626
- decoder = 'utf-8' if type_str == 'unicode' else 'ASCII'
627
- data = str(np.array(data).tobytes().decode(decoder))
628
- desc_val = data[:10] + '...' if len(data) > 10 else data
629
- else:
630
- desc = 'Items: %s'
631
- desc_val = len(data)
632
- this_str = ('%%s Key: %s | Type: %s | ' + desc) % (
633
- str_format, str_format, str_format)
634
- this_str = this_str % (tab_str, key, type_str, desc_val)
635
- strs.append(this_str)
636
- out_str = '\n'.join(strs)
637
- print(out_str)
638
-
639
-
640
- def list_file_contents(h5file):
641
- """List the contents of an h5io file.
642
-
643
- This will list the root and one-level-deep contents of the file.
644
-
645
- Parameters
646
- ----------
647
- h5file : str
648
- The path to an h5io hdf5 file.
649
- """
650
- h5py = _check_h5py()
651
- err = 'h5file must be an h5py File object, not {0}'
652
- if isinstance(h5file, str):
653
- with h5py.File(h5file, 'r') as f:
654
- _list_file_contents(f)
655
- else:
656
- if not isinstance(h5file, h5py.File):
657
- raise TypeError(err.format(type(h5file)))
658
- _list_file_contents(h5file)
659
-
660
-
661
- def _json_compatible(obj, slash='error'):
662
- if isinstance(obj, (str, int, float, bool, type(None))):
663
- return True
664
- elif isinstance(obj, list):
665
- return all([_json_compatible(item) for item in obj])
666
- elif isinstance(obj, dict):
667
- _check_keys_in_dict(obj, slash=slash)
668
- return all([_json_compatible(item) for item in obj.values()])
669
- else:
670
- return False
671
-
672
-
673
- def _check_keys_in_dict(obj, slash='error'):
674
- repl = list()
675
- for key in obj.keys():
676
- if '/' in key:
677
- key_prev = key
678
- if slash == 'error':
679
- raise ValueError('Found a key with "/", '
680
- 'this is not allowed if slash == error')
681
- elif slash == 'replace':
682
- # Auto-replace keys with proper values
683
- for key_spec, val_spec in special_chars.items():
684
- key = key.replace(val_spec, key_spec)
685
- repl.append((key, key_prev))
686
- else:
687
- raise ValueError("slash must be one of ['error', 'replace'")
688
- for key, key_prev in repl:
689
- obj[key] = obj.pop(key_prev)
690
-
691
-
692
- ##############################################################################
693
- # Arrays with mixed dimensions
694
- def _validate_object_array(array):
695
- if not (array.dtype == np.dtype('object') and
696
- len(set([sub.dtype for sub in array])) == 1):
697
- raise TypeError('unsupported array type')
698
-
699
-
700
- def _shape_list(array):
701
- return [np.shape(sub) for sub in array]
702
-
703
-
704
- def _validate_sub_shapes(shape_lst):
705
- if not all([shape_lst[0][1:] == t[1:] for t in shape_lst]):
706
- raise ValueError('shape does not match!')
707
-
708
-
709
- def _array_index(shape_lst):
710
- return [t[0] for t in shape_lst]
711
-
712
-
713
- def _index_sum(index_lst):
714
- index_sum_lst = []
715
- for step in index_lst:
716
- if index_sum_lst != []:
717
- index_sum_lst.append(index_sum_lst[-1] + step)
718
- else:
719
- index_sum_lst.append(step)
720
- return index_sum_lst
721
-
722
-
723
- def _merge_array(array):
724
- merged_lst = []
725
- for sub in array:
726
- merged_lst += sub.tolist()
727
- return np.array(merged_lst)
728
-
729
-
730
- def multiarray_dump(array):
731
- _validate_object_array(array)
732
- shape_lst = _shape_list(array)
733
- _validate_sub_shapes(shape_lst=shape_lst)
734
- index_sum = _index_sum(index_lst=_array_index(shape_lst=shape_lst))
735
- return index_sum, _merge_array(array=array)
736
-
737
-
738
- def multiarray_load(index, array_merged):
739
- array_restore = []
740
- i_prev = 0
741
- for i in index[:-1]:
742
- array_restore.append(array_merged[i_prev:i])
743
- i_prev = i
744
- array_restore.append(array_merged[i_prev:])
745
- return np.array(array_restore, dtype=object)
746
-
747
-
748
- def has_hdf5(fname, title="h5io"):
749
- h5py = _check_h5py()
750
- if isinstance(fname, str):
751
- if not op.isfile(fname):
752
- raise IOError('file "%s" not found' % fname)
753
- elif isinstance(fname, h5py.File):
754
- if fname.mode == 'w':
755
- raise UnsupportedOperation(
756
- 'file must not be opened be opened with "w"'
757
- )
758
- print(fname.mode)
759
- else:
760
- raise ValueError(f'fname must be str or h5py.File, got {type(fname)}')
761
- if not isinstance(title, str):
762
- raise ValueError('title must be a string')
763
- if isinstance(fname, h5py.File):
764
- return title in fname
765
- else:
766
- with h5py.File(fname, mode='r') as fid:
767
- return title in fid