dclab 0.62.17__cp39-cp39-macosx_11_0_arm64.whl → 0.67.3__cp39-cp39-macosx_11_0_arm64.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.
- dclab/_version.py +16 -3
- dclab/cli/task_tdms2rtdc.py +1 -1
- dclab/cli/task_verify_dataset.py +3 -3
- dclab/definitions/__init__.py +1 -1
- dclab/definitions/feat_const.py +6 -4
- dclab/definitions/feat_logic.py +27 -28
- dclab/downsampling.cpython-39-darwin.so +0 -0
- dclab/downsampling.pyx +12 -7
- dclab/external/skimage/_find_contours_cy.cpython-39-darwin.so +0 -0
- dclab/external/skimage/_pnpoly.cpython-39-darwin.so +0 -0
- dclab/external/skimage/_shared/geometry.cpython-39-darwin.so +0 -0
- dclab/features/bright.py +11 -2
- dclab/features/bright_bc.py +13 -2
- dclab/features/bright_perc.py +10 -2
- dclab/features/contour.py +12 -7
- dclab/features/emodulus/__init__.py +33 -27
- dclab/features/emodulus/load.py +8 -6
- dclab/features/emodulus/pxcorr.py +33 -15
- dclab/features/emodulus/scale_linear.py +79 -52
- dclab/features/emodulus/viscosity.py +31 -19
- dclab/features/fl_crosstalk.py +19 -10
- dclab/features/inert_ratio.py +18 -11
- dclab/features/volume.py +24 -14
- dclab/http_utils.py +1 -1
- dclab/kde/base.py +238 -14
- dclab/kde/methods.py +33 -12
- dclab/rtdc_dataset/config.py +1 -1
- dclab/rtdc_dataset/core.py +22 -8
- dclab/rtdc_dataset/export.py +171 -34
- dclab/rtdc_dataset/feat_basin.py +250 -33
- dclab/rtdc_dataset/fmt_dcor/api.py +69 -7
- dclab/rtdc_dataset/fmt_dcor/base.py +103 -4
- dclab/rtdc_dataset/fmt_dcor/logs.py +1 -1
- dclab/rtdc_dataset/fmt_dcor/tables.py +1 -1
- dclab/rtdc_dataset/fmt_hdf5/events.py +20 -1
- dclab/rtdc_dataset/fmt_hierarchy/base.py +1 -1
- dclab/rtdc_dataset/fmt_s3.py +29 -10
- dclab/rtdc_dataset/fmt_tdms/event_trace.py +1 -1
- dclab/rtdc_dataset/fmt_tdms/naming.py +1 -1
- dclab/rtdc_dataset/writer.py +43 -11
- dclab/statistics.py +27 -4
- dclab/warn.py +1 -1
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/METADATA +26 -4
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/RECORD +48 -48
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/WHEEL +1 -1
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/entry_points.txt +0 -0
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/licenses/LICENSE +0 -0
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/top_level.txt +0 -0
dclab/features/volume.py
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
"""Volume computation based on contour revolution"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
2
4
|
import numpy as np
|
|
5
|
+
import numpy.typing as npt
|
|
3
6
|
|
|
4
7
|
|
|
5
|
-
def get_volume(
|
|
8
|
+
def get_volume(
|
|
9
|
+
cont: npt.NDArray | list[npt.NDArray],
|
|
10
|
+
pos_x: float | npt.NDArray,
|
|
11
|
+
pos_y: float | npt.NDArray,
|
|
12
|
+
pix: float,
|
|
13
|
+
fix_orientation: bool = False) -> float | npt.NDArray:
|
|
6
14
|
"""Calculate the volume of a polygon revolved around an axis
|
|
7
15
|
|
|
8
16
|
The volume estimation assumes rotational symmetry.
|
|
@@ -20,10 +28,10 @@ def get_volume(cont, pos_x, pos_y, pix, fix_orientation=False):
|
|
|
20
28
|
pos_y: float or ndarray of length N
|
|
21
29
|
The y coordinate(s) of the centroid of the event(s) [µm]
|
|
22
30
|
e.g. obtained using `mm.pos_y`
|
|
23
|
-
pix
|
|
31
|
+
pix
|
|
24
32
|
The detector pixel size in µm.
|
|
25
33
|
e.g. obtained using: `mm.config["imaging"]["pixel size"]`
|
|
26
|
-
fix_orientation
|
|
34
|
+
fix_orientation
|
|
27
35
|
If set to True, make sure that the orientation of the
|
|
28
36
|
contour is counter-clockwise in the r-z plane
|
|
29
37
|
(see :func:`vol_revolve`). This is False by default, because
|
|
@@ -34,7 +42,7 @@ def get_volume(cont, pos_x, pos_y, pix, fix_orientation=False):
|
|
|
34
42
|
|
|
35
43
|
Returns
|
|
36
44
|
-------
|
|
37
|
-
volume
|
|
45
|
+
volume
|
|
38
46
|
volume in um^3
|
|
39
47
|
|
|
40
48
|
Notes
|
|
@@ -43,7 +51,7 @@ def get_volume(cont, pos_x, pos_y, pix, fix_orientation=False):
|
|
|
43
51
|
upper and the lower halves of the contour from which the
|
|
44
52
|
average is then used.
|
|
45
53
|
|
|
46
|
-
The volume is computed radially from the
|
|
54
|
+
The volume is computed radially from the center position
|
|
47
55
|
given by (`pos_x`, `pos_y`). For sufficiently smooth contours,
|
|
48
56
|
such as densely sampled ellipses, the center position does not
|
|
49
57
|
play an important role. For contours that are given on a coarse
|
|
@@ -125,7 +133,7 @@ def get_volume(cont, pos_x, pos_y, pix, fix_orientation=False):
|
|
|
125
133
|
return v_avg
|
|
126
134
|
|
|
127
135
|
|
|
128
|
-
def counter_clockwise(cx, cy):
|
|
136
|
+
def counter_clockwise(cx: npt.NDArray, cy: npt.NDArray) -> tuple[float, float]:
|
|
129
137
|
"""Put contour coordinates into counter-clockwise order
|
|
130
138
|
|
|
131
139
|
Parameters
|
|
@@ -135,7 +143,7 @@ def counter_clockwise(cx, cy):
|
|
|
135
143
|
|
|
136
144
|
Returns
|
|
137
145
|
-------
|
|
138
|
-
cx_cc, cy_cc
|
|
146
|
+
cx_cc, cy_cc
|
|
139
147
|
The x- and y-coordinates of the contour in
|
|
140
148
|
counter-clockwise orientation.
|
|
141
149
|
|
|
@@ -152,7 +160,9 @@ def counter_clockwise(cx, cy):
|
|
|
152
160
|
return cx, cy
|
|
153
161
|
|
|
154
162
|
|
|
155
|
-
def vol_revolve(r
|
|
163
|
+
def vol_revolve(r: npt.NDArray,
|
|
164
|
+
z: npt.NDArray,
|
|
165
|
+
point_scale: float = 1.) -> float | npt.NDArray:
|
|
156
166
|
r"""Calculate the volume of a polygon revolved around the Z-axis
|
|
157
167
|
|
|
158
168
|
This implementation yields the same results as the volRevolve
|
|
@@ -183,11 +193,11 @@ def vol_revolve(r, z, point_scale=1.):
|
|
|
183
193
|
|
|
184
194
|
Parameters
|
|
185
195
|
----------
|
|
186
|
-
r: 1d
|
|
196
|
+
r: 1d ndarray
|
|
187
197
|
radial coordinates (perpendicular to the z axis)
|
|
188
|
-
z: 1d
|
|
198
|
+
z: 1d ndarray
|
|
189
199
|
coordinate along the axis of rotation
|
|
190
|
-
point_scale
|
|
200
|
+
point_scale
|
|
191
201
|
point size in your preferred units; The volume is multiplied
|
|
192
202
|
by a factor of `point_scale**3`.
|
|
193
203
|
|
|
@@ -222,9 +232,9 @@ def vol_revolve(r, z, point_scale=1.):
|
|
|
222
232
|
# dr = R - r and dz = h, then we get three terms for the volume
|
|
223
233
|
# (as opposed to four terms in Olynyk's script). Those three terms
|
|
224
234
|
# all resemble area slices multiplied by the z-distance dz.
|
|
225
|
-
a1 = 3 * rp**2
|
|
226
|
-
a2 = 3 * rp*dr
|
|
227
|
-
a3 = dr**2
|
|
235
|
+
a1 = 3 * rp ** 2
|
|
236
|
+
a2 = 3 * rp * dr
|
|
237
|
+
a3 = dr ** 2
|
|
228
238
|
|
|
229
239
|
# Note that the formula for computing the volume is symmetric
|
|
230
240
|
# with respect to r and R. This means that it does not matter
|
dclab/http_utils.py
CHANGED
dclab/kde/base.py
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
|
+
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
4
5
|
|
|
5
|
-
from .methods import
|
|
6
|
+
from .methods import bin_width_doane_div5, get_bad_vals, methods
|
|
7
|
+
from .contours import find_contours_level, get_quantile_levels
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ContourSpacingTooLarge(UserWarning):
|
|
11
|
+
pass
|
|
6
12
|
|
|
7
13
|
|
|
8
14
|
class KernelDensityEstimator:
|
|
@@ -107,6 +113,139 @@ class KernelDensityEstimator:
|
|
|
107
113
|
yscale: str
|
|
108
114
|
See `xscale`.
|
|
109
115
|
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
X, Y, Z : coordinates
|
|
119
|
+
The kernel density Z evaluated on a rectangular grid (X,Y).
|
|
120
|
+
"""
|
|
121
|
+
warnings.warn("`get_contour` is deprecated; please use "
|
|
122
|
+
"`get_raster` instead", DeprecationWarning)
|
|
123
|
+
return self.get_raster(
|
|
124
|
+
xax=xax, yax=yax, xacc=xacc, yacc=yacc,
|
|
125
|
+
kde_type=kde_type, kde_kwargs=kde_kwargs,
|
|
126
|
+
xscale=xscale, yscale=yscale
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def get_contour_lines(self, quantiles=None, xax="area_um", yax="deform",
|
|
130
|
+
xacc=None, yacc=None, kde_type="histogram",
|
|
131
|
+
kde_kwargs=None, xscale="linear", yscale="linear",
|
|
132
|
+
ret_levels=False):
|
|
133
|
+
"""Compute contour lines for a given kernel kensity estimate.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
quantiles: list or array of floats
|
|
138
|
+
KDE Quantiles for which contour levels are computed. The
|
|
139
|
+
values must be between 0 and 1. If set to None, use
|
|
140
|
+
[0.5, 0.95] as default.
|
|
141
|
+
xax: str
|
|
142
|
+
Identifier for X axis (e.g. "area_um", "aspect", "deform")
|
|
143
|
+
yax: str
|
|
144
|
+
Identifier for Y axis
|
|
145
|
+
xacc: float
|
|
146
|
+
Contour accuracy in x direction
|
|
147
|
+
if set to None, will use :func:`bin_width_doane_div5`
|
|
148
|
+
yacc: float
|
|
149
|
+
Contour accuracy in y direction
|
|
150
|
+
if set to None, will use :func:`bin_width_doane_div5`
|
|
151
|
+
kde_type: str
|
|
152
|
+
The KDE method to use
|
|
153
|
+
kde_kwargs: dict
|
|
154
|
+
Additional keyword arguments to the KDE method
|
|
155
|
+
xscale: str
|
|
156
|
+
If set to "log", take the logarithm of the x-values before
|
|
157
|
+
computing the KDE. This is useful when data are
|
|
158
|
+
displayed on a log-scale. Defaults to "linear".
|
|
159
|
+
yscale: str
|
|
160
|
+
See `xscale`
|
|
161
|
+
ret_levels: bool
|
|
162
|
+
If set to True, return the levels of the contours
|
|
163
|
+
(default: False)
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
contour_lines: list of lists (of lists)
|
|
168
|
+
For every number in `quantiles`, this list contains a list of
|
|
169
|
+
corresponding contour lines. Each contour line is a 2D
|
|
170
|
+
array of shape (N, 2), where N is the number of points in the
|
|
171
|
+
contour line.
|
|
172
|
+
levels: list of floats
|
|
173
|
+
The density levels corresponding to each number in `quantiles`.
|
|
174
|
+
Only returned if `ret_levels` is set to True.
|
|
175
|
+
"""
|
|
176
|
+
if not quantiles:
|
|
177
|
+
quantiles = [0.5, 0.95]
|
|
178
|
+
try:
|
|
179
|
+
x, y, density = self.get_raster(
|
|
180
|
+
xax=xax,
|
|
181
|
+
yax=yax,
|
|
182
|
+
xacc=xacc,
|
|
183
|
+
yacc=yacc,
|
|
184
|
+
xscale=xscale,
|
|
185
|
+
yscale=yscale,
|
|
186
|
+
kde_type=kde_type,
|
|
187
|
+
kde_kwargs=kde_kwargs,
|
|
188
|
+
)
|
|
189
|
+
except ValueError:
|
|
190
|
+
# most-likely there is nothing to compute a contour for
|
|
191
|
+
return []
|
|
192
|
+
if density.shape[0] < 3 or density.shape[1] < 3:
|
|
193
|
+
warnings.warn("Contour not possible; spacing may be too large!",
|
|
194
|
+
ContourSpacingTooLarge)
|
|
195
|
+
return []
|
|
196
|
+
levels = get_quantile_levels(
|
|
197
|
+
density=density,
|
|
198
|
+
x=x,
|
|
199
|
+
y=y,
|
|
200
|
+
xp=self.rtdc_ds[xax][self.rtdc_ds.filter.all],
|
|
201
|
+
yp=self.rtdc_ds[yax][self.rtdc_ds.filter.all],
|
|
202
|
+
q=np.array(quantiles),
|
|
203
|
+
normalize=False)
|
|
204
|
+
contours = []
|
|
205
|
+
# Normalize levels to [0, 1]
|
|
206
|
+
nlevels = np.array(levels) / density.max()
|
|
207
|
+
for nlev in nlevels:
|
|
208
|
+
# make sure that the contour levels are not at the boundaries
|
|
209
|
+
if not (np.allclose(nlev, 0, atol=1e-12, rtol=0)
|
|
210
|
+
or np.allclose(nlev, 1, atol=1e-12, rtol=0)):
|
|
211
|
+
cc = find_contours_level(
|
|
212
|
+
density, x=x, y=y, level=nlev)
|
|
213
|
+
contours.append(cc)
|
|
214
|
+
else:
|
|
215
|
+
contours.append([])
|
|
216
|
+
if ret_levels:
|
|
217
|
+
return contours, levels
|
|
218
|
+
else:
|
|
219
|
+
return contours
|
|
220
|
+
|
|
221
|
+
def get_raster(self, xax="area_um", yax="deform", xacc=None, yacc=None,
|
|
222
|
+
kde_type="histogram", kde_kwargs=None, xscale="linear",
|
|
223
|
+
yscale="linear"):
|
|
224
|
+
"""Evaluate the kernel density estimate on a grid
|
|
225
|
+
|
|
226
|
+
Parameters
|
|
227
|
+
----------
|
|
228
|
+
xax: str
|
|
229
|
+
Identifier for X axis (e.g. "area_um", "aspect", "deform")
|
|
230
|
+
yax: str
|
|
231
|
+
Identifier for Y axis
|
|
232
|
+
xacc: float
|
|
233
|
+
Contour accuracy in x direction
|
|
234
|
+
if set to None, will use :func:`bin_width_doane_div5`
|
|
235
|
+
yacc: float
|
|
236
|
+
Contour accuracy in y direction
|
|
237
|
+
if set to None, will use :func:`bin_width_doane_div5`
|
|
238
|
+
kde_type: str
|
|
239
|
+
The KDE method to use
|
|
240
|
+
kde_kwargs: dict
|
|
241
|
+
Additional keyword arguments to the KDE method
|
|
242
|
+
xscale: str
|
|
243
|
+
If set to "log", take the logarithm of the x-values before
|
|
244
|
+
computing the KDE. This is useful when data are
|
|
245
|
+
displayed on a log-scale. Defaults to "linear".
|
|
246
|
+
yscale: str
|
|
247
|
+
See `xscale`.
|
|
248
|
+
|
|
110
249
|
Returns
|
|
111
250
|
-------
|
|
112
251
|
X, Y, Z : coordinates
|
|
@@ -128,42 +267,45 @@ class KernelDensityEstimator:
|
|
|
128
267
|
a=x,
|
|
129
268
|
feat=xax,
|
|
130
269
|
scale=xscale,
|
|
131
|
-
method=
|
|
270
|
+
method=bin_width_doane_div5,
|
|
132
271
|
ret_scaled=True)
|
|
133
272
|
|
|
134
273
|
yacc_sc, ys = self.get_spacing(
|
|
135
274
|
a=y,
|
|
136
275
|
feat=yax,
|
|
137
276
|
scale=yscale,
|
|
138
|
-
method=
|
|
277
|
+
method=bin_width_doane_div5,
|
|
139
278
|
ret_scaled=True)
|
|
140
279
|
|
|
141
280
|
if xacc is None or xacc == 0:
|
|
142
|
-
xacc = xacc_sc
|
|
281
|
+
xacc = xacc_sc
|
|
143
282
|
|
|
144
283
|
if yacc is None or yacc == 0:
|
|
145
|
-
yacc = yacc_sc
|
|
284
|
+
yacc = yacc_sc
|
|
146
285
|
|
|
147
286
|
# Ignore infs and nans
|
|
148
287
|
bad = get_bad_vals(xs, ys)
|
|
149
288
|
xc = xs[~bad]
|
|
150
289
|
yc = ys[~bad]
|
|
151
290
|
|
|
152
|
-
|
|
153
|
-
|
|
291
|
+
if xc.size:
|
|
292
|
+
# Compute the mesh for rastering
|
|
293
|
+
xnum = int(np.ceil((xc.max() - xc.min()) / xacc))
|
|
294
|
+
ynum = int(np.ceil((yc.max() - yc.min()) / yacc))
|
|
154
295
|
|
|
155
|
-
|
|
156
|
-
|
|
296
|
+
xlin = np.linspace(xc.min(), xc.max(), xnum, endpoint=True)
|
|
297
|
+
ylin = np.linspace(yc.min(), yc.max(), ynum, endpoint=True)
|
|
157
298
|
|
|
158
|
-
|
|
299
|
+
xmesh, ymesh = np.meshgrid(xlin, ylin, indexing="ij")
|
|
159
300
|
|
|
160
|
-
|
|
161
|
-
|
|
301
|
+
# Compute the KDE for each point on the mesh
|
|
302
|
+
kde_fct = methods[kde_type]
|
|
162
303
|
density = kde_fct(events_x=xs, events_y=ys,
|
|
163
304
|
xout=xmesh, yout=ymesh,
|
|
164
305
|
**kde_kwargs)
|
|
165
306
|
else:
|
|
166
|
-
|
|
307
|
+
xmesh, ymesh = np.meshgrid([0, 1], [0, 1], indexing="ij")
|
|
308
|
+
density = np.array(np.nan * xmesh)
|
|
167
309
|
|
|
168
310
|
# Convert mesh back to linear scale if applicable
|
|
169
311
|
if xscale == "log":
|
|
@@ -178,6 +320,8 @@ class KernelDensityEstimator:
|
|
|
178
320
|
yscale="linear"):
|
|
179
321
|
"""Evaluate the kernel density estimate for scatter plots
|
|
180
322
|
|
|
323
|
+
The KDE is evaluated with the `kde_type` function for every point.
|
|
324
|
+
|
|
181
325
|
Parameters
|
|
182
326
|
----------
|
|
183
327
|
xax: str
|
|
@@ -194,7 +338,7 @@ class KernelDensityEstimator:
|
|
|
194
338
|
Additional keyword arguments to the KDE method
|
|
195
339
|
xscale: str
|
|
196
340
|
If set to "log", take the logarithm of the x-values before
|
|
197
|
-
computing the KDE. This is useful when data are
|
|
341
|
+
computing the KDE. This is useful when data are
|
|
198
342
|
displayed on a log-scale. Defaults to "linear".
|
|
199
343
|
yscale: str
|
|
200
344
|
See `xscale`.
|
|
@@ -236,3 +380,83 @@ class KernelDensityEstimator:
|
|
|
236
380
|
density = np.array([])
|
|
237
381
|
|
|
238
382
|
return density
|
|
383
|
+
|
|
384
|
+
def get_at(self, xax="area_um", yax="deform", positions=None,
|
|
385
|
+
kde_type="histogram", kde_kwargs=None, xscale="linear",
|
|
386
|
+
yscale="linear"):
|
|
387
|
+
"""Evaluate the kernel density estimate for specific events
|
|
388
|
+
|
|
389
|
+
The KDE is computed via linear interpolation from the output
|
|
390
|
+
of `get_raster`.
|
|
391
|
+
|
|
392
|
+
Parameters
|
|
393
|
+
----------
|
|
394
|
+
xax: str
|
|
395
|
+
Identifier for X axis (e.g. "area_um", "aspect", "deform")
|
|
396
|
+
yax: str
|
|
397
|
+
Identifier for Y axis
|
|
398
|
+
positions: list of two 1d ndarrays or ndarray of shape (2, N)
|
|
399
|
+
The positions where the KDE will be computed. Note that
|
|
400
|
+
the KDE estimate is computed from the points that
|
|
401
|
+
are set in `self.rtdc_ds.filter.all`.
|
|
402
|
+
kde_type: str
|
|
403
|
+
The KDE method to use, see :const:`.kde_methods.methods`
|
|
404
|
+
kde_kwargs: dict
|
|
405
|
+
Additional keyword arguments to the KDE method
|
|
406
|
+
xscale: str
|
|
407
|
+
If set to "log", take the logarithm of the x-values before
|
|
408
|
+
computing the KDE. This is useful when data are
|
|
409
|
+
displayed on a log-scale. Defaults to "linear".
|
|
410
|
+
yscale: str
|
|
411
|
+
See `xscale`.
|
|
412
|
+
|
|
413
|
+
Returns
|
|
414
|
+
-------
|
|
415
|
+
density : 1d ndarray
|
|
416
|
+
The kernel density evaluated for the filtered events.
|
|
417
|
+
"""
|
|
418
|
+
if kde_kwargs is None:
|
|
419
|
+
kde_kwargs = {}
|
|
420
|
+
xax = xax.lower()
|
|
421
|
+
yax = yax.lower()
|
|
422
|
+
kde_type = kde_type.lower()
|
|
423
|
+
if kde_type not in methods:
|
|
424
|
+
raise ValueError(f"Not a valid kde type: {kde_type}!")
|
|
425
|
+
|
|
426
|
+
# Get data
|
|
427
|
+
x = self.rtdc_ds[xax][self.rtdc_ds.filter.all]
|
|
428
|
+
y = self.rtdc_ds[yax][self.rtdc_ds.filter.all]
|
|
429
|
+
|
|
430
|
+
# Apply scale (no change for linear scale)
|
|
431
|
+
xs = self.apply_scale(x, xscale, xax)
|
|
432
|
+
ys = self.apply_scale(y, yscale, yax)
|
|
433
|
+
|
|
434
|
+
if positions:
|
|
435
|
+
xs = self.apply_scale(positions[0], xscale, xax)
|
|
436
|
+
ys = self.apply_scale(positions[1], yscale, yax)
|
|
437
|
+
|
|
438
|
+
if len(x):
|
|
439
|
+
xr, yr, density_grid = self.get_raster(xax=xax,
|
|
440
|
+
yax=yax,
|
|
441
|
+
kde_type=kde_type,
|
|
442
|
+
kde_kwargs=kde_kwargs,
|
|
443
|
+
xscale=xscale,
|
|
444
|
+
yscale=yscale)
|
|
445
|
+
|
|
446
|
+
# Apply scale (no change for linear scale)
|
|
447
|
+
xrs = self.apply_scale(xr, xscale, xax)
|
|
448
|
+
yrs = self.apply_scale(yr, yscale, yax)
|
|
449
|
+
|
|
450
|
+
# 'scipy.interp2d' has been removed in SciPy 1.14.0
|
|
451
|
+
# https://scipy.github.io/devdocs/tutorial/interpolate/interp_transition_guide.html
|
|
452
|
+
interp_func = RGI((xrs[:, 0], yrs[0, :]),
|
|
453
|
+
density_grid,
|
|
454
|
+
method="linear",
|
|
455
|
+
bounds_error=False,
|
|
456
|
+
fill_value=np.nan)
|
|
457
|
+
density = interp_func((xs, ys))
|
|
458
|
+
|
|
459
|
+
else:
|
|
460
|
+
density = np.array([])
|
|
461
|
+
|
|
462
|
+
return density
|
dclab/kde/methods.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Kernel Density Estimation methods"""
|
|
2
|
+
import warnings
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
4
5
|
from scipy.interpolate import RectBivariateSpline
|
|
@@ -8,6 +9,10 @@ from ..cached import Cache
|
|
|
8
9
|
from ..external.statsmodels.nonparametric.kernel_density import KDEMultivariate
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
class KernelDensityEstimationForEmtpyArrayWarning(UserWarning):
|
|
13
|
+
"""Used when user attempts to compute KDE for an empty array"""
|
|
14
|
+
|
|
15
|
+
|
|
11
16
|
def bin_num_doane(a):
|
|
12
17
|
"""Compute number of bins based on Doane's formula
|
|
13
18
|
|
|
@@ -49,13 +54,28 @@ def bin_width_doane(a):
|
|
|
49
54
|
bad = np.isnan(a) | np.isinf(a)
|
|
50
55
|
data = a[~bad]
|
|
51
56
|
n = data.size
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
if n > 0:
|
|
58
|
+
g1 = skew(data)
|
|
59
|
+
sigma_g1 = np.sqrt(6 * (n - 2) / ((n + 1) * (n + 3)))
|
|
60
|
+
k = 1 + np.log2(n) + np.log2(1 + np.abs(g1) / sigma_g1)
|
|
61
|
+
acc = (data.max() - data.min()) / k
|
|
62
|
+
else:
|
|
63
|
+
warnings.warn("KDE encountered an empty array",
|
|
64
|
+
KernelDensityEstimationForEmtpyArrayWarning)
|
|
65
|
+
acc = 1
|
|
56
66
|
return acc
|
|
57
67
|
|
|
58
68
|
|
|
69
|
+
def bin_width_doane_div5(a):
|
|
70
|
+
"""Compute contour spacing based on Doane's formula divided by five
|
|
71
|
+
|
|
72
|
+
See Also
|
|
73
|
+
--------
|
|
74
|
+
bin_width_doane: method used to compute the bin width
|
|
75
|
+
"""
|
|
76
|
+
return bin_width_doane(a) / 5
|
|
77
|
+
|
|
78
|
+
|
|
59
79
|
def bin_width_percentile(a):
|
|
60
80
|
"""Compute contour spacing based on data percentiles
|
|
61
81
|
|
|
@@ -84,12 +104,12 @@ def get_bad_vals(x, y):
|
|
|
84
104
|
|
|
85
105
|
|
|
86
106
|
def ignore_nan_inf(kde_method):
|
|
87
|
-
"""
|
|
107
|
+
"""Decorator that computes the KDE only for valid values
|
|
88
108
|
|
|
89
109
|
Invalid positions in the resulting density are set to nan.
|
|
90
110
|
"""
|
|
91
|
-
def
|
|
92
|
-
|
|
111
|
+
def kde_wrapper(events_x, events_y, xout=None, yout=None,
|
|
112
|
+
*args, **kwargs):
|
|
93
113
|
bad_in = get_bad_vals(events_x, events_y)
|
|
94
114
|
if xout is None:
|
|
95
115
|
density = np.zeros_like(events_x, dtype=np.float64)
|
|
@@ -103,18 +123,19 @@ def ignore_nan_inf(kde_method):
|
|
|
103
123
|
# Filter events
|
|
104
124
|
ev_x = events_x[~bad_in]
|
|
105
125
|
ev_y = events_y[~bad_in]
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
126
|
+
if ev_x.size:
|
|
127
|
+
density[~bad_out] = kde_method(ev_x, ev_y,
|
|
128
|
+
xo, yo,
|
|
129
|
+
*args, **kwargs)
|
|
109
130
|
density[bad_out] = np.nan
|
|
110
131
|
return density
|
|
111
132
|
|
|
112
133
|
doc_add = "\n Notes\n" +\
|
|
113
134
|
" -----\n" +\
|
|
114
135
|
" This is a wrapped version that ignores nan and inf values."
|
|
115
|
-
|
|
136
|
+
kde_wrapper.__doc__ = kde_method.__doc__ + doc_add
|
|
116
137
|
|
|
117
|
-
return
|
|
138
|
+
return kde_wrapper
|
|
118
139
|
|
|
119
140
|
|
|
120
141
|
@ignore_nan_inf
|
dclab/rtdc_dataset/config.py
CHANGED
|
@@ -437,7 +437,7 @@ def load_from_file(cfg_file):
|
|
|
437
437
|
convfunc = dfn.get_config_value_func(sec, var)
|
|
438
438
|
val = convfunc(val)
|
|
439
439
|
else:
|
|
440
|
-
# unknown parameter (e.g. plotting in
|
|
440
|
+
# unknown parameter (e.g. plotting in DCscope), guess type
|
|
441
441
|
var, val = keyval_str2typ(var, val)
|
|
442
442
|
if len(var) != 0 and len(str(val)) != 0:
|
|
443
443
|
cfg[sec][var] = val
|
dclab/rtdc_dataset/core.py
CHANGED
|
@@ -307,6 +307,9 @@ class RTDCBase(abc.ABC):
|
|
|
307
307
|
data = bn.get_feature_data(feat)
|
|
308
308
|
# The data are available, we may abort the search.
|
|
309
309
|
break
|
|
310
|
+
except feat_basin.BasinIdentifierMismatchError:
|
|
311
|
+
# Likely a basin identifier mismatch
|
|
312
|
+
warnings.warn(traceback.format_exc())
|
|
310
313
|
except (KeyError, OSError, PermissionError):
|
|
311
314
|
# Basin data not available
|
|
312
315
|
pass
|
|
@@ -635,7 +638,7 @@ class RTDCBase(abc.ABC):
|
|
|
635
638
|
The kernel density Z evaluated on a rectangular grid (X,Y).
|
|
636
639
|
"""
|
|
637
640
|
kde_instance = KernelDensityEstimator(rtdc_ds=self)
|
|
638
|
-
xmesh, ymesh, density = kde_instance.
|
|
641
|
+
xmesh, ymesh, density = kde_instance.get_raster(
|
|
639
642
|
xax=xax, yax=yax, xacc=xacc, yacc=yacc, kde_type=kde_type,
|
|
640
643
|
kde_kwargs=kde_kwargs, xscale=xscale, yscale=yscale
|
|
641
644
|
)
|
|
@@ -741,11 +744,15 @@ class RTDCBase(abc.ABC):
|
|
|
741
744
|
# need the referring dataset.
|
|
742
745
|
"mapping_referrer": self,
|
|
743
746
|
# Make sure the measurement identifier is checked.
|
|
744
|
-
"
|
|
747
|
+
"referrer_identifier": self.get_measurement_identifier(),
|
|
748
|
+
# Make sure the basin identifier is checked.
|
|
749
|
+
"basin_identifier": bdict.get("identifier"),
|
|
745
750
|
# allow to ignore basins
|
|
746
751
|
"ignored_basins": bd_keys,
|
|
747
752
|
# basin key
|
|
748
753
|
"key": bdict["key"],
|
|
754
|
+
# whether the basin is perishable or not
|
|
755
|
+
"perishable": bdict.get("perishable", False),
|
|
749
756
|
}
|
|
750
757
|
|
|
751
758
|
# Check whether this basin is supported and exists
|
|
@@ -783,12 +790,19 @@ class RTDCBase(abc.ABC):
|
|
|
783
790
|
b_cls = bc[bdict["format"]]
|
|
784
791
|
# Try absolute path
|
|
785
792
|
bna = b_cls(pp, **kwargs)
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
793
|
+
|
|
794
|
+
try:
|
|
795
|
+
absolute_exists = bna.verify_basin()
|
|
796
|
+
except BaseException:
|
|
797
|
+
pass
|
|
798
|
+
else:
|
|
799
|
+
if absolute_exists:
|
|
800
|
+
basins.append(bna)
|
|
801
|
+
break
|
|
789
802
|
# Try relative path
|
|
790
803
|
this_path = pathlib.Path(self.path)
|
|
791
804
|
if this_path.exists():
|
|
805
|
+
|
|
792
806
|
# Insert relative path
|
|
793
807
|
bnr = b_cls(this_path.parent / pp, **kwargs)
|
|
794
808
|
if bnr.verify_basin():
|
|
@@ -826,9 +840,9 @@ class RTDCBase(abc.ABC):
|
|
|
826
840
|
identifier = self.config.get("experiment", {}).get("run identifier",
|
|
827
841
|
None)
|
|
828
842
|
if identifier is None:
|
|
829
|
-
time = self.config.get("experiment", {}).get("time", None)
|
|
830
|
-
date = self.config.get("experiment", {}).get("date", None)
|
|
831
|
-
sid = self.config.get("setup", {}).get("identifier", None)
|
|
843
|
+
time = self.config.get("experiment", {}).get("time", None) or None
|
|
844
|
+
date = self.config.get("experiment", {}).get("date", None) or None
|
|
845
|
+
sid = self.config.get("setup", {}).get("identifier", None) or None
|
|
832
846
|
if None not in [time, date, sid]:
|
|
833
847
|
# only compute an identifier if all of the above are defined.
|
|
834
848
|
hasher = hashlib.md5(f"{time}_{date}_{sid}".encode("utf-8"))
|