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.
Files changed (48) hide show
  1. dclab/_version.py +16 -3
  2. dclab/cli/task_tdms2rtdc.py +1 -1
  3. dclab/cli/task_verify_dataset.py +3 -3
  4. dclab/definitions/__init__.py +1 -1
  5. dclab/definitions/feat_const.py +6 -4
  6. dclab/definitions/feat_logic.py +27 -28
  7. dclab/downsampling.cpython-39-darwin.so +0 -0
  8. dclab/downsampling.pyx +12 -7
  9. dclab/external/skimage/_find_contours_cy.cpython-39-darwin.so +0 -0
  10. dclab/external/skimage/_pnpoly.cpython-39-darwin.so +0 -0
  11. dclab/external/skimage/_shared/geometry.cpython-39-darwin.so +0 -0
  12. dclab/features/bright.py +11 -2
  13. dclab/features/bright_bc.py +13 -2
  14. dclab/features/bright_perc.py +10 -2
  15. dclab/features/contour.py +12 -7
  16. dclab/features/emodulus/__init__.py +33 -27
  17. dclab/features/emodulus/load.py +8 -6
  18. dclab/features/emodulus/pxcorr.py +33 -15
  19. dclab/features/emodulus/scale_linear.py +79 -52
  20. dclab/features/emodulus/viscosity.py +31 -19
  21. dclab/features/fl_crosstalk.py +19 -10
  22. dclab/features/inert_ratio.py +18 -11
  23. dclab/features/volume.py +24 -14
  24. dclab/http_utils.py +1 -1
  25. dclab/kde/base.py +238 -14
  26. dclab/kde/methods.py +33 -12
  27. dclab/rtdc_dataset/config.py +1 -1
  28. dclab/rtdc_dataset/core.py +22 -8
  29. dclab/rtdc_dataset/export.py +171 -34
  30. dclab/rtdc_dataset/feat_basin.py +250 -33
  31. dclab/rtdc_dataset/fmt_dcor/api.py +69 -7
  32. dclab/rtdc_dataset/fmt_dcor/base.py +103 -4
  33. dclab/rtdc_dataset/fmt_dcor/logs.py +1 -1
  34. dclab/rtdc_dataset/fmt_dcor/tables.py +1 -1
  35. dclab/rtdc_dataset/fmt_hdf5/events.py +20 -1
  36. dclab/rtdc_dataset/fmt_hierarchy/base.py +1 -1
  37. dclab/rtdc_dataset/fmt_s3.py +29 -10
  38. dclab/rtdc_dataset/fmt_tdms/event_trace.py +1 -1
  39. dclab/rtdc_dataset/fmt_tdms/naming.py +1 -1
  40. dclab/rtdc_dataset/writer.py +43 -11
  41. dclab/statistics.py +27 -4
  42. dclab/warn.py +1 -1
  43. {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/METADATA +26 -4
  44. {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/RECORD +48 -48
  45. {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/WHEEL +1 -1
  46. {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/entry_points.txt +0 -0
  47. {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/licenses/LICENSE +0 -0
  48. {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(cont, pos_x, pos_y, pix, fix_orientation=False):
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: float
31
+ pix
24
32
  The detector pixel size in µm.
25
33
  e.g. obtained using: `mm.config["imaging"]["pixel size"]`
26
- fix_orientation: bool
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: float or ndarray
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 the center position
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, z, point_scale=1.):
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 np.ndarray
196
+ r: 1d ndarray
187
197
  radial coordinates (perpendicular to the z axis)
188
- z: 1d np.ndarray
198
+ z: 1d ndarray
189
199
  coordinate along the axis of rotation
190
- point_scale: float
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
@@ -254,7 +254,7 @@ class ResoluteRequestsSession(requests.Session):
254
254
  break
255
255
  else:
256
256
  raise requests.exceptions.ReadTimeout(
257
- f"Resolut sesion failed for {args} and {kwargs}!")
257
+ f"Resolute session failed for {args} and {kwargs}!")
258
258
  return resp
259
259
 
260
260
 
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 bin_width_doane, get_bad_vals, methods
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=bin_width_doane,
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=bin_width_doane,
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 / 5
281
+ xacc = xacc_sc
143
282
 
144
283
  if yacc is None or yacc == 0:
145
- yacc = yacc_sc / 5
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
- xnum = int(np.ceil((xc.max() - xc.min()) / xacc))
153
- ynum = int(np.ceil((yc.max() - yc.min()) / yacc))
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
- xlin = np.linspace(xc.min(), xc.max(), xnum, endpoint=True)
156
- ylin = np.linspace(yc.min(), yc.max(), ynum, endpoint=True)
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
- xmesh, ymesh = np.meshgrid(xlin, ylin, indexing="ij")
299
+ xmesh, ymesh = np.meshgrid(xlin, ylin, indexing="ij")
159
300
 
160
- kde_fct = methods[kde_type]
161
- if len(x):
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
- density = np.array([])
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 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
- g1 = skew(data)
53
- sigma_g1 = np.sqrt(6 * (n - 2) / ((n + 1) * (n + 3)))
54
- k = 1 + np.log2(n) + np.log2(1 + np.abs(g1) / sigma_g1)
55
- acc = (data.max() - data.min()) / k
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
- """Ignores nans and infs from the input data
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 new_kde_method(events_x, events_y, xout=None, yout=None,
92
- *args, **kwargs):
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
- density[~bad_out] = kde_method(ev_x, ev_y,
107
- xo, yo,
108
- *args, **kwargs)
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
- new_kde_method.__doc__ = kde_method.__doc__ + doc_add
136
+ kde_wrapper.__doc__ = kde_method.__doc__ + doc_add
116
137
 
117
- return new_kde_method
138
+ return kde_wrapper
118
139
 
119
140
 
120
141
  @ignore_nan_inf
@@ -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 Shape-Out), guess type
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
@@ -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.get_contour(
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
- "measurement_identifier": self.get_measurement_identifier(),
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
- if bna.verify_basin():
787
- basins.append(bna)
788
- break
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"))