dclab 0.62.11__cp313-cp313-win_amd64.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 dclab might be problematic. Click here for more details.
- dclab/__init__.py +23 -0
- dclab/_version.py +16 -0
- dclab/cached.py +97 -0
- dclab/cli/__init__.py +10 -0
- dclab/cli/common.py +237 -0
- dclab/cli/task_compress.py +126 -0
- dclab/cli/task_condense.py +223 -0
- dclab/cli/task_join.py +229 -0
- dclab/cli/task_repack.py +98 -0
- dclab/cli/task_split.py +154 -0
- dclab/cli/task_tdms2rtdc.py +186 -0
- dclab/cli/task_verify_dataset.py +75 -0
- dclab/definitions/__init__.py +79 -0
- dclab/definitions/feat_const.py +202 -0
- dclab/definitions/feat_logic.py +183 -0
- dclab/definitions/meta_const.py +252 -0
- dclab/definitions/meta_logic.py +111 -0
- dclab/definitions/meta_parse.py +94 -0
- dclab/downsampling.cp313-win_amd64.pyd +0 -0
- dclab/downsampling.pyx +230 -0
- dclab/external/__init__.py +4 -0
- dclab/external/packaging/LICENSE +3 -0
- dclab/external/packaging/LICENSE.APACHE +177 -0
- dclab/external/packaging/LICENSE.BSD +23 -0
- dclab/external/packaging/__init__.py +6 -0
- dclab/external/packaging/_structures.py +61 -0
- dclab/external/packaging/version.py +505 -0
- dclab/external/skimage/LICENSE +28 -0
- dclab/external/skimage/__init__.py +2 -0
- dclab/external/skimage/_find_contours.py +216 -0
- dclab/external/skimage/_find_contours_cy.cp313-win_amd64.pyd +0 -0
- dclab/external/skimage/_find_contours_cy.pyx +188 -0
- dclab/external/skimage/_pnpoly.cp313-win_amd64.pyd +0 -0
- dclab/external/skimage/_pnpoly.pyx +99 -0
- dclab/external/skimage/_shared/__init__.py +1 -0
- dclab/external/skimage/_shared/geometry.cp313-win_amd64.pyd +0 -0
- dclab/external/skimage/_shared/geometry.pxd +6 -0
- dclab/external/skimage/_shared/geometry.pyx +55 -0
- dclab/external/skimage/measure.py +7 -0
- dclab/external/skimage/pnpoly.py +53 -0
- dclab/external/statsmodels/LICENSE +35 -0
- dclab/external/statsmodels/__init__.py +6 -0
- dclab/external/statsmodels/nonparametric/__init__.py +1 -0
- dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
- dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
- dclab/external/statsmodels/nonparametric/kernels.py +36 -0
- dclab/features/__init__.py +9 -0
- dclab/features/bright.py +81 -0
- dclab/features/bright_bc.py +93 -0
- dclab/features/bright_perc.py +63 -0
- dclab/features/contour.py +161 -0
- dclab/features/emodulus/__init__.py +339 -0
- dclab/features/emodulus/load.py +252 -0
- dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
- dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
- dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
- dclab/features/emodulus/pxcorr.py +135 -0
- dclab/features/emodulus/scale_linear.py +247 -0
- dclab/features/emodulus/viscosity.py +256 -0
- dclab/features/fl_crosstalk.py +95 -0
- dclab/features/inert_ratio.py +377 -0
- dclab/features/volume.py +242 -0
- dclab/http_utils.py +322 -0
- dclab/isoelastics/__init__.py +468 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
- dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
- dclab/kde_contours.py +222 -0
- dclab/kde_methods.py +303 -0
- dclab/lme4/__init__.py +5 -0
- dclab/lme4/lme4_template.R +94 -0
- dclab/lme4/rsetup.py +204 -0
- dclab/lme4/wrapr.py +386 -0
- dclab/polygon_filter.py +398 -0
- dclab/rtdc_dataset/__init__.py +15 -0
- dclab/rtdc_dataset/check.py +902 -0
- dclab/rtdc_dataset/config.py +533 -0
- dclab/rtdc_dataset/copier.py +353 -0
- dclab/rtdc_dataset/core.py +1001 -0
- dclab/rtdc_dataset/export.py +737 -0
- dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
- dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
- dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
- dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
- dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
- dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
- dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
- dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
- dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
- dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
- dclab/rtdc_dataset/feat_basin.py +550 -0
- dclab/rtdc_dataset/feat_temp.py +102 -0
- dclab/rtdc_dataset/filter.py +263 -0
- dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
- dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
- dclab/rtdc_dataset/fmt_dcor/api.py +111 -0
- dclab/rtdc_dataset/fmt_dcor/base.py +200 -0
- dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
- dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
- dclab/rtdc_dataset/fmt_dcor/tables.py +42 -0
- dclab/rtdc_dataset/fmt_dict.py +103 -0
- dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
- dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
- dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
- dclab/rtdc_dataset/fmt_hdf5/events.py +257 -0
- dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
- dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
- dclab/rtdc_dataset/fmt_hdf5/tables.py +30 -0
- dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
- dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
- dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
- dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
- dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
- dclab/rtdc_dataset/fmt_http.py +102 -0
- dclab/rtdc_dataset/fmt_s3.py +320 -0
- dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
- dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
- dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
- dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
- dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
- dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
- dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
- dclab/rtdc_dataset/load.py +72 -0
- dclab/rtdc_dataset/writer.py +985 -0
- dclab/statistics.py +203 -0
- dclab/util.py +156 -0
- dclab/warn.py +15 -0
- dclab-0.62.11.dist-info/LICENSE +343 -0
- dclab-0.62.11.dist-info/METADATA +146 -0
- dclab-0.62.11.dist-info/RECORD +137 -0
- dclab-0.62.11.dist-info/WHEEL +5 -0
- dclab-0.62.11.dist-info/entry_points.txt +8 -0
- dclab-0.62.11.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from ..feat_basin import Basin
|
|
4
|
+
|
|
5
|
+
from .api import REQUESTS_AVAILABLE, APIHandler, DCORAccessError
|
|
6
|
+
from .base import RTDC_DCOR
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
REGEXP_FULL_DCOR_URL = re.compile(
|
|
10
|
+
r"^https?:\/\/" # scheme
|
|
11
|
+
r"[a-z0-9-\.]*\.[a-z0-9-\.]*\/?api\/3\/action\/dcserv\?id=" # host and API
|
|
12
|
+
r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$") # id
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DCORBasin(Basin):
|
|
16
|
+
basin_format = "dcor"
|
|
17
|
+
basin_type = "remote"
|
|
18
|
+
|
|
19
|
+
def __init__(self, *args, **kwargs):
|
|
20
|
+
"""Access to private and public DCOR resources
|
|
21
|
+
|
|
22
|
+
Since version 2 of the DCOR data API, all feature data are
|
|
23
|
+
accessed via :class:`.HTTPBasin`s on S3. The DCOR basin is just
|
|
24
|
+
a wrapper around those `HTTPBasin`s.
|
|
25
|
+
|
|
26
|
+
For private resources, the DCOR format facilitates authentication
|
|
27
|
+
via access tokens. Behind the scenes, DCOR creates a pre-signed
|
|
28
|
+
URL to access private data on an S3 object storage provider.
|
|
29
|
+
Note that you must let dclab know your DCOR access
|
|
30
|
+
token via :func:`.APIHandler.add_api_key` for this to work.
|
|
31
|
+
|
|
32
|
+
The `location` must be a full DCOR URL, including the scheme
|
|
33
|
+
and netloc, e.g:
|
|
34
|
+
|
|
35
|
+
https://dcor.mpl.mpg.de/api/3/action/dcserv?
|
|
36
|
+
id=b1404eb5-f661-4920-be79-5ff4e85915d5
|
|
37
|
+
"""
|
|
38
|
+
self._available_verified = None
|
|
39
|
+
super(DCORBasin, self).__init__(*args, **kwargs)
|
|
40
|
+
|
|
41
|
+
def _load_dataset(self, location, **kwargs):
|
|
42
|
+
return RTDC_DCOR(location, **kwargs)
|
|
43
|
+
|
|
44
|
+
def is_available(self):
|
|
45
|
+
"""Check whether a DCOR resource is available
|
|
46
|
+
|
|
47
|
+
Notes
|
|
48
|
+
-----
|
|
49
|
+
- Make sure that your DCOR access token is stored in
|
|
50
|
+
:class:`.APIHandler`. You can add tokens with
|
|
51
|
+
:func:`.APIHandler.add_api_key`.
|
|
52
|
+
"""
|
|
53
|
+
with self._av_check_lock:
|
|
54
|
+
if not REQUESTS_AVAILABLE:
|
|
55
|
+
# don't even bother
|
|
56
|
+
self._available_verified = False
|
|
57
|
+
elif not is_full_dcor_url(self.location):
|
|
58
|
+
# not a full DCOR URL
|
|
59
|
+
self._available_verified = False
|
|
60
|
+
if self._available_verified is None:
|
|
61
|
+
api = APIHandler(self.location)
|
|
62
|
+
try:
|
|
63
|
+
self._available_verified = api.get("valid")
|
|
64
|
+
except DCORAccessError:
|
|
65
|
+
self._available_verified = False
|
|
66
|
+
return self._available_verified
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def is_full_dcor_url(string):
|
|
70
|
+
if not isinstance(string, str):
|
|
71
|
+
return False
|
|
72
|
+
else:
|
|
73
|
+
return REGEXP_FULL_DCOR_URL.match(string.strip())
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class DCORLogs:
|
|
2
|
+
def __init__(self, api):
|
|
3
|
+
self.api = api
|
|
4
|
+
self._logs_cache = None
|
|
5
|
+
|
|
6
|
+
def __contains__(self, key):
|
|
7
|
+
return key in self.keys()
|
|
8
|
+
|
|
9
|
+
def __getitem__(self, key):
|
|
10
|
+
return self._logs[key]
|
|
11
|
+
|
|
12
|
+
def __iter__(self):
|
|
13
|
+
for key in self.keys():
|
|
14
|
+
yield key
|
|
15
|
+
|
|
16
|
+
def __len__(self):
|
|
17
|
+
return len(self._logs)
|
|
18
|
+
|
|
19
|
+
def keys(self):
|
|
20
|
+
return self._logs.keys()
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def _logs(self):
|
|
24
|
+
if self._logs_cache is None:
|
|
25
|
+
self._logs_cache = self.api.get(query="logs")
|
|
26
|
+
return self._logs_cache
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DCORTables:
|
|
5
|
+
def __init__(self, api):
|
|
6
|
+
self.api = api
|
|
7
|
+
self._tables_cache = None
|
|
8
|
+
|
|
9
|
+
def __contains__(self, key):
|
|
10
|
+
return key in self.keys()
|
|
11
|
+
|
|
12
|
+
def __getitem__(self, key):
|
|
13
|
+
return self._tables[key]
|
|
14
|
+
|
|
15
|
+
def __iter__(self):
|
|
16
|
+
for key in self.keys():
|
|
17
|
+
yield key
|
|
18
|
+
|
|
19
|
+
def __len__(self):
|
|
20
|
+
return len(self._tables)
|
|
21
|
+
|
|
22
|
+
def keys(self):
|
|
23
|
+
return self._tables.keys()
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def _tables(self):
|
|
27
|
+
if self._tables_cache is None:
|
|
28
|
+
table_data = self.api.get(query="tables")
|
|
29
|
+
# assemble the tables
|
|
30
|
+
tables = {}
|
|
31
|
+
for key in table_data:
|
|
32
|
+
columns, data = table_data[key]
|
|
33
|
+
tab_data = np.asarray(data)
|
|
34
|
+
if columns is not None:
|
|
35
|
+
# We have a rec-array (named columns)
|
|
36
|
+
ds_dt = np.dtype({'names': columns,
|
|
37
|
+
'formats': [np.float64] * len(columns)})
|
|
38
|
+
tab_data = np.rec.array(tab_data, dtype=ds_dt)
|
|
39
|
+
tables[key] = tab_data
|
|
40
|
+
|
|
41
|
+
self._tables_cache = tables
|
|
42
|
+
return self._tables_cache
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""RT-DC dictionary format"""
|
|
2
|
+
import collections
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from .. import definitions as dfn
|
|
8
|
+
from ..util import hashobj
|
|
9
|
+
|
|
10
|
+
from .config import Configuration
|
|
11
|
+
from .core import RTDCBase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DictContourEvent:
|
|
15
|
+
def __init__(self, contours):
|
|
16
|
+
assert contours[0].shape[1] == 2
|
|
17
|
+
self.shape = (len(contours), np.nan, 2)
|
|
18
|
+
self.contours = contours
|
|
19
|
+
|
|
20
|
+
def __iter__(self):
|
|
21
|
+
return iter(self.contours)
|
|
22
|
+
|
|
23
|
+
def __getitem__(self, item):
|
|
24
|
+
return self.contours[item]
|
|
25
|
+
|
|
26
|
+
def __len__(self):
|
|
27
|
+
return len(self.contours)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DictTraceEvent(collections.UserDict):
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def shape(self):
|
|
34
|
+
key0 = sorted(self.keys())[0]
|
|
35
|
+
return len(self), len(self[key0]), len(self[key0][0])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RTDC_Dict(RTDCBase):
|
|
39
|
+
_local_basins_allowed = True
|
|
40
|
+
|
|
41
|
+
def __init__(self, ddict, *args, **kwargs):
|
|
42
|
+
"""Dictionary-based RT-DC dataset
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
ddict: dict
|
|
47
|
+
Dictionary with features as keys (valid features like
|
|
48
|
+
"area_cvx", "deform", "image" are defined by
|
|
49
|
+
`dclab.definitions.feature_exists`) with which the class
|
|
50
|
+
will be instantiated. The configuration is set to the
|
|
51
|
+
default configuration of dclab.
|
|
52
|
+
|
|
53
|
+
.. versionchanged:: 0.27.0
|
|
54
|
+
Scalar features are automatically converted to arrays.
|
|
55
|
+
*args:
|
|
56
|
+
Arguments for `RTDCBase`
|
|
57
|
+
**kwargs:
|
|
58
|
+
Keyword arguments for `RTDCBase`
|
|
59
|
+
"""
|
|
60
|
+
assert ddict
|
|
61
|
+
|
|
62
|
+
super(RTDC_Dict, self).__init__(*args, **kwargs)
|
|
63
|
+
|
|
64
|
+
t = time.localtime()
|
|
65
|
+
|
|
66
|
+
# Get an identifying string
|
|
67
|
+
keys = list(ddict.keys())
|
|
68
|
+
keys.sort()
|
|
69
|
+
ids = hashobj(ddict[keys[0]])
|
|
70
|
+
self._ids = ids
|
|
71
|
+
self.path = "none"
|
|
72
|
+
self.title = "{}_{:02d}_{:02d}/{}.dict".format(t[0], t[1], t[2], ids)
|
|
73
|
+
|
|
74
|
+
# Populate events
|
|
75
|
+
for feat in ddict:
|
|
76
|
+
if dfn.feature_exists(feat):
|
|
77
|
+
if dfn.scalar_feature_exists(feat):
|
|
78
|
+
data = np.array(ddict[feat])
|
|
79
|
+
elif feat == "contour":
|
|
80
|
+
data = DictContourEvent(ddict[feat])
|
|
81
|
+
elif feat == "trace":
|
|
82
|
+
data = DictTraceEvent(ddict[feat])
|
|
83
|
+
elif isinstance(ddict[feat], list):
|
|
84
|
+
# convert e.g. image data to arrays
|
|
85
|
+
data = np.array(ddict[feat])
|
|
86
|
+
else:
|
|
87
|
+
data = ddict[feat]
|
|
88
|
+
else:
|
|
89
|
+
raise ValueError("Invalid feature name '{}'".format(feat))
|
|
90
|
+
if isinstance(data, np.ndarray):
|
|
91
|
+
# Convert numpy array to read-only array
|
|
92
|
+
data = data.view()
|
|
93
|
+
data.setflags(write=False)
|
|
94
|
+
self._events[feat] = data
|
|
95
|
+
|
|
96
|
+
event_count = len(ddict[list(ddict.keys())[0]])
|
|
97
|
+
|
|
98
|
+
self.config = Configuration()
|
|
99
|
+
self.config["experiment"]["event count"] = event_count
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def hash(self):
|
|
103
|
+
return self._ids
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# flake8: noqa: F401
|
|
2
|
+
from .base import RTDC_HDF5, MIN_DCLAB_EXPORT_VERSION
|
|
3
|
+
from .basin import HDF5Basin # import means registering
|
|
4
|
+
from .events import (
|
|
5
|
+
H5Events, H5MaskEvent, H5TraceEvent, H5ScalarEvent, H5ContourEvent)
|
|
6
|
+
from .feat_defect import DEFECTIVE_FEATURES
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""RT-DC hdf5 format"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import io
|
|
5
|
+
import json
|
|
6
|
+
import pathlib
|
|
7
|
+
from typing import Any, BinaryIO, Dict
|
|
8
|
+
import warnings
|
|
9
|
+
|
|
10
|
+
import h5py
|
|
11
|
+
|
|
12
|
+
from ...external.packaging import parse as parse_version
|
|
13
|
+
from ...util import hashobj, hashfile
|
|
14
|
+
|
|
15
|
+
from ..config import Configuration
|
|
16
|
+
from ..core import RTDCBase
|
|
17
|
+
|
|
18
|
+
from . import events
|
|
19
|
+
from . import logs
|
|
20
|
+
from . import tables
|
|
21
|
+
|
|
22
|
+
#: rtdc files exported with dclab prior to this version are not supported
|
|
23
|
+
MIN_DCLAB_EXPORT_VERSION = "0.3.3.dev2"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OldFormatNotSupportedError(BaseException):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class UnknownKeyWarning(UserWarning):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RTDC_HDF5(RTDCBase):
|
|
35
|
+
def __init__(self,
|
|
36
|
+
h5path: str | pathlib.Path | BinaryIO | io.IOBase,
|
|
37
|
+
h5kwargs: Dict[str, Any] = None,
|
|
38
|
+
*args,
|
|
39
|
+
**kwargs):
|
|
40
|
+
"""HDF5 file format for RT-DC measurements
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
h5path: str or pathlib.Path or file-like object
|
|
45
|
+
Path to an '.rtdc' measurement file or a file-like object
|
|
46
|
+
h5kwargs: dict
|
|
47
|
+
Additional keyword arguments given to :class:`h5py.File`
|
|
48
|
+
*args:
|
|
49
|
+
Arguments for `RTDCBase`
|
|
50
|
+
**kwargs:
|
|
51
|
+
Keyword arguments for `RTDCBase`
|
|
52
|
+
|
|
53
|
+
Attributes
|
|
54
|
+
----------
|
|
55
|
+
path: pathlib.Path
|
|
56
|
+
Path to the experimental HDF5 (.rtdc) file
|
|
57
|
+
"""
|
|
58
|
+
super(RTDC_HDF5, self).__init__(*args, **kwargs)
|
|
59
|
+
|
|
60
|
+
# Any subclass from RTDC_HDF5 is probably a remote-type and should
|
|
61
|
+
# not be able to access local basins. If you do not agree, please
|
|
62
|
+
# enable this in the definition of the subclass.
|
|
63
|
+
self._local_basins_allowed = True if self.format == "hdf5" else False
|
|
64
|
+
|
|
65
|
+
if isinstance(h5path, (str, pathlib.Path)):
|
|
66
|
+
h5path = pathlib.Path(h5path)
|
|
67
|
+
else:
|
|
68
|
+
h5path = h5path
|
|
69
|
+
|
|
70
|
+
self._hash = None
|
|
71
|
+
self.path = h5path
|
|
72
|
+
|
|
73
|
+
# Increase the read cache (which defaults to 1MiB), since
|
|
74
|
+
# normally we have around 2.5MiB image chunks.
|
|
75
|
+
if h5kwargs is None:
|
|
76
|
+
h5kwargs = {}
|
|
77
|
+
h5kwargs.setdefault("rdcc_nbytes", 10 * 1024 ** 2)
|
|
78
|
+
h5kwargs.setdefault("rdcc_w0", 0)
|
|
79
|
+
|
|
80
|
+
self.h5kwargs = h5kwargs
|
|
81
|
+
self.h5file = h5py.File(h5path, **h5kwargs)
|
|
82
|
+
|
|
83
|
+
self._events = events.H5Events(self.h5file)
|
|
84
|
+
|
|
85
|
+
# Parse configuration
|
|
86
|
+
self.config = RTDC_HDF5.parse_config(self.h5file)
|
|
87
|
+
|
|
88
|
+
# Override logs property with HDF5 data
|
|
89
|
+
self.logs = logs.H5Logs(self.h5file)
|
|
90
|
+
|
|
91
|
+
# Override the tables property with HDF5 data
|
|
92
|
+
self.tables = tables.H5Tables(self.h5file)
|
|
93
|
+
|
|
94
|
+
# check version
|
|
95
|
+
rtdc_soft = self.config["setup"].get("software version", "unknown")
|
|
96
|
+
if rtdc_soft.startswith("dclab "):
|
|
97
|
+
rtdc_ver = parse_version(rtdc_soft.split(" ")[1])
|
|
98
|
+
if rtdc_ver < parse_version(MIN_DCLAB_EXPORT_VERSION):
|
|
99
|
+
msg = "The file {} was created ".format(self.path) \
|
|
100
|
+
+ "with dclab {} which is ".format(rtdc_ver) \
|
|
101
|
+
+ "not supported anymore! Please rerun " \
|
|
102
|
+
+ "dclab-tdms2rtdc / export the data again."
|
|
103
|
+
raise OldFormatNotSupportedError(msg)
|
|
104
|
+
|
|
105
|
+
self.title = "{} - M{}".format(
|
|
106
|
+
self.config["experiment"].get("sample", "undefined sample"),
|
|
107
|
+
self.config["experiment"].get("run index", "0"))
|
|
108
|
+
|
|
109
|
+
def close(self):
|
|
110
|
+
"""Close the underlying HDF5 file"""
|
|
111
|
+
super(RTDC_HDF5, self).close()
|
|
112
|
+
self.h5file.close()
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def _h5(self):
|
|
116
|
+
warnings.warn("Access to the underlying HDF5 file is now public. "
|
|
117
|
+
"Please use the `h5file` attribute instead of `_h5`!",
|
|
118
|
+
DeprecationWarning)
|
|
119
|
+
return self.h5file
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def can_open(h5path):
|
|
123
|
+
"""Check whether a given file is in the .rtdc file format"""
|
|
124
|
+
h5path = pathlib.Path(h5path)
|
|
125
|
+
if h5path.suffix == ".rtdc":
|
|
126
|
+
return True
|
|
127
|
+
else:
|
|
128
|
+
# we don't know the extension; check for the "events" group
|
|
129
|
+
canopen = False
|
|
130
|
+
try:
|
|
131
|
+
# This is a workaround for Python2 where h5py cannot handle
|
|
132
|
+
# unicode file names.
|
|
133
|
+
with h5path.open("rb") as fd:
|
|
134
|
+
h5 = h5py.File(fd, "r")
|
|
135
|
+
if "events" in h5:
|
|
136
|
+
canopen = True
|
|
137
|
+
except IOError:
|
|
138
|
+
# not an HDF5 file
|
|
139
|
+
pass
|
|
140
|
+
return canopen
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def parse_config(h5path):
|
|
144
|
+
"""Parse the RT-DC configuration of an HDF5 file
|
|
145
|
+
|
|
146
|
+
`h5path` may be a h5py.File object or an actual path
|
|
147
|
+
"""
|
|
148
|
+
if not isinstance(h5path, h5py.File):
|
|
149
|
+
with h5py.File(h5path, mode="r") as fh5:
|
|
150
|
+
h5attrs = dict(fh5.attrs)
|
|
151
|
+
else:
|
|
152
|
+
h5attrs = dict(h5path.attrs)
|
|
153
|
+
|
|
154
|
+
# Convert byte strings to unicode strings
|
|
155
|
+
# https://github.com/h5py/h5py/issues/379
|
|
156
|
+
for key in h5attrs:
|
|
157
|
+
if isinstance(h5attrs[key], bytes):
|
|
158
|
+
h5attrs[key] = h5attrs[key].decode("utf-8")
|
|
159
|
+
|
|
160
|
+
config = Configuration()
|
|
161
|
+
for key in h5attrs:
|
|
162
|
+
section, pname = key.split(":")
|
|
163
|
+
config[section][pname] = h5attrs[key]
|
|
164
|
+
return config
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def hash(self):
|
|
168
|
+
"""Hash value based on file name and content"""
|
|
169
|
+
if self._hash is None:
|
|
170
|
+
tohash = [self.path.name,
|
|
171
|
+
# Hash a maximum of ~1MB of the hdf5 file
|
|
172
|
+
hashfile(self.path, blocksize=65536, count=20)]
|
|
173
|
+
self._hash = hashobj(tohash)
|
|
174
|
+
return self._hash
|
|
175
|
+
|
|
176
|
+
def basins_get_dicts(self):
|
|
177
|
+
"""Return list of dicts for all basins defined in `self.h5file`"""
|
|
178
|
+
return self.basin_get_dicts_from_h5file(self.h5file)
|
|
179
|
+
|
|
180
|
+
@staticmethod
|
|
181
|
+
def basin_get_dicts_from_h5file(h5file):
|
|
182
|
+
"""Return list of dicts for all basins defined in `h5file`"""
|
|
183
|
+
basins = []
|
|
184
|
+
# Do not sort anything here, sorting is done in `RTDCBase`.
|
|
185
|
+
for bk in h5file.get("basins", []):
|
|
186
|
+
bdat = list(h5file["basins"][bk])
|
|
187
|
+
if isinstance(bdat[0], bytes):
|
|
188
|
+
bdat = [bi.decode("utf") for bi in bdat]
|
|
189
|
+
bdict = json.loads(" ".join(bdat))
|
|
190
|
+
bdict["key"] = bk
|
|
191
|
+
basins.append(bdict)
|
|
192
|
+
return basins
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""RT-DC hdf5 format"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import pathlib
|
|
5
|
+
|
|
6
|
+
from .. import feat_basin
|
|
7
|
+
|
|
8
|
+
from .base import RTDC_HDF5
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HDF5Basin(feat_basin.Basin):
|
|
12
|
+
basin_format = "hdf5"
|
|
13
|
+
basin_type = "file"
|
|
14
|
+
|
|
15
|
+
def __init__(self, *args, **kwargs):
|
|
16
|
+
self._available_verified = None
|
|
17
|
+
super(HDF5Basin, self).__init__(*args, **kwargs)
|
|
18
|
+
|
|
19
|
+
def _load_dataset(self, location, **kwargs):
|
|
20
|
+
return RTDC_HDF5(location, **kwargs)
|
|
21
|
+
|
|
22
|
+
def is_available(self):
|
|
23
|
+
if self._available_verified is None:
|
|
24
|
+
with self._av_check_lock:
|
|
25
|
+
try:
|
|
26
|
+
self._available_verified = \
|
|
27
|
+
pathlib.Path(self.location).exists()
|
|
28
|
+
except OSError:
|
|
29
|
+
pass
|
|
30
|
+
return self._available_verified
|