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.
- 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-314t-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-314t-darwin.so +0 -0
- dclab/external/skimage/_find_contours_cy.pyx +188 -0
- dclab/external/skimage/_pnpoly.cpython-314t-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-314t-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/kde/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .base import KernelDensityEstimator # noqa: F401
|
dclab/kde/base.py
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
5
|
+
|
|
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
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class KernelDensityEstimator:
|
|
15
|
+
def __init__(self, rtdc_ds):
|
|
16
|
+
self.rtdc_ds = rtdc_ds
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def apply_scale(a, scale, feat):
|
|
20
|
+
"""Helper function for transforming an aray to log-scale
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
a: np.ndarray
|
|
25
|
+
Input array
|
|
26
|
+
scale: str
|
|
27
|
+
If set to "log", take the logarithm of `a`; if set to
|
|
28
|
+
"linear" return `a` unchanged.
|
|
29
|
+
feat: str
|
|
30
|
+
Feature name (required for debugging)
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
b: np.ndarray
|
|
35
|
+
The scaled array
|
|
36
|
+
|
|
37
|
+
Notes
|
|
38
|
+
-----
|
|
39
|
+
If the scale is not "linear", then a new array is returned.
|
|
40
|
+
All warnings are suppressed when computing `np.log(a)`, as
|
|
41
|
+
`a` may have negative or nan values.
|
|
42
|
+
"""
|
|
43
|
+
if scale == "linear":
|
|
44
|
+
b = a
|
|
45
|
+
elif scale == "log":
|
|
46
|
+
with warnings.catch_warnings(record=True) as w:
|
|
47
|
+
warnings.simplefilter("always")
|
|
48
|
+
b = np.log(a)
|
|
49
|
+
if len(w):
|
|
50
|
+
# Tell the user that the log-transformation issued
|
|
51
|
+
# a warning.
|
|
52
|
+
warnings.warn(f"Invalid values encounterd in np.log "
|
|
53
|
+
f"while scaling feature '{feat}'!")
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError(f"`scale` must be either 'linear' or 'log', "
|
|
56
|
+
f"got '{scale}'!")
|
|
57
|
+
return b
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def get_spacing(a, method, scale="linear", method_kw=None,
|
|
61
|
+
feat="undefined", ret_scaled=False):
|
|
62
|
+
"""Convenience function for computing the contour spacing
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
a: ndarray
|
|
67
|
+
feature data
|
|
68
|
+
scale: str
|
|
69
|
+
how the data should be scaled ("log" or "linear")
|
|
70
|
+
method: callable
|
|
71
|
+
KDE spacing method to use
|
|
72
|
+
method_kw: dict
|
|
73
|
+
keyword arguments to `method`
|
|
74
|
+
feat: str
|
|
75
|
+
feature name for debugging
|
|
76
|
+
ret_scaled: bool
|
|
77
|
+
whether to return the scaled array of `a`
|
|
78
|
+
"""
|
|
79
|
+
if method_kw is None:
|
|
80
|
+
method_kw = {}
|
|
81
|
+
# Apply scale (no change for linear scale)
|
|
82
|
+
asc = KernelDensityEstimator.apply_scale(a, scale, feat)
|
|
83
|
+
# Apply multiplicator
|
|
84
|
+
acc = method(asc, **method_kw)
|
|
85
|
+
if ret_scaled:
|
|
86
|
+
return acc, asc
|
|
87
|
+
else:
|
|
88
|
+
return acc
|
|
89
|
+
|
|
90
|
+
def get_contour(self, xax="area_um", yax="deform", xacc=None, yacc=None,
|
|
91
|
+
kde_type="histogram", kde_kwargs=None, xscale="linear",
|
|
92
|
+
yscale="linear"):
|
|
93
|
+
"""Evaluate the kernel density estimate for contour plots
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
xax: str
|
|
98
|
+
Identifier for X axis (e.g. "area_um", "aspect", "deform")
|
|
99
|
+
yax: str
|
|
100
|
+
Identifier for Y axis
|
|
101
|
+
xacc: float
|
|
102
|
+
Contour accuracy in x direction
|
|
103
|
+
yacc: float
|
|
104
|
+
Contour accuracy in y direction
|
|
105
|
+
kde_type: str
|
|
106
|
+
The KDE method to use
|
|
107
|
+
kde_kwargs: dict
|
|
108
|
+
Additional keyword arguments to the KDE method
|
|
109
|
+
xscale: str
|
|
110
|
+
If set to "log", take the logarithm of the x-values before
|
|
111
|
+
computing the KDE. This is useful when data are
|
|
112
|
+
displayed on a log-scale. Defaults to "linear".
|
|
113
|
+
yscale: str
|
|
114
|
+
See `xscale`.
|
|
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
|
+
|
|
249
|
+
Returns
|
|
250
|
+
-------
|
|
251
|
+
X, Y, Z : coordinates
|
|
252
|
+
The kernel density Z evaluated on a rectangular grid (X,Y).
|
|
253
|
+
"""
|
|
254
|
+
if kde_kwargs is None:
|
|
255
|
+
kde_kwargs = {}
|
|
256
|
+
xax = xax.lower()
|
|
257
|
+
yax = yax.lower()
|
|
258
|
+
kde_type = kde_type.lower()
|
|
259
|
+
if kde_type not in methods:
|
|
260
|
+
raise ValueError(f"Not a valid kde type: {kde_type}!")
|
|
261
|
+
|
|
262
|
+
# Get data
|
|
263
|
+
x = self.rtdc_ds[xax][self.rtdc_ds.filter.all]
|
|
264
|
+
y = self.rtdc_ds[yax][self.rtdc_ds.filter.all]
|
|
265
|
+
|
|
266
|
+
xacc_sc, xs = self.get_spacing(
|
|
267
|
+
a=x,
|
|
268
|
+
feat=xax,
|
|
269
|
+
scale=xscale,
|
|
270
|
+
method=bin_width_doane_div5,
|
|
271
|
+
ret_scaled=True)
|
|
272
|
+
|
|
273
|
+
yacc_sc, ys = self.get_spacing(
|
|
274
|
+
a=y,
|
|
275
|
+
feat=yax,
|
|
276
|
+
scale=yscale,
|
|
277
|
+
method=bin_width_doane_div5,
|
|
278
|
+
ret_scaled=True)
|
|
279
|
+
|
|
280
|
+
if xacc is None or xacc == 0:
|
|
281
|
+
xacc = xacc_sc
|
|
282
|
+
|
|
283
|
+
if yacc is None or yacc == 0:
|
|
284
|
+
yacc = yacc_sc
|
|
285
|
+
|
|
286
|
+
# Ignore infs and nans
|
|
287
|
+
bad = get_bad_vals(xs, ys)
|
|
288
|
+
xc = xs[~bad]
|
|
289
|
+
yc = ys[~bad]
|
|
290
|
+
|
|
291
|
+
xnum = int(np.ceil((xc.max() - xc.min()) / xacc))
|
|
292
|
+
ynum = int(np.ceil((yc.max() - yc.min()) / yacc))
|
|
293
|
+
|
|
294
|
+
xlin = np.linspace(xc.min(), xc.max(), xnum, endpoint=True)
|
|
295
|
+
ylin = np.linspace(yc.min(), yc.max(), ynum, endpoint=True)
|
|
296
|
+
|
|
297
|
+
xmesh, ymesh = np.meshgrid(xlin, ylin, indexing="ij")
|
|
298
|
+
|
|
299
|
+
kde_fct = methods[kde_type]
|
|
300
|
+
if len(x):
|
|
301
|
+
density = kde_fct(events_x=xs, events_y=ys,
|
|
302
|
+
xout=xmesh, yout=ymesh,
|
|
303
|
+
**kde_kwargs)
|
|
304
|
+
else:
|
|
305
|
+
density = np.array([])
|
|
306
|
+
|
|
307
|
+
# Convert mesh back to linear scale if applicable
|
|
308
|
+
if xscale == "log":
|
|
309
|
+
xmesh = np.exp(xmesh)
|
|
310
|
+
if yscale == "log":
|
|
311
|
+
ymesh = np.exp(ymesh)
|
|
312
|
+
|
|
313
|
+
return xmesh, ymesh, density
|
|
314
|
+
|
|
315
|
+
def get_scatter(self, xax="area_um", yax="deform", positions=None,
|
|
316
|
+
kde_type="histogram", kde_kwargs=None, xscale="linear",
|
|
317
|
+
yscale="linear"):
|
|
318
|
+
"""Evaluate the kernel density estimate for scatter plots
|
|
319
|
+
|
|
320
|
+
The KDE is evaluated with the `kde_type` function for every point.
|
|
321
|
+
|
|
322
|
+
Parameters
|
|
323
|
+
----------
|
|
324
|
+
xax: str
|
|
325
|
+
Identifier for X axis (e.g. "area_um", "aspect", "deform")
|
|
326
|
+
yax: str
|
|
327
|
+
Identifier for Y axis
|
|
328
|
+
positions: list of two 1d ndarrays or ndarray of shape (2, N)
|
|
329
|
+
The positions where the KDE will be computed. Note that
|
|
330
|
+
the KDE estimate is computed from the points that
|
|
331
|
+
are set in `self.rtdc_ds.filter.all`.
|
|
332
|
+
kde_type: str
|
|
333
|
+
The KDE method to use, see :const:`.kde_methods.methods`
|
|
334
|
+
kde_kwargs: dict
|
|
335
|
+
Additional keyword arguments to the KDE method
|
|
336
|
+
xscale: str
|
|
337
|
+
If set to "log", take the logarithm of the x-values before
|
|
338
|
+
computing the KDE. This is useful when data are
|
|
339
|
+
displayed on a log-scale. Defaults to "linear".
|
|
340
|
+
yscale: str
|
|
341
|
+
See `xscale`.
|
|
342
|
+
|
|
343
|
+
Returns
|
|
344
|
+
-------
|
|
345
|
+
density : 1d ndarray
|
|
346
|
+
The kernel density evaluated for the filtered data points.
|
|
347
|
+
"""
|
|
348
|
+
if kde_kwargs is None:
|
|
349
|
+
kde_kwargs = {}
|
|
350
|
+
xax = xax.lower()
|
|
351
|
+
yax = yax.lower()
|
|
352
|
+
kde_type = kde_type.lower()
|
|
353
|
+
if kde_type not in methods:
|
|
354
|
+
raise ValueError(f"Not a valid kde type: {kde_type}!")
|
|
355
|
+
|
|
356
|
+
# Get data
|
|
357
|
+
x = self.rtdc_ds[xax][self.rtdc_ds.filter.all]
|
|
358
|
+
y = self.rtdc_ds[yax][self.rtdc_ds.filter.all]
|
|
359
|
+
|
|
360
|
+
# Apply scale (no change for linear scale)
|
|
361
|
+
xs = self.apply_scale(x, xscale, xax)
|
|
362
|
+
ys = self.apply_scale(y, yscale, yax)
|
|
363
|
+
|
|
364
|
+
if positions is None:
|
|
365
|
+
posx = None
|
|
366
|
+
posy = None
|
|
367
|
+
else:
|
|
368
|
+
posx = self.apply_scale(positions[0], xscale, xax)
|
|
369
|
+
posy = self.apply_scale(positions[1], yscale, yax)
|
|
370
|
+
|
|
371
|
+
kde_fct = methods[kde_type]
|
|
372
|
+
if len(x):
|
|
373
|
+
density = kde_fct(events_x=xs, events_y=ys,
|
|
374
|
+
xout=posx, yout=posy,
|
|
375
|
+
**kde_kwargs)
|
|
376
|
+
else:
|
|
377
|
+
density = np.array([])
|
|
378
|
+
|
|
379
|
+
return density
|
|
380
|
+
|
|
381
|
+
def get_at(self, xax="area_um", yax="deform", positions=None,
|
|
382
|
+
kde_type="histogram", kde_kwargs=None, xscale="linear",
|
|
383
|
+
yscale="linear"):
|
|
384
|
+
"""Evaluate the kernel density estimate for specific events
|
|
385
|
+
|
|
386
|
+
The KDE is computed via linear interpolation from the output
|
|
387
|
+
of `get_raster`.
|
|
388
|
+
|
|
389
|
+
Parameters
|
|
390
|
+
----------
|
|
391
|
+
xax: str
|
|
392
|
+
Identifier for X axis (e.g. "area_um", "aspect", "deform")
|
|
393
|
+
yax: str
|
|
394
|
+
Identifier for Y axis
|
|
395
|
+
positions: list of two 1d ndarrays or ndarray of shape (2, N)
|
|
396
|
+
The positions where the KDE will be computed. Note that
|
|
397
|
+
the KDE estimate is computed from the points that
|
|
398
|
+
are set in `self.rtdc_ds.filter.all`.
|
|
399
|
+
kde_type: str
|
|
400
|
+
The KDE method to use, see :const:`.kde_methods.methods`
|
|
401
|
+
kde_kwargs: dict
|
|
402
|
+
Additional keyword arguments to the KDE method
|
|
403
|
+
xscale: str
|
|
404
|
+
If set to "log", take the logarithm of the x-values before
|
|
405
|
+
computing the KDE. This is useful when data are
|
|
406
|
+
displayed on a log-scale. Defaults to "linear".
|
|
407
|
+
yscale: str
|
|
408
|
+
See `xscale`.
|
|
409
|
+
|
|
410
|
+
Returns
|
|
411
|
+
-------
|
|
412
|
+
density : 1d ndarray
|
|
413
|
+
The kernel density evaluated for the filtered events.
|
|
414
|
+
"""
|
|
415
|
+
if kde_kwargs is None:
|
|
416
|
+
kde_kwargs = {}
|
|
417
|
+
xax = xax.lower()
|
|
418
|
+
yax = yax.lower()
|
|
419
|
+
kde_type = kde_type.lower()
|
|
420
|
+
if kde_type not in methods:
|
|
421
|
+
raise ValueError(f"Not a valid kde type: {kde_type}!")
|
|
422
|
+
|
|
423
|
+
# Get data
|
|
424
|
+
x = self.rtdc_ds[xax][self.rtdc_ds.filter.all]
|
|
425
|
+
y = self.rtdc_ds[yax][self.rtdc_ds.filter.all]
|
|
426
|
+
|
|
427
|
+
# Apply scale (no change for linear scale)
|
|
428
|
+
xs = self.apply_scale(x, xscale, xax)
|
|
429
|
+
ys = self.apply_scale(y, yscale, yax)
|
|
430
|
+
|
|
431
|
+
if positions:
|
|
432
|
+
xs = self.apply_scale(positions[0], xscale, xax)
|
|
433
|
+
ys = self.apply_scale(positions[1], yscale, yax)
|
|
434
|
+
|
|
435
|
+
if len(x):
|
|
436
|
+
xr, yr, density_grid = self.get_raster(xax=xax,
|
|
437
|
+
yax=yax,
|
|
438
|
+
kde_type=kde_type,
|
|
439
|
+
kde_kwargs=kde_kwargs,
|
|
440
|
+
xscale=xscale,
|
|
441
|
+
yscale=yscale)
|
|
442
|
+
|
|
443
|
+
# Apply scale (no change for linear scale)
|
|
444
|
+
xrs = self.apply_scale(xr, xscale, xax)
|
|
445
|
+
yrs = self.apply_scale(yr, yscale, yax)
|
|
446
|
+
|
|
447
|
+
# 'scipy.interp2d' has been removed in SciPy 1.14.0
|
|
448
|
+
# https://scipy.github.io/devdocs/tutorial/interpolate/interp_transition_guide.html
|
|
449
|
+
interp_func = RGI((xrs[:, 0], yrs[0, :]),
|
|
450
|
+
density_grid,
|
|
451
|
+
method="linear",
|
|
452
|
+
bounds_error=False,
|
|
453
|
+
fill_value=np.nan)
|
|
454
|
+
density = interp_func((xs, ys))
|
|
455
|
+
|
|
456
|
+
else:
|
|
457
|
+
density = np.array([])
|
|
458
|
+
|
|
459
|
+
return density
|
dclab/kde/contours.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from ..external.skimage.measure import find_contours, points_in_poly
|
|
5
|
+
import scipy.interpolate as spint
|
|
6
|
+
|
|
7
|
+
from .methods import get_bad_vals
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find_contours_level(density, x, y, level, closed=False):
|
|
11
|
+
"""Find iso-valued density contours for a given level value
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
density: 2d ndarray of shape (M, N)
|
|
16
|
+
Kernel density estimate (KDE) for which to compute the contours
|
|
17
|
+
x: 2d ndarray of shape (M, N) or 1d ndarray of size M
|
|
18
|
+
X-values corresponding to `density`
|
|
19
|
+
y: 2d ndarray of shape (M, N) or 1d ndarray of size M
|
|
20
|
+
Y-values corresponding to `density`
|
|
21
|
+
level: float between 0 and 1
|
|
22
|
+
Value along which to find contours in `density` relative
|
|
23
|
+
to its maximum
|
|
24
|
+
closed: bool
|
|
25
|
+
Whether to close contours at the KDE support boundaries
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
contours: list of ndarrays of shape (P, 2)
|
|
30
|
+
Contours found for the given level value
|
|
31
|
+
|
|
32
|
+
See Also
|
|
33
|
+
--------
|
|
34
|
+
skimage.measure.find_contours: Contour finding algorithm used
|
|
35
|
+
"""
|
|
36
|
+
if level >= 1 or level <= 0:
|
|
37
|
+
raise ValueError("`level` must be in (0,1), got '{}'!".format(level))
|
|
38
|
+
# level relative to maximum
|
|
39
|
+
level = level * density.max()
|
|
40
|
+
# xy coordinates
|
|
41
|
+
if len(x.shape) == 2:
|
|
42
|
+
assert np.all(x[:, 0] == x[:, 1])
|
|
43
|
+
x = x[:, 0]
|
|
44
|
+
if len(y.shape) == 2:
|
|
45
|
+
assert np.all(y[0, :] == y[1, :])
|
|
46
|
+
y = y[0, :]
|
|
47
|
+
if closed:
|
|
48
|
+
# find closed contours
|
|
49
|
+
density = np.pad(density, ((1, 1), (1, 1)), mode="constant")
|
|
50
|
+
offset = 1
|
|
51
|
+
else:
|
|
52
|
+
# leave contours open at kde boundary
|
|
53
|
+
offset = 0
|
|
54
|
+
|
|
55
|
+
conts_idx = find_contours(density, level)
|
|
56
|
+
conts_xy = []
|
|
57
|
+
|
|
58
|
+
for cc in conts_idx:
|
|
59
|
+
cx = np.interp(x=cc[:, 0]-offset,
|
|
60
|
+
xp=range(x.size),
|
|
61
|
+
fp=x)
|
|
62
|
+
cy = np.interp(x=cc[:, 1]-offset,
|
|
63
|
+
xp=range(y.size),
|
|
64
|
+
fp=y)
|
|
65
|
+
conts_xy.append(np.stack((cx, cy), axis=1))
|
|
66
|
+
|
|
67
|
+
return conts_xy
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_quantile_levels(density, x, y, xp, yp, q, normalize=True):
|
|
71
|
+
"""Compute density levels for given quantiles by interpolation
|
|
72
|
+
|
|
73
|
+
For a given 2D density, compute the density levels at which
|
|
74
|
+
the resulting contours contain the fraction `1-q` of all
|
|
75
|
+
data points. E.g. for a measurement of 1000 events, all
|
|
76
|
+
contours at the level corresponding to a quantile of
|
|
77
|
+
`q=0.95` (95th percentile) contain 50 events (5%).
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
density: 2d ndarray of shape (M, N)
|
|
82
|
+
Kernel density estimate for which to compute the contours
|
|
83
|
+
x: 2d ndarray of shape (M, N) or 1d ndarray of size M
|
|
84
|
+
X-values corresponding to `density`
|
|
85
|
+
y: 2d ndarray of shape (M, N) or 1d ndarray of size M
|
|
86
|
+
Y-values corresponding to `density`
|
|
87
|
+
xp: 1d ndarray of size D
|
|
88
|
+
Event x-data from which to compute the quantile
|
|
89
|
+
yp: 1d ndarray of size D
|
|
90
|
+
Event y-data from which to compute the quantile
|
|
91
|
+
q: array_like or float between 0 and 1
|
|
92
|
+
Quantile along which to find contours in `density` relative
|
|
93
|
+
to its maximum
|
|
94
|
+
normalize: bool
|
|
95
|
+
Whether output levels should be normalized to the maximum
|
|
96
|
+
of `density`
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
level: np.ndarray or float
|
|
101
|
+
Contours level(s) corresponding to the given quantile
|
|
102
|
+
|
|
103
|
+
Notes
|
|
104
|
+
-----
|
|
105
|
+
NaN-values events in `xp` and `yp` are ignored.
|
|
106
|
+
"""
|
|
107
|
+
# xy coordinates
|
|
108
|
+
if len(x.shape) == 2:
|
|
109
|
+
assert np.all(x[:, 0] == x[:, 1])
|
|
110
|
+
x = x[:, 0]
|
|
111
|
+
if len(y.shape) == 2:
|
|
112
|
+
assert np.all(y[0, :] == y[1, :])
|
|
113
|
+
y = y[0, :]
|
|
114
|
+
|
|
115
|
+
# remove bad events
|
|
116
|
+
bad = get_bad_vals(xp, yp)
|
|
117
|
+
xp = xp[~bad]
|
|
118
|
+
yp = yp[~bad]
|
|
119
|
+
|
|
120
|
+
# Normalize interpolation data such that the spacing for
|
|
121
|
+
# x and y is about the same during interpolation.
|
|
122
|
+
x_norm = x.max()
|
|
123
|
+
x = x / x_norm
|
|
124
|
+
xp = xp / x_norm
|
|
125
|
+
|
|
126
|
+
y_norm = y.max()
|
|
127
|
+
y = y / y_norm
|
|
128
|
+
yp = yp / y_norm
|
|
129
|
+
|
|
130
|
+
# Perform interpolation
|
|
131
|
+
dp = spint.interpn((x, y), density,
|
|
132
|
+
(xp, yp),
|
|
133
|
+
method='linear',
|
|
134
|
+
bounds_error=False,
|
|
135
|
+
fill_value=0)
|
|
136
|
+
|
|
137
|
+
if normalize:
|
|
138
|
+
dp /= density.max()
|
|
139
|
+
|
|
140
|
+
if not np.isscalar(q):
|
|
141
|
+
q = np.array(q)
|
|
142
|
+
plev = np.nanpercentile(dp, q=q*100)
|
|
143
|
+
return plev
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _find_quantile_level(density, x, y, xp, yp, quantile, acc=.01,
|
|
147
|
+
ret_err=False):
|
|
148
|
+
"""Find density level for a given data quantile by iteration
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
density: 2d ndarray of shape (M, N)
|
|
153
|
+
Kernel density estimate for which to compute the contours
|
|
154
|
+
x: 2d ndarray of shape (M, N) or 1d ndarray of size M
|
|
155
|
+
X-values corresponding to `density`
|
|
156
|
+
y: 2d ndarray of shape (M, N) or 1d ndarray of size M
|
|
157
|
+
Y-values corresponding to `density`
|
|
158
|
+
xp: 1d ndarray of size D
|
|
159
|
+
Event x-data from which to compute the quantile
|
|
160
|
+
yp: 1d ndarray of size D
|
|
161
|
+
Event y-data from which to compute the quantile
|
|
162
|
+
quantile: float between 0 and 1
|
|
163
|
+
Quantile along which to find contours in `density` relative
|
|
164
|
+
to its maximum
|
|
165
|
+
acc: float
|
|
166
|
+
Desired absolute accuracy (stopping criterion) of the
|
|
167
|
+
contours
|
|
168
|
+
ret_err: bool
|
|
169
|
+
If True, also return the absolute error
|
|
170
|
+
|
|
171
|
+
Returns
|
|
172
|
+
-------
|
|
173
|
+
level: float
|
|
174
|
+
Contours level corresponding to the given quantile
|
|
175
|
+
|
|
176
|
+
Notes
|
|
177
|
+
-----
|
|
178
|
+
A much more faster method (using interpolation) is implemented in
|
|
179
|
+
:func:`get_quantile_levels`.
|
|
180
|
+
NaN-values events in `xp` and `yp` are ignored.
|
|
181
|
+
|
|
182
|
+
See Also
|
|
183
|
+
--------
|
|
184
|
+
skimage.measure.find_contours: Contour finding algorithm
|
|
185
|
+
"""
|
|
186
|
+
if quantile >= 1 or quantile <= 0:
|
|
187
|
+
raise ValueError("Invalid value for `quantile`: {}".format(quantile))
|
|
188
|
+
|
|
189
|
+
# remove bad events
|
|
190
|
+
bad = get_bad_vals(xp, yp)
|
|
191
|
+
xp = xp[~bad]
|
|
192
|
+
yp = yp[~bad]
|
|
193
|
+
points = np.concatenate((xp.reshape(-1, 1), yp.reshape(-1, 1)), axis=1)
|
|
194
|
+
|
|
195
|
+
# initial guess
|
|
196
|
+
level = quantile
|
|
197
|
+
# error of current iteration
|
|
198
|
+
err = 1
|
|
199
|
+
# iteration factor (guarantees convergence)
|
|
200
|
+
itfac = 1
|
|
201
|
+
# total number of events
|
|
202
|
+
nev = xp.size
|
|
203
|
+
|
|
204
|
+
while np.abs(err) > acc:
|
|
205
|
+
# compute contours
|
|
206
|
+
conts = find_contours_level(density, x, y, level, closed=True)
|
|
207
|
+
# compute number of points in contour
|
|
208
|
+
isin = 0
|
|
209
|
+
pi = np.array(points, copy=True)
|
|
210
|
+
for cc in conts:
|
|
211
|
+
pinc = points_in_poly(points=pi, verts=cc)
|
|
212
|
+
isin += np.sum(pinc)
|
|
213
|
+
# ignore these points for the other contours
|
|
214
|
+
pi = pi[~pinc]
|
|
215
|
+
err = quantile - (nev - isin) / nev
|
|
216
|
+
level += err * itfac
|
|
217
|
+
itfac *= .9
|
|
218
|
+
|
|
219
|
+
if ret_err:
|
|
220
|
+
return level, err
|
|
221
|
+
else:
|
|
222
|
+
return level
|