dclab 0.67.0__cp314-cp314t-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.

Files changed (142) hide show
  1. dclab/__init__.py +41 -0
  2. dclab/_version.py +34 -0
  3. dclab/cached.py +97 -0
  4. dclab/cli/__init__.py +10 -0
  5. dclab/cli/common.py +237 -0
  6. dclab/cli/task_compress.py +126 -0
  7. dclab/cli/task_condense.py +223 -0
  8. dclab/cli/task_join.py +229 -0
  9. dclab/cli/task_repack.py +98 -0
  10. dclab/cli/task_split.py +154 -0
  11. dclab/cli/task_tdms2rtdc.py +186 -0
  12. dclab/cli/task_verify_dataset.py +75 -0
  13. dclab/definitions/__init__.py +79 -0
  14. dclab/definitions/feat_const.py +202 -0
  15. dclab/definitions/feat_logic.py +182 -0
  16. dclab/definitions/meta_const.py +252 -0
  17. dclab/definitions/meta_logic.py +111 -0
  18. dclab/definitions/meta_parse.py +94 -0
  19. dclab/downsampling.cpython-314t-darwin.so +0 -0
  20. dclab/downsampling.pyx +230 -0
  21. dclab/external/__init__.py +4 -0
  22. dclab/external/packaging/LICENSE +3 -0
  23. dclab/external/packaging/LICENSE.APACHE +177 -0
  24. dclab/external/packaging/LICENSE.BSD +23 -0
  25. dclab/external/packaging/__init__.py +6 -0
  26. dclab/external/packaging/_structures.py +61 -0
  27. dclab/external/packaging/version.py +505 -0
  28. dclab/external/skimage/LICENSE +28 -0
  29. dclab/external/skimage/__init__.py +2 -0
  30. dclab/external/skimage/_find_contours.py +216 -0
  31. dclab/external/skimage/_find_contours_cy.cpython-314t-darwin.so +0 -0
  32. dclab/external/skimage/_find_contours_cy.pyx +188 -0
  33. dclab/external/skimage/_pnpoly.cpython-314t-darwin.so +0 -0
  34. dclab/external/skimage/_pnpoly.pyx +99 -0
  35. dclab/external/skimage/_shared/__init__.py +1 -0
  36. dclab/external/skimage/_shared/geometry.cpython-314t-darwin.so +0 -0
  37. dclab/external/skimage/_shared/geometry.pxd +6 -0
  38. dclab/external/skimage/_shared/geometry.pyx +55 -0
  39. dclab/external/skimage/measure.py +7 -0
  40. dclab/external/skimage/pnpoly.py +53 -0
  41. dclab/external/statsmodels/LICENSE +35 -0
  42. dclab/external/statsmodels/__init__.py +6 -0
  43. dclab/external/statsmodels/nonparametric/__init__.py +1 -0
  44. dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
  45. dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
  46. dclab/external/statsmodels/nonparametric/kernels.py +36 -0
  47. dclab/features/__init__.py +9 -0
  48. dclab/features/bright.py +81 -0
  49. dclab/features/bright_bc.py +93 -0
  50. dclab/features/bright_perc.py +63 -0
  51. dclab/features/contour.py +161 -0
  52. dclab/features/emodulus/__init__.py +339 -0
  53. dclab/features/emodulus/load.py +252 -0
  54. dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
  55. dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
  56. dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
  57. dclab/features/emodulus/pxcorr.py +135 -0
  58. dclab/features/emodulus/scale_linear.py +247 -0
  59. dclab/features/emodulus/viscosity.py +260 -0
  60. dclab/features/fl_crosstalk.py +95 -0
  61. dclab/features/inert_ratio.py +377 -0
  62. dclab/features/volume.py +242 -0
  63. dclab/http_utils.py +322 -0
  64. dclab/isoelastics/__init__.py +468 -0
  65. dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
  66. dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
  67. dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
  68. dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
  69. dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
  70. dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
  71. dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
  72. dclab/kde/__init__.py +1 -0
  73. dclab/kde/base.py +459 -0
  74. dclab/kde/contours.py +222 -0
  75. dclab/kde/methods.py +313 -0
  76. dclab/kde_contours.py +10 -0
  77. dclab/kde_methods.py +11 -0
  78. dclab/lme4/__init__.py +5 -0
  79. dclab/lme4/lme4_template.R +94 -0
  80. dclab/lme4/rsetup.py +204 -0
  81. dclab/lme4/wrapr.py +386 -0
  82. dclab/polygon_filter.py +398 -0
  83. dclab/rtdc_dataset/__init__.py +15 -0
  84. dclab/rtdc_dataset/check.py +902 -0
  85. dclab/rtdc_dataset/config.py +533 -0
  86. dclab/rtdc_dataset/copier.py +353 -0
  87. dclab/rtdc_dataset/core.py +896 -0
  88. dclab/rtdc_dataset/export.py +867 -0
  89. dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
  90. dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
  91. dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
  92. dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
  93. dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
  94. dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
  95. dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
  96. dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
  97. dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
  98. dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
  99. dclab/rtdc_dataset/feat_basin.py +762 -0
  100. dclab/rtdc_dataset/feat_temp.py +102 -0
  101. dclab/rtdc_dataset/filter.py +263 -0
  102. dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
  103. dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
  104. dclab/rtdc_dataset/fmt_dcor/api.py +173 -0
  105. dclab/rtdc_dataset/fmt_dcor/base.py +299 -0
  106. dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
  107. dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
  108. dclab/rtdc_dataset/fmt_dcor/tables.py +66 -0
  109. dclab/rtdc_dataset/fmt_dict.py +103 -0
  110. dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
  111. dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
  112. dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
  113. dclab/rtdc_dataset/fmt_hdf5/events.py +276 -0
  114. dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
  115. dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
  116. dclab/rtdc_dataset/fmt_hdf5/tables.py +60 -0
  117. dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
  118. dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
  119. dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
  120. dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
  121. dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
  122. dclab/rtdc_dataset/fmt_http.py +102 -0
  123. dclab/rtdc_dataset/fmt_s3.py +354 -0
  124. dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
  125. dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
  126. dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
  127. dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
  128. dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
  129. dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
  130. dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
  131. dclab/rtdc_dataset/load.py +77 -0
  132. dclab/rtdc_dataset/meta_table.py +25 -0
  133. dclab/rtdc_dataset/writer.py +1019 -0
  134. dclab/statistics.py +226 -0
  135. dclab/util.py +176 -0
  136. dclab/warn.py +15 -0
  137. dclab-0.67.0.dist-info/METADATA +153 -0
  138. dclab-0.67.0.dist-info/RECORD +142 -0
  139. dclab-0.67.0.dist-info/WHEEL +6 -0
  140. dclab-0.67.0.dist-info/entry_points.txt +8 -0
  141. dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
  142. dclab-0.67.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,161 @@
1
+ """Computation of event contour from event mask"""
2
+ from collections import deque
3
+ import numbers
4
+
5
+ import numpy as np
6
+
7
+ # equivalent to
8
+ # from skimage.measure import find_contours
9
+ from ..external.skimage.measure import find_contours
10
+
11
+
12
+ class NoValidContourFoundError(BaseException):
13
+ pass
14
+
15
+
16
+ class LazyContourList(object):
17
+ def __init__(self, masks, max_events=1000):
18
+ """A list-like object that computes contours upon indexing
19
+
20
+ Parameters
21
+ ----------
22
+ masks: array-like
23
+ 3D array of masks, may be an HDF5 dataset or any other
24
+ structure that supports indexing
25
+ max_events: int
26
+ maximum number of contours to keep in the contour list;
27
+ set to 0/False/None to cache all contours
28
+
29
+ .. versionchanged:: 0.58.3
30
+
31
+ Added the `max_events` parameter which now makes this class
32
+ a lazy, least-recently-used contour list. To achieve the
33
+ old behavior (which may fill up your memory), set
34
+ `max_events=None`.
35
+ """
36
+ self.masks = masks
37
+ self.contours = deque(maxlen=max_events or None)
38
+ self.indices = deque(maxlen=max_events or None)
39
+ #: used for hashing in ancillary features
40
+ self.identifier = str(masks[0][:].tobytes())
41
+ self.shape = len(masks), np.nan, 2
42
+
43
+ def __getitem__(self, idx):
44
+ """Compute contour(s) if not already in self.contours"""
45
+ if not isinstance(idx, numbers.Integral):
46
+ # slicing!
47
+ indices = np.arange(len(self))[idx]
48
+ output = []
49
+ # populate the output list
50
+ for evid in indices:
51
+ output.append(self.__getitem__(evid))
52
+ return output
53
+ else:
54
+ try:
55
+ # Is the contour already available?
56
+ idx_q = self.indices.index(idx)
57
+ except ValueError:
58
+ # The contour is not there. Compute it.
59
+ try:
60
+ cont = get_contour(self.masks[idx])
61
+ except BaseException as e:
62
+ e.args = (f"Event {idx}, {e.args[0]}",)
63
+ raise
64
+ else:
65
+ # Get the contour from deque
66
+ cont = self.contours[idx_q]
67
+ # If we got here, it means that we have computed a contour
68
+ # successfully. Store it in the deque.
69
+ self.contours.append(cont)
70
+ self.indices.append(idx)
71
+ return cont
72
+
73
+ def __len__(self):
74
+ return len(self.masks)
75
+
76
+
77
+ def get_contour(mask):
78
+ """Compute the image contour from a mask
79
+
80
+ The contour is computed in a very inefficient way using scikit-image
81
+ and a conversion of float coordinates to pixel coordinates.
82
+
83
+ Parameters
84
+ ----------
85
+ mask: binary ndarray of shape (M,N) or (K,M,N)
86
+ The mask outlining the pixel positions of the event.
87
+ If a 3d array is given, then `K` indexes the individual
88
+ contours.
89
+
90
+ Returns
91
+ -------
92
+ cont: ndarray or list of K ndarrays of shape (J,2)
93
+ A 2D array that holds the contour of an event (in pixels)
94
+ e.g. obtained using `mm.contour` where `mm` is an instance
95
+ of `RTDCBase`. The first and second columns of `cont`
96
+ correspond to the x- and y-coordinates of the contour.
97
+ """
98
+ if isinstance(mask, np.ndarray) and len(mask.shape) == 2:
99
+ mask = [mask]
100
+ ret_list = False
101
+ else:
102
+ ret_list = True
103
+ contours = []
104
+
105
+ for mi in mask:
106
+ # This is only 10% slower than doing:
107
+ # conts, _ = cv2.findContours(np.array(mi, dtype=np.uint8),
108
+ # cv2.RETR_EXTERNAL,
109
+ # cv2.CHAIN_APPROX_NONE)
110
+ # c2 = conts[0].reshape(-1, 2)
111
+ conts = find_contours(mi.transpose(),
112
+ level=.9999,
113
+ positive_orientation="low",
114
+ fully_connected="high")
115
+ # get the longest contour
116
+ c0 = sorted(conts, key=lambda x: len(x))[-1]
117
+ # round all coordinates to pixel values
118
+ c1 = np.asarray(np.round(c0), int)
119
+ # remove duplicates
120
+ c2 = remove_duplicates(c1)
121
+ if len(c2) == 0:
122
+ raise NoValidContourFoundError("No contour found!")
123
+ contours.append(c2)
124
+ if ret_list:
125
+ return contours
126
+ else:
127
+ return contours[0]
128
+
129
+
130
+ def get_contour_lazily(mask):
131
+ """Like :func:`get_contour`, but computes contours on demand
132
+
133
+ Parameters
134
+ ----------
135
+ mask: binary ndarray of shape (M,N) or (K,M,N)
136
+ The mask outlining the pixel positions of the event.
137
+ If a 3d array is given, then `K` indexes the individual
138
+ contours.
139
+
140
+ Returns
141
+ -------
142
+ cont: ndarray or LazyContourList of K ndarrays of shape (J,2)
143
+ A 2D array that holds the contour of an event (in pixels)
144
+ e.g. obtained using `mm.contour` where `mm` is an instance
145
+ of `RTDCBase`. The first and second columns of `cont`
146
+ correspond to the x- and y-coordinates of the contour.
147
+ """
148
+ if isinstance(mask, np.ndarray) and len(mask.shape) == 2:
149
+ # same behavior as `get_contour`
150
+ cont = get_contour(mask=mask)
151
+ else:
152
+ cont = LazyContourList(masks=mask)
153
+ return cont
154
+
155
+
156
+ def remove_duplicates(cont):
157
+ """Remove duplicates in a circular contour"""
158
+ x = np.resize(cont, (len(cont) + 1, 2))
159
+ selection = np.ones(len(x), dtype=bool)
160
+ selection[1:] = ~np.prod((x[1:] == x[:-1]), axis=1, dtype=bool)
161
+ return x[selection][:-1]
@@ -0,0 +1,339 @@
1
+ """Computation of apparent Young's modulus for RT-DC measurements"""
2
+ from __future__ import annotations
3
+
4
+ import numbers
5
+ import pathlib
6
+ from typing import Literal
7
+ import warnings
8
+
9
+ import numpy as np
10
+ import scipy.interpolate as spint
11
+
12
+ from ...warn import PipelineWarning
13
+ from .load import load_lut, load_mtext, register_lut # noqa: F401
14
+ from .pxcorr import get_pixelation_delta
15
+ from .pxcorr import get_pixelation_delta_pair # noqa: F401
16
+ # TODO: remove deprecated `convert`
17
+ from .scale_linear import convert # noqa: F401
18
+ from .scale_linear import scale_emodulus, scale_feature
19
+ from .viscosity import get_viscosity
20
+
21
+
22
+ #: Set this to True to globally enable spline extrapolation when the
23
+ #: `area_um`/`deform` data are outside the LUT. This is discouraged and
24
+ #: a :class:`KnowWhatYouAreDoingWarning` warning will be issued.
25
+ INACCURATE_SPLINE_EXTRAPOLATION = False
26
+
27
+
28
+ class KnowWhatYouAreDoingWarning(PipelineWarning):
29
+ pass
30
+
31
+
32
+ class YoungsModulusLookupTableExceededWarning(PipelineWarning):
33
+ pass
34
+
35
+
36
+ def extrapolate_emodulus(lut, datax, deform, emod, deform_norm,
37
+ deform_thresh=.05, inplace=True):
38
+ """Use spline interpolation to fill in nan-values
39
+
40
+ When points (`datax`, `deform`) are outside the convex
41
+ hull of the lut, then :func:`scipy.interpolate.griddata` returns
42
+ nan-valules.
43
+
44
+ With this function, some of these nan-values are extrapolated
45
+ using :class:`scipy.interpolate.SmoothBivariateSpline`. The
46
+ supported extrapolation values are currently limited to those
47
+ where the deformation is above 0.05.
48
+
49
+ A warning will be issued, because this is not really
50
+ recommended.
51
+
52
+ Parameters
53
+ ----------
54
+ lut: ndarray of shape (N, 3)
55
+ The normalized (!! see :func:`normalize`) LUT (first axis is
56
+ points, second axis enumerates datax, deform, and emodulus)
57
+ datax: ndarray of size N
58
+ The normalized x data (corresponding to `lut[:, 0]`)
59
+ deform: ndarray of size N
60
+ The normalized deform (corresponding to `lut[:, 1]`)
61
+ emod: ndarray of size N
62
+ The emodulus (corresponding to `lut[:, 2]`); If `emod`
63
+ does not contain nan-values, there is nothing to do here.
64
+ deform_norm: float
65
+ The normalization value used to normalize `lut[:, 1]` and
66
+ `deform`.
67
+ deform_thresh: float
68
+ Not the entire LUT is used for bivariate spline interpolation.
69
+ Only the points where `lut[:, 1] > deform_thresh/deform_norm`
70
+ are used. This is necessary, because for small deformations,
71
+ the LUT has an extreme slope that kills any meaningful
72
+ spline interpolation.
73
+ inplace: bool
74
+ If True (default), replaces nan values in `emod` in-place.
75
+ If False, `emod` is not modified.
76
+ """
77
+ if not inplace:
78
+ emod = np.array(emod, copy=True)
79
+ # unknowns are nans and deformation values above the threshold
80
+ unkn = np.logical_and(np.isnan(emod),
81
+ deform > deform_thresh/deform_norm)
82
+
83
+ if np.sum(unkn) == 0:
84
+ # nothing to do
85
+ return emod
86
+
87
+ warnings.warn("LUT extrapolation is barely tested and may yield "
88
+ + "unphysical values!",
89
+ KnowWhatYouAreDoingWarning)
90
+
91
+ lut_crop = lut[lut[:, 1] > deform_thresh/deform_norm, :]
92
+
93
+ itp = spint.SmoothBivariateSpline(x=lut_crop[:, 0],
94
+ y=lut_crop[:, 1],
95
+ z=lut_crop[:, 2],
96
+ )
97
+
98
+ emod[unkn] = itp.ev(datax[unkn], deform[unkn])
99
+ return emod
100
+
101
+
102
+ def get_emodulus(deform: float | np.array,
103
+ area_um: float | np.array | None = None,
104
+ volume: float | np.array | None = None,
105
+ medium: float | str = "0.49% MC-PBS",
106
+ channel_width: float = 20.0,
107
+ flow_rate: float = 0.16,
108
+ px_um: float = 0.34,
109
+ temperature: float | np.ndarray | None = 23.0,
110
+ lut_data: str | pathlib.Path | np.ndarray = "LE-2D-FEM-19",
111
+ visc_model: Literal['herold-2017',
112
+ 'herold-2017-fallback',
113
+ 'buyukurganci-2022',
114
+ 'kestin-1978',
115
+ None] = "herold-2017-fallback",
116
+ extrapolate: bool = INACCURATE_SPLINE_EXTRAPOLATION,
117
+ copy: bool = True):
118
+ """Compute apparent Young's modulus using a look-up table
119
+
120
+ Parameters
121
+ ----------
122
+ area_um: float or ndarray
123
+ Apparent (2D image) area [µm²] of the event(s)
124
+ deform: float or ndarray
125
+ Deformation (1-circularity) of the event(s)
126
+ volume: float or ndarray
127
+ Apparent volume of the event(s). It is not possible to define
128
+ `volume` and `area_um` at the same time (makes no sense).
129
+
130
+ .. versionadded:: 0.25.0
131
+ medium: str or float
132
+ The medium to compute the viscosity for. If a string
133
+ is given, the viscosity is computed. If a float is given,
134
+ this value is used as the viscosity in mPa*s (Note that
135
+ `temperature` and `visc_model` must be set to None in this case).
136
+ channel_width: float
137
+ The channel width [µm]
138
+ flow_rate: float
139
+ Flow rate [µL/s]
140
+ px_um: float
141
+ The detector pixel size [µm] used for pixelation correction.
142
+ Set to zero to disable.
143
+ temperature: float, ndarray, or None
144
+ Temperature [°C] of the event(s)
145
+ lut_data: path, str, or tuple of (np.ndarray of shape (N, 3), dict)
146
+ The LUT data to use. If it is a built-in identifier,
147
+ then the respective LUT will be used. Otherwise, a path to a
148
+ file on disk or a tuple (LUT array, metadata) is possible.
149
+ The LUT metadata is used to check whether the given features
150
+ (e.g. `area_um` and `deform`) are valid interpolation choices.
151
+
152
+ .. versionadded:: 0.25.0
153
+ visc_model: str
154
+ The viscosity model to use,
155
+ see :func:`dclab.features.emodulus.viscosity.get_viscosity`
156
+ extrapolate: bool
157
+ Perform extrapolation using :func:`extrapolate_emodulus`. This
158
+ is discouraged!
159
+ copy: bool
160
+ Copy input arrays. If set to false, input arrays are
161
+ overridden.
162
+
163
+ Returns
164
+ -------
165
+ elasticity: float or ndarray
166
+ Apparent Young's modulus in kPa
167
+
168
+ Notes
169
+ -----
170
+ - The look-up table used was computed with finite elements methods
171
+ according to :cite:`Mokbel2017` and complemented with analytical
172
+ isoelastics from :cite:`Mietke2015`. The original simulation
173
+ results are available on figshare :cite:`FigshareWittwer2020`.
174
+ - The computation of the Young's modulus takes into account
175
+ a correction for the viscosity (medium, channel width, flow rate,
176
+ and temperature) :cite:`Mietke2015` and a correction for
177
+ pixelation for the deformation which were derived
178
+ from a (pixelated) image :cite:`Herold2017`.
179
+ - Note that while deformation is pixelation-corrected, area_um and
180
+ volume are scaled to match the LUT data. This is somewhat
181
+ fortunate, because we don't have to worry about the order of
182
+ applying pixelation correction and scale conversion.
183
+ - By using external LUTs, it is possible to interpolate on the
184
+ volume-deformation plane. This feature was added in version
185
+ 0.25.0.
186
+
187
+ See Also
188
+ --------
189
+ dclab.features.emodulus.viscosity.get_viscosity: compute viscosity
190
+ for known media
191
+ """
192
+ # Get lut data
193
+ lut, lut_meta = load_lut(lut_data)
194
+ # Get the correct features (sanity checks)
195
+ featx, featy, _ = lut_meta["column features"]
196
+ if featx == "area_um" and featy == "deform":
197
+ assert volume is None, "Don't define area_um and volume at same time!"
198
+ datax = np.array(area_um, dtype=float, copy=copy)
199
+ elif featx == "volume" and featy == "deform":
200
+ assert area_um is None, "Don't define area_um and volume at same time!"
201
+ datax = np.array(volume, dtype=float, copy=copy)
202
+ else:
203
+ raise KeyError("No recipe for '{}' and '{}'!".format(featx, featy))
204
+
205
+ # copy deform so we can use in-place pixel correction
206
+ deform = np.array(deform, dtype=float, copy=copy)
207
+ if px_um:
208
+ # Correct deformation for pixelation effect (subtract ddelt).
209
+ # It is important to do this before scaling datax (area_um/volume).
210
+ deform -= get_pixelation_delta(feat_corr=featy,
211
+ feat_absc=featx,
212
+ data_absc=datax,
213
+ px_um=px_um)
214
+
215
+ # Compute viscosity
216
+ if isinstance(medium, numbers.Number):
217
+ visco = medium
218
+ if temperature is not None:
219
+ raise ValueError("If `medium` is given in Pa*s, then "
220
+ + "`temperature` must be set to None!")
221
+ if visc_model is not None:
222
+ warnings.warn("If `medium` is given in Pa*s, then `visc_model` "
223
+ "must be set to None. An exception will be raised "
224
+ "in future versions of dclab.",
225
+ DeprecationWarning)
226
+ else:
227
+ visco = get_viscosity(medium=medium, channel_width=channel_width,
228
+ flow_rate=flow_rate, temperature=temperature,
229
+ model=visc_model)
230
+
231
+ if isinstance(visco, np.ndarray):
232
+ # New in dclab 0.20.0: Computation for viscosities array
233
+ # Convert the input area_um to that of the LUT (deform does not change)
234
+ scale_kw = {"channel_width_in": channel_width,
235
+ "channel_width_out": lut_meta["channel_width"],
236
+ "inplace": False}
237
+ datax_4lut = scale_feature(feat=featx, data=datax, **scale_kw)
238
+ deform_4lut = np.array(deform, dtype=float, copy=copy)
239
+
240
+ # Normalize interpolation data such that the spacing for
241
+ # area and deformation is about the same during interpolation.
242
+ featx_norm = lut[:, 0].max()
243
+ normalize(lut[:, 0], featx_norm)
244
+ normalize(datax_4lut, featx_norm)
245
+
246
+ defo_norm = lut[:, 1].max()
247
+ normalize(lut[:, 1], defo_norm)
248
+ normalize(deform_4lut, defo_norm)
249
+
250
+ # Perform interpolation
251
+ emod = spint.griddata((lut[:, 0], lut[:, 1]), lut[:, 2],
252
+ (datax_4lut, deform_4lut),
253
+ method='linear')
254
+
255
+ if extrapolate:
256
+ # New in dclab 0.23.0: Perform extrapolation outside of the LUT
257
+ # This is not well-tested and thus discouraged!
258
+ extrapolate_emodulus(lut=lut,
259
+ datax=datax_4lut,
260
+ deform=deform_4lut,
261
+ emod=emod,
262
+ deform_norm=defo_norm,
263
+ inplace=True)
264
+
265
+ # Convert the LUT-interpolated emodulus back
266
+ backscale_kw = {"channel_width_in": lut_meta["channel_width"],
267
+ "channel_width_out": channel_width,
268
+ "flow_rate_in": lut_meta["flow_rate"],
269
+ "flow_rate_out": flow_rate,
270
+ "viscosity_in": lut_meta["fluid_viscosity"],
271
+ "viscosity_out": visco,
272
+ "inplace": True}
273
+ # deformation is not scaled (no units)
274
+ scale_feature(feat=featx, data=datax_4lut, **backscale_kw)
275
+ scale_emodulus(emod, **backscale_kw)
276
+ else:
277
+ # Corrections
278
+ # We correct the LUT, because it contains less points than
279
+ # the event data. Furthermore, the lut could be cached
280
+ # in the future, if this takes up a lot of time.
281
+ scale_kw = {"channel_width_in": lut_meta["channel_width"],
282
+ "channel_width_out": channel_width,
283
+ "flow_rate_in": lut_meta["flow_rate"],
284
+ "flow_rate_out": flow_rate,
285
+ "viscosity_in": lut_meta["fluid_viscosity"],
286
+ "viscosity_out": visco,
287
+ "inplace": True}
288
+ # deformation is not scaled (no units)
289
+ scale_feature(feat=featx, data=lut[:, 0], **scale_kw)
290
+ scale_emodulus(lut[:, 2], **scale_kw)
291
+
292
+ # Normalize interpolation data such that the spacing for
293
+ # area and deformation is about the same during interpolation.
294
+ featx_norm = lut[:, 0].max()
295
+ normalize(lut[:, 0], featx_norm)
296
+ normalize(datax, featx_norm)
297
+
298
+ defo_norm = lut[:, 1].max()
299
+ normalize(lut[:, 1], defo_norm)
300
+ normalize(deform, defo_norm)
301
+
302
+ # Perform interpolation
303
+ emod = spint.griddata((lut[:, 0], lut[:, 1]), lut[:, 2],
304
+ (datax, deform),
305
+ method='linear')
306
+
307
+ if extrapolate:
308
+ # New in dclab 0.23.0: Perform extrapolation outside of the LUT
309
+ # This is not well-tested and thus discouraged!
310
+ extrapolate_emodulus(lut=lut,
311
+ datax=datax,
312
+ deform=deform,
313
+ emod=emod,
314
+ deform_norm=defo_norm,
315
+ inplace=True)
316
+
317
+ # Let the user know when the emodulus contains too many nan values
318
+ nans = np.sum(np.isnan(emod))
319
+ if nans / emod.size > 0.1:
320
+ warnings.warn("The Young's modulus could not be computed for "
321
+ + "{:.0f}% of the data. ".format(nans/emod.size*100)
322
+ + "This is because they are not covered by the "
323
+ + "look-up table '{}'.".format(lut_data),
324
+ YoungsModulusLookupTableExceededWarning)
325
+
326
+ return emod
327
+
328
+
329
+ def normalize(data, dmax):
330
+ """Perform normalization in-place for interpolation
331
+
332
+ Note that :func:`scipy.interpolate.griddata` has a `rescale`
333
+ option which rescales the data onto the unit cube. For some
334
+ reason this does not work well with LUT data, so we
335
+ just normalize it by dividing by the maximum value.
336
+ """
337
+ assert isinstance(data, np.ndarray)
338
+ data /= dmax
339
+ return data