dclab 0.67.0__cp314-cp314-macosx_10_13_x86_64.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 +41 -0
- dclab/_version.py +34 -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 +182 -0
- dclab/definitions/meta_const.py +252 -0
- dclab/definitions/meta_logic.py +111 -0
- dclab/definitions/meta_parse.py +94 -0
- dclab/downsampling.cpython-314-darwin.so +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.cpython-314-darwin.so +0 -0
- dclab/external/skimage/_find_contours_cy.pyx +188 -0
- dclab/external/skimage/_pnpoly.cpython-314-darwin.so +0 -0
- dclab/external/skimage/_pnpoly.pyx +99 -0
- dclab/external/skimage/_shared/__init__.py +1 -0
- dclab/external/skimage/_shared/geometry.cpython-314-darwin.so +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 +260 -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/__init__.py +1 -0
- dclab/kde/base.py +459 -0
- dclab/kde/contours.py +222 -0
- dclab/kde/methods.py +313 -0
- dclab/kde_contours.py +10 -0
- dclab/kde_methods.py +11 -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 +896 -0
- dclab/rtdc_dataset/export.py +867 -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 +762 -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 +173 -0
- dclab/rtdc_dataset/fmt_dcor/base.py +299 -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 +66 -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 +276 -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 +60 -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 +354 -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 +77 -0
- dclab/rtdc_dataset/meta_table.py +25 -0
- dclab/rtdc_dataset/writer.py +1019 -0
- dclab/statistics.py +226 -0
- dclab/util.py +176 -0
- dclab/warn.py +15 -0
- dclab-0.67.0.dist-info/METADATA +153 -0
- dclab-0.67.0.dist-info/RECORD +142 -0
- dclab-0.67.0.dist-info/WHEEL +6 -0
- dclab-0.67.0.dist-info/entry_points.txt +8 -0
- dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
- dclab-0.67.0.dist-info/top_level.txt +1 -0
dclab/statistics.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Statistics computation for RT-DC dataset instances"""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import traceback as tb
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
7
|
+
from . import definitions as dfn
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BadMethodWarning(UserWarning):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Statistics(object):
|
|
15
|
+
available_methods = {}
|
|
16
|
+
|
|
17
|
+
def __init__(self, name, method, req_feature=False):
|
|
18
|
+
"""A helper class for computing statistics
|
|
19
|
+
|
|
20
|
+
All statistical methods are registered in the dictionary
|
|
21
|
+
`Statistics.available_methods`.
|
|
22
|
+
"""
|
|
23
|
+
self.method = method
|
|
24
|
+
self.name = name
|
|
25
|
+
self.req_feature = req_feature
|
|
26
|
+
Statistics.available_methods[name] = self
|
|
27
|
+
|
|
28
|
+
def __call__(self, **kwargs):
|
|
29
|
+
data = self._get_data(kwargs)
|
|
30
|
+
if len(data) == 0:
|
|
31
|
+
result = np.nan
|
|
32
|
+
else:
|
|
33
|
+
try:
|
|
34
|
+
result = self.method(data)
|
|
35
|
+
except BaseException:
|
|
36
|
+
exc = tb.format_exc().replace("\n", "\n | ")
|
|
37
|
+
warnings.warn("Failed to compute {} for {}: {}".format(
|
|
38
|
+
self.name, kwargs["ds"].title, exc),
|
|
39
|
+
BadMethodWarning)
|
|
40
|
+
result = np.nan
|
|
41
|
+
return result
|
|
42
|
+
|
|
43
|
+
def _get_data(self, kwargs):
|
|
44
|
+
"""Convenience wrapper to get statistics data"""
|
|
45
|
+
if "ds" not in kwargs:
|
|
46
|
+
raise ValueError("Keyword argument 'ds' missing.")
|
|
47
|
+
|
|
48
|
+
ds = kwargs["ds"]
|
|
49
|
+
|
|
50
|
+
if self.req_feature:
|
|
51
|
+
if "feature" not in kwargs:
|
|
52
|
+
raise ValueError("Keyword argument 'feature' missing.")
|
|
53
|
+
return self.get_feature(ds, kwargs["feature"])
|
|
54
|
+
else:
|
|
55
|
+
return ds
|
|
56
|
+
|
|
57
|
+
def get_feature(self, ds, feat):
|
|
58
|
+
"""Return filtered feature data
|
|
59
|
+
|
|
60
|
+
The features are filtered according to the user-defined filters,
|
|
61
|
+
using the information in `ds.filter.all`. In addition, all
|
|
62
|
+
`nan` and `inf` values are purged.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
ds: dclab.rtdc_dataset.RTDCBase
|
|
67
|
+
The dataset containing the feature
|
|
68
|
+
feat: str
|
|
69
|
+
The name of the feature; must be a scalar feature
|
|
70
|
+
"""
|
|
71
|
+
if ds.config["filtering"]["enable filters"]:
|
|
72
|
+
x = ds[feat][ds.filter.all]
|
|
73
|
+
else:
|
|
74
|
+
x = ds[feat]
|
|
75
|
+
bad = np.isnan(x) | np.isinf(x)
|
|
76
|
+
xout = x[~bad]
|
|
77
|
+
return xout
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def flow_rate(ds):
|
|
81
|
+
"""Return the flow rate of an RT-DC dataset"""
|
|
82
|
+
conf = ds.config["setup"]
|
|
83
|
+
if "flow rate" in conf:
|
|
84
|
+
return conf["flow rate"]
|
|
85
|
+
else:
|
|
86
|
+
return np.nan
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_statistics(ds, methods=None, features=None, ret_dict=False):
|
|
90
|
+
"""Compute statistics for an RT-DC dataset
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
ds: dclab.rtdc_dataset.RTDCBase
|
|
95
|
+
The dataset for which to compute the statistics.
|
|
96
|
+
methods: list of str or None
|
|
97
|
+
The methods wih which to compute the statistics.
|
|
98
|
+
The list of available methods is given with
|
|
99
|
+
:func:`.available_methods.keys`
|
|
100
|
+
If set to `None`, statistics for all methods are computed.
|
|
101
|
+
features: list of str
|
|
102
|
+
Feature name identifiers are defined by
|
|
103
|
+
:func:`dclab.definitions.feature_exists`.
|
|
104
|
+
If set to `None`, statistics for all scalar features
|
|
105
|
+
available are computed.
|
|
106
|
+
ret_dict: bool
|
|
107
|
+
Instead of returning ``(header, values)``, return a dictionary
|
|
108
|
+
with headers as keys.
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
header: list of str
|
|
113
|
+
The header (feature + method names) of the computed statistics.
|
|
114
|
+
values: list of float
|
|
115
|
+
The computed statistics.
|
|
116
|
+
"""
|
|
117
|
+
if methods is None:
|
|
118
|
+
cls = list(Statistics.available_methods.keys())
|
|
119
|
+
# sort the features in a usable way
|
|
120
|
+
avm = Statistics.available_methods
|
|
121
|
+
me1 = [m for m in cls if not avm[m].req_feature]
|
|
122
|
+
me2 = [m for m in cls if avm[m].req_feature]
|
|
123
|
+
methods = me1 + me2
|
|
124
|
+
|
|
125
|
+
if features is None:
|
|
126
|
+
features = ds.features_scalar
|
|
127
|
+
else:
|
|
128
|
+
features = [a.lower() for a in features]
|
|
129
|
+
|
|
130
|
+
header = []
|
|
131
|
+
values = []
|
|
132
|
+
|
|
133
|
+
# First loop over all methods that do not require a feature
|
|
134
|
+
for mt in methods:
|
|
135
|
+
meth = Statistics.available_methods[mt]
|
|
136
|
+
if not meth.req_feature:
|
|
137
|
+
values.append(meth(ds=ds))
|
|
138
|
+
header.append(mt)
|
|
139
|
+
|
|
140
|
+
# To make sure that all methods are computed for each feature in a block,
|
|
141
|
+
# we loop over all features. It would be easier to loop over the methods,
|
|
142
|
+
# but the ordering of the resulting statistics would not be human-friendly.
|
|
143
|
+
for ft in features:
|
|
144
|
+
for mt in methods:
|
|
145
|
+
meth = Statistics.available_methods[mt]
|
|
146
|
+
if meth.req_feature:
|
|
147
|
+
if ft in ds:
|
|
148
|
+
values.append(meth(ds=ds, feature=ft))
|
|
149
|
+
else:
|
|
150
|
+
values.append(np.nan)
|
|
151
|
+
label = dfn.get_feature_label(ft, rtdc_ds=ds)
|
|
152
|
+
header.append(" ".join([mt, label]))
|
|
153
|
+
|
|
154
|
+
if ret_dict:
|
|
155
|
+
return dict(zip(header, values))
|
|
156
|
+
else:
|
|
157
|
+
return header, values
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def mode(data):
|
|
161
|
+
"""Compute an intelligent value for the mode
|
|
162
|
+
|
|
163
|
+
The most common value in experimental is not very useful if there
|
|
164
|
+
are a lot of digits after the comma. This method approaches this
|
|
165
|
+
issue by rounding to bin size that is determined by the
|
|
166
|
+
Freedman–Diaconis rule.
|
|
167
|
+
|
|
168
|
+
Parameters
|
|
169
|
+
----------
|
|
170
|
+
data: 1d ndarray
|
|
171
|
+
The data for which the mode should be computed.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
mode: float
|
|
176
|
+
The mode computed with the Freedman-Diaconis rule.
|
|
177
|
+
"""
|
|
178
|
+
# size
|
|
179
|
+
n = data.shape[0]
|
|
180
|
+
# interquartile range
|
|
181
|
+
iqr = np.percentile(data, 75)-np.percentile(data, 25)
|
|
182
|
+
# Freedman–Diaconis
|
|
183
|
+
bin_size = 2 * iqr / n**(1/3)
|
|
184
|
+
|
|
185
|
+
if bin_size == 0:
|
|
186
|
+
return np.nan
|
|
187
|
+
|
|
188
|
+
# Add bin_size/2, because we want the center of the bin and
|
|
189
|
+
# not the left corner of the bin.
|
|
190
|
+
databin = np.round(data/bin_size)*bin_size + bin_size/2
|
|
191
|
+
u, indices = np.unique(databin, return_inverse=True)
|
|
192
|
+
mode = u[np.argmax(np.bincount(indices))]
|
|
193
|
+
|
|
194
|
+
return mode
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# Register all the methods
|
|
198
|
+
# Methods that require an axis
|
|
199
|
+
Statistics(name="Mean", req_feature=True, method=np.average)
|
|
200
|
+
# Premature-Optimization warning: `np.percentile` also accepts an array
|
|
201
|
+
# of percentiles as the `q` argument, which I would expect to yield better
|
|
202
|
+
# performance than computing percentiles individually. Implementing this
|
|
203
|
+
# would break the way we are defining statistical methods here (One
|
|
204
|
+
# `Statistics` instance per method) and thus requires a considerable
|
|
205
|
+
# amount of work (much more work than writing this text here). It would
|
|
206
|
+
# also make understanding the code more difficult. In addition, computing
|
|
207
|
+
# statistics is not done often and is extremely fast anyway for a few
|
|
208
|
+
# millions of events. Don't optimize this!
|
|
209
|
+
Statistics(name="10th Percentile", req_feature=True,
|
|
210
|
+
method=lambda data: np.percentile(data, 10))
|
|
211
|
+
Statistics(name="25th Percentile", req_feature=True,
|
|
212
|
+
method=lambda data: np.percentile(data, 25))
|
|
213
|
+
Statistics(name="Median", req_feature=True, method=np.median)
|
|
214
|
+
Statistics(name="75th Percentile", req_feature=True,
|
|
215
|
+
method=lambda data: np.percentile(data, 75))
|
|
216
|
+
Statistics(name="90th Percentile", req_feature=True,
|
|
217
|
+
method=lambda data: np.percentile(data, 90))
|
|
218
|
+
Statistics(name="Mode", req_feature=True, method=mode)
|
|
219
|
+
Statistics(name="SD", req_feature=True, method=np.std)
|
|
220
|
+
# Methods that work on RTDCBase
|
|
221
|
+
Statistics(name="Events",
|
|
222
|
+
method=lambda mm: np.sum(mm.filter.all))
|
|
223
|
+
Statistics(name="%-gated",
|
|
224
|
+
method=lambda mm: np.average(mm.filter.all)*100)
|
|
225
|
+
Statistics(name="Flow rate",
|
|
226
|
+
method=lambda mm: flow_rate(mm))
|
dclab/util.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Utility methods"""
|
|
2
|
+
import functools
|
|
3
|
+
import hashlib
|
|
4
|
+
import importlib
|
|
5
|
+
import numbers
|
|
6
|
+
import pathlib
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
import h5py
|
|
10
|
+
import numpy as np
|
|
11
|
+
from .rtdc_dataset.config import Configuration, ConfigurationDict
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if np.lib.NumpyVersion(np.__version__) >= "2.0.0":
|
|
15
|
+
copy_if_needed = None
|
|
16
|
+
else:
|
|
17
|
+
copy_if_needed = False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class file_monitoring_lru_cache:
|
|
21
|
+
"""Decorator for caching data extracted from files
|
|
22
|
+
|
|
23
|
+
The function that is decorated with `file_monitoring_lru_cache`
|
|
24
|
+
must accept `path` as its first argument. Caching is
|
|
25
|
+
done with an `lru_cache`. In addition to the full path
|
|
26
|
+
and the other arguments to the decorated function, the
|
|
27
|
+
size and the modification time of `path` is used as a
|
|
28
|
+
key for the decorator.
|
|
29
|
+
If the path does not exist, no caching is done.
|
|
30
|
+
|
|
31
|
+
Use case: Extract and cache metadata from a file on disk
|
|
32
|
+
that may change.
|
|
33
|
+
"""
|
|
34
|
+
def __init__(self, maxsize=100):
|
|
35
|
+
self.lru_cache = functools.lru_cache(maxsize=maxsize)
|
|
36
|
+
self.cached_wrapper = None
|
|
37
|
+
|
|
38
|
+
def __call__(self, func):
|
|
39
|
+
@self.lru_cache
|
|
40
|
+
def cached_wrapper(path, path_stats, *args, **kwargs):
|
|
41
|
+
assert path_stats, "We need stat for validating the cache"
|
|
42
|
+
return func(path, *args, **kwargs)
|
|
43
|
+
|
|
44
|
+
@functools.wraps(func)
|
|
45
|
+
def wrapper(path, *args, **kwargs):
|
|
46
|
+
full_path = pathlib.Path(path).resolve()
|
|
47
|
+
if full_path.exists():
|
|
48
|
+
path_stat = full_path.stat()
|
|
49
|
+
return cached_wrapper(
|
|
50
|
+
path=full_path,
|
|
51
|
+
path_stats=(path_stat.st_mtime_ns, path_stat.st_size),
|
|
52
|
+
*args,
|
|
53
|
+
**kwargs)
|
|
54
|
+
else:
|
|
55
|
+
# `func` will most-likely raise an exception
|
|
56
|
+
return func(path, *args, **kwargs)
|
|
57
|
+
|
|
58
|
+
wrapper.cache_clear = cached_wrapper.cache_clear
|
|
59
|
+
wrapper.cache_info = cached_wrapper.cache_info
|
|
60
|
+
|
|
61
|
+
return wrapper
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class LazyLoader():
|
|
65
|
+
"""Lazy load a module on first attribute access"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, modname):
|
|
68
|
+
self._modname = modname
|
|
69
|
+
self._mod = None
|
|
70
|
+
|
|
71
|
+
def __getattr__(self, attr):
|
|
72
|
+
"""Get attribute from module, load module if not loaded yet"""
|
|
73
|
+
|
|
74
|
+
if self._mod is None:
|
|
75
|
+
# module is unset, load it
|
|
76
|
+
self._mod = importlib.import_module(self._modname)
|
|
77
|
+
|
|
78
|
+
# retry getattr if module was just loaded for first time
|
|
79
|
+
# call this outside exception handler in case it raises new exception
|
|
80
|
+
return getattr(self._mod, attr)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@file_monitoring_lru_cache(maxsize=100)
|
|
84
|
+
def hashfile(fname, blocksize=65536, count=0, constructor=hashlib.md5,
|
|
85
|
+
hasher_class=None):
|
|
86
|
+
"""Compute md5 hex-hash of a file
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
fname: str or pathlib.Path
|
|
91
|
+
path to the file
|
|
92
|
+
blocksize: int
|
|
93
|
+
block size in bytes read from the file
|
|
94
|
+
(set to `0` to hash the entire file)
|
|
95
|
+
count: int
|
|
96
|
+
number of blocks read from the file
|
|
97
|
+
hasher_class: callable
|
|
98
|
+
deprecated, see use `constructor` instead
|
|
99
|
+
constructor: callable
|
|
100
|
+
hash algorithm constructor
|
|
101
|
+
"""
|
|
102
|
+
if hasher_class is not None:
|
|
103
|
+
warnings.warn("The `hasher_class` argument is deprecated, please use "
|
|
104
|
+
"`constructor` instead.")
|
|
105
|
+
constructor = hasher_class
|
|
106
|
+
|
|
107
|
+
path = pathlib.Path(fname)
|
|
108
|
+
|
|
109
|
+
hasher = constructor()
|
|
110
|
+
with path.open('rb') as fd:
|
|
111
|
+
buf = fd.read(blocksize)
|
|
112
|
+
ii = 0
|
|
113
|
+
while len(buf) > 0:
|
|
114
|
+
hasher.update(buf)
|
|
115
|
+
buf = fd.read(blocksize)
|
|
116
|
+
ii += 1
|
|
117
|
+
if count and ii == count:
|
|
118
|
+
break
|
|
119
|
+
return hasher.hexdigest()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def hashobj(obj):
|
|
123
|
+
"""Compute md5 hex-hash of a Python object"""
|
|
124
|
+
return hashlib.md5(obj2bytes(obj)).hexdigest()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def obj2bytes(obj):
|
|
128
|
+
"""Bytes representation of an object for hashing
|
|
129
|
+
|
|
130
|
+
Note that there is no guarantee that the bytes representation
|
|
131
|
+
returned is reproducible across sessions. This is currently the
|
|
132
|
+
case when an :class:`.RTDCBase` instance is passed. There is no
|
|
133
|
+
opinion on wether/how this should be changed.
|
|
134
|
+
"""
|
|
135
|
+
if isinstance(obj, str):
|
|
136
|
+
return obj.encode("utf-8")
|
|
137
|
+
elif isinstance(obj, pathlib.Path):
|
|
138
|
+
return obj2bytes(str(obj))
|
|
139
|
+
elif isinstance(obj, (bool, numbers.Number)):
|
|
140
|
+
return str(obj).encode("utf-8")
|
|
141
|
+
elif obj is None:
|
|
142
|
+
return b"none"
|
|
143
|
+
elif isinstance(obj, np.ndarray):
|
|
144
|
+
return obj.tobytes()
|
|
145
|
+
elif isinstance(obj, tuple):
|
|
146
|
+
return obj2bytes(list(obj))
|
|
147
|
+
elif isinstance(obj, list):
|
|
148
|
+
return b"".join(obj2bytes(o) for o in obj)
|
|
149
|
+
elif isinstance(obj, dict):
|
|
150
|
+
return obj2bytes(sorted(obj.items()))
|
|
151
|
+
elif hasattr(obj, "identifier"):
|
|
152
|
+
# For RTDCBase, this identifier is not reproducible in-between
|
|
153
|
+
# sessions. We might want to change this to something that is
|
|
154
|
+
# reproducible in the future (if the need arises).
|
|
155
|
+
return obj2bytes(obj.identifier)
|
|
156
|
+
elif isinstance(obj, h5py.Dataset):
|
|
157
|
+
# path within the HDF5 file
|
|
158
|
+
o_name = obj.name
|
|
159
|
+
# filename
|
|
160
|
+
o_filename = obj.file.filename
|
|
161
|
+
_data = [o_name, o_filename]
|
|
162
|
+
if pathlib.Path(o_filename).exists():
|
|
163
|
+
# when the file was changed
|
|
164
|
+
_data.append(pathlib.Path(obj.file.filename).stat().st_mtime)
|
|
165
|
+
# size of the file
|
|
166
|
+
_data.append(pathlib.Path(obj.file.filename).stat().st_size)
|
|
167
|
+
return obj2bytes(_data)
|
|
168
|
+
elif hasattr(obj, "__array__"): # must come after h5py.Dataset
|
|
169
|
+
return obj2bytes(obj.__array__())
|
|
170
|
+
elif isinstance(obj, Configuration):
|
|
171
|
+
return obj2bytes(obj.tostring())
|
|
172
|
+
elif isinstance(obj, ConfigurationDict):
|
|
173
|
+
return obj2bytes(dict(obj))
|
|
174
|
+
else:
|
|
175
|
+
raise ValueError("No rule to convert object '{}' to string.".
|
|
176
|
+
format(obj.__class__))
|
dclab/warn.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class PipelineWarning(UserWarning):
|
|
4
|
+
"""Super-class for warnings relevant to data analysis
|
|
5
|
+
|
|
6
|
+
There are those types of warnings in dclab that are
|
|
7
|
+
important to the user, because they suggest that the
|
|
8
|
+
user may not use the correct model (e.g. Young's modulus
|
|
9
|
+
computation) in his analysis pipeline. All of these
|
|
10
|
+
warnings should be subclassed from PipelineWarning
|
|
11
|
+
to allow identifying them in higher-level software
|
|
12
|
+
such as DCscope and to present them correctly to the
|
|
13
|
+
user.
|
|
14
|
+
"""
|
|
15
|
+
pass
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dclab
|
|
3
|
+
Version: 0.67.0
|
|
4
|
+
Summary: Library for real-time deformability cytometry (RT-DC)
|
|
5
|
+
Author: Benedikt Hartmann, Eoghan O'Connell, Maik Herbig, Maximilian Schlögel, Nadia Sbaa, Paul Müller, Philipp Rosendahl, Raghava Alajangi
|
|
6
|
+
Maintainer-email: Paul Müller <dev@craban.de>
|
|
7
|
+
License-Expression: GPL-2.0-or-later
|
|
8
|
+
Project-URL: source, https://github.com/DC-Analysis/dclab
|
|
9
|
+
Project-URL: tracker, https://github.com/DC-Analysis/dclab/issues
|
|
10
|
+
Project-URL: documentation, https://dclab.readthedocs.io/en/stable/
|
|
11
|
+
Project-URL: changelog, https://dclab.readthedocs.io/en/stable/sec_changelog.html
|
|
12
|
+
Keywords: RT-DC,deformability,cytometry
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Requires-Python: <4,>=3.9
|
|
18
|
+
Description-Content-Type: text/x-rst
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: h5py<4,>=3.0.0
|
|
21
|
+
Requires-Dist: hdf5plugin<5,>=3.3.1
|
|
22
|
+
Requires-Dist: importlib-resources>=6.0
|
|
23
|
+
Requires-Dist: numpy<3,>=1.21
|
|
24
|
+
Requires-Dist: scipy<2,>=1.10.0
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: dclab[dcor,export,http,s3,tdms]; extra == "all"
|
|
27
|
+
Provides-Extra: dcor
|
|
28
|
+
Requires-Dist: requests<3,>=2.31.0; extra == "dcor"
|
|
29
|
+
Provides-Extra: export
|
|
30
|
+
Requires-Dist: fcswrite>=0.5.1; extra == "export"
|
|
31
|
+
Requires-Dist: av; extra == "export"
|
|
32
|
+
Provides-Extra: http
|
|
33
|
+
Requires-Dist: requests<3,>=2.31.0; extra == "http"
|
|
34
|
+
Provides-Extra: s3
|
|
35
|
+
Requires-Dist: boto3>=1.34.31; extra == "s3"
|
|
36
|
+
Provides-Extra: tdms
|
|
37
|
+
Requires-Dist: imageio[ffmpeg]; extra == "tdms"
|
|
38
|
+
Requires-Dist: nptdms<1.9,>=0.23.0; extra == "tdms"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
|dclab|
|
|
42
|
+
=======
|
|
43
|
+
|
|
44
|
+
|PyPI Version| |Build Status| |Coverage Status| |Docs Status|
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
This is a Python library for the post-measurement analysis of
|
|
48
|
+
real-time deformability cytometry (RT-DC) datasets; an essential part of
|
|
49
|
+
the DC Cosmos (
|
|
50
|
+
`DCscope <https://github.com/DC-analysis/DCscope>`__,
|
|
51
|
+
`DCOR <https://github.com/DCOR-dev/dcor_control>`__,
|
|
52
|
+
`DCOR-Aid <https://github.com/DCOR-dev/DCOR-Aid>`__,
|
|
53
|
+
`DCTag <https://github.com/DC-analysis/DCTag>`__,
|
|
54
|
+
`DCKit <https://github.com/DC-analysis/DCKit>`__,
|
|
55
|
+
).
|
|
56
|
+
|
|
57
|
+
Documentation
|
|
58
|
+
-------------
|
|
59
|
+
The documentation, including the code reference and examples, is available at
|
|
60
|
+
`dclab.readthedocs.io <https://dclab.readthedocs.io/en/stable/>`__.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
Installation
|
|
64
|
+
------------
|
|
65
|
+
|
|
66
|
+
::
|
|
67
|
+
|
|
68
|
+
pip install dclab[all]
|
|
69
|
+
|
|
70
|
+
For more options, please check out the `documentation
|
|
71
|
+
<https://dclab.readthedocs.io/en/latest/sec_getting_started.html#installation>`__.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
Information for developers
|
|
75
|
+
--------------------------
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
Contributing
|
|
79
|
+
~~~~~~~~~~~~
|
|
80
|
+
The main branch for developing dclab is master.
|
|
81
|
+
If you want to make small changes like one-liners,
|
|
82
|
+
documentation, or default values in the configuration,
|
|
83
|
+
you may work on the master branch. If you want to change
|
|
84
|
+
more, please (fork dclab and) create a separate branch,
|
|
85
|
+
e.g. ``my_new_feature_dev``, and create a pull-request
|
|
86
|
+
once you are done making your changes.
|
|
87
|
+
Please make sure to edit the
|
|
88
|
+
`Changelog <https://github.com/DC-analysis/dclab/blob/master/CHANGELOG>`__.
|
|
89
|
+
|
|
90
|
+
**Very important:** Please always try to use ::
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
git pull --rebase
|
|
94
|
+
|
|
95
|
+
instead of::
|
|
96
|
+
|
|
97
|
+
git pull
|
|
98
|
+
|
|
99
|
+
to prevent non-linearities in the commit history.
|
|
100
|
+
|
|
101
|
+
Tests
|
|
102
|
+
~~~~~
|
|
103
|
+
dclab is tested using pytest. If you have the time, please write test
|
|
104
|
+
methods for your code and put them in the ``tests`` directory. To run
|
|
105
|
+
the tests, install `pytest` and run::
|
|
106
|
+
|
|
107
|
+
pytest tests
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
Docs
|
|
111
|
+
~~~~
|
|
112
|
+
The docs are built with `sphinx <https://www.sphinx-doc.org>`_. Please make
|
|
113
|
+
sure they compile when you change them (this also includes function doc strings)::
|
|
114
|
+
|
|
115
|
+
cd docs
|
|
116
|
+
pip install -r requirements.txt
|
|
117
|
+
sphinx-build . _build # open "index.html" in the "_build" directory
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
PEP8
|
|
121
|
+
~~~~
|
|
122
|
+
We use flake8 to enforce coding style::
|
|
123
|
+
|
|
124
|
+
pip install flake8
|
|
125
|
+
flake8 --exclude _version.py dclab
|
|
126
|
+
flake8 docs
|
|
127
|
+
flake8 examples
|
|
128
|
+
flake8 tests
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
Incrementing version
|
|
132
|
+
~~~~~~~~~~~~~~~~~~~~
|
|
133
|
+
Dclab gets its version from the latest git tag.
|
|
134
|
+
If you think that a new version should be published,
|
|
135
|
+
create a tag on the master branch (if you have the necessary
|
|
136
|
+
permissions to do so)::
|
|
137
|
+
|
|
138
|
+
git tag -a "0.1.3"
|
|
139
|
+
git push --tags origin
|
|
140
|
+
|
|
141
|
+
Appveyor and GitHub Actions will then automatically build source package and wheels
|
|
142
|
+
and publish them on PyPI.
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
.. |dclab| image:: https://raw.github.com/DC-analysis/dclab/master/docs/logo/dclab.png
|
|
146
|
+
.. |PyPI Version| image:: https://img.shields.io/pypi/v/dclab.svg
|
|
147
|
+
:target: https://pypi.python.org/pypi/dclab
|
|
148
|
+
.. |Build Status| image:: https://img.shields.io/github/actions/workflow/status/DC-analysis/dclab/check.yml
|
|
149
|
+
:target: https://github.com/DC-analysis/dclab/actions?query=workflow%3AChecks
|
|
150
|
+
.. |Coverage Status| image:: https://img.shields.io/codecov/c/github/DC-analysis/dclab/master.svg
|
|
151
|
+
:target: https://codecov.io/gh/DC-analysis/dclab
|
|
152
|
+
.. |Docs Status| image:: https://readthedocs.org/projects/dclab/badge/?version=latest
|
|
153
|
+
:target: https://readthedocs.org/projects/dclab/builds/
|