dclab 0.67.0__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.
@@ -1,47 +1,60 @@
1
1
  """Scale conversion applicable to a linear elastic model"""
2
+ from __future__ import annotations
2
3
 
3
4
  import warnings
4
5
 
5
6
  import numpy as np
6
-
7
-
8
- def convert(area_um, deform, channel_width_in, channel_width_out,
9
- emodulus=None, flow_rate_in=None, flow_rate_out=None,
10
- viscosity_in=None, viscosity_out=None, inplace=False):
11
- """convert area-deformation-emodulus triplet
7
+ import numpy.typing as npt
8
+
9
+
10
+ def convert(area_um: npt.NDArray,
11
+ deform: npt.NDArray,
12
+ channel_width_in: float,
13
+ channel_width_out: float,
14
+ emodulus: npt.NDArray = None,
15
+ flow_rate_in: float = None,
16
+ flow_rate_out: float = None,
17
+ viscosity_in: float = None,
18
+ viscosity_out: float | npt.NDArray = None,
19
+ inplace: bool = False
20
+ ) -> (
21
+ tuple[npt.NDArray, npt.NDArray] |
22
+ tuple[npt.NDArray, npt.NDArray, npt.NDArray]
23
+ ):
24
+ """Convert area-deformation-emodulus triplet
12
25
 
13
26
  The conversion formula is described in :cite:`Mietke2015`.
14
27
 
15
28
  Parameters
16
29
  ----------
17
- area_um: ndarray
30
+ area_um
18
31
  Convex cell area [µm²]
19
- deform: ndarray
32
+ deform
20
33
  Deformation
21
- channel_width_in: float
34
+ channel_width_in
22
35
  Original channel width [µm]
23
- channel_width_out: float
36
+ channel_width_out
24
37
  Target channel width [µm]
25
- emodulus: ndarray
38
+ emodulus
26
39
  Young's Modulus [kPa]
27
- flow_rate_in: float
40
+ flow_rate_in
28
41
  Original flow rate [µL/s]
29
- flow_rate_out: float
42
+ flow_rate_out
30
43
  Target flow rate [µL/s]
31
- viscosity_in: float
44
+ viscosity_in
32
45
  Original viscosity [mPa*s]
33
- viscosity_out: float or ndarray
46
+ viscosity_out
34
47
  Target viscosity [mPa*s]; This can be an array
35
- inplace: bool
48
+ inplace
36
49
  If True, override input arrays with corrected data
37
50
 
38
51
  Returns
39
52
  -------
40
- area_um_corr: ndarray
53
+ area_um_corr
41
54
  Corrected cell area [µm²]
42
- deform_corr: ndarray
55
+ deform_corr
43
56
  Deformation (a copy if `inplace` is False)
44
- emodulus_corr: ndarray
57
+ emodulus_corr
45
58
  Corrected emodulus [kPa]; only returned if `emodulus` is given.
46
59
 
47
60
  Notes
@@ -81,8 +94,11 @@ def convert(area_um, deform, channel_width_in, channel_width_out,
81
94
  return area_um_corr, deform_corr, emodulus_corr
82
95
 
83
96
 
84
- def scale_area_um(area_um, channel_width_in, channel_width_out, inplace=False,
85
- **kwargs):
97
+ def scale_area_um(area_um: npt.NDArray,
98
+ channel_width_in: float,
99
+ channel_width_out: float,
100
+ inplace: bool = False,
101
+ **kwargs) -> npt.NDArray:
86
102
  """Perform scale conversion for area_um (linear elastic model)
87
103
 
88
104
  The area scales with the characteristic length
@@ -94,20 +110,20 @@ def scale_area_um(area_um, channel_width_in, channel_width_out, inplace=False,
94
110
 
95
111
  Parameters
96
112
  ----------
97
- area_um: ndarray
113
+ area_um
98
114
  Convex area [µm²]
99
- channel_width_in: float
115
+ channel_width_in
100
116
  Original channel width [µm]
101
- channel_width_out: float
117
+ channel_width_out
102
118
  Target channel width [µm]
103
- inplace: bool
119
+ inplace
104
120
  If True, override input arrays with corrected data
105
- kwargs:
121
+ kwargs
106
122
  not used
107
123
 
108
124
  Returns
109
125
  -------
110
- area_um_corr: ndarray
126
+ area_um_corr
111
127
  Scaled area [µm²]
112
128
  """
113
129
  copy = not inplace
@@ -120,9 +136,14 @@ def scale_area_um(area_um, channel_width_in, channel_width_out, inplace=False,
120
136
  return area_um_corr
121
137
 
122
138
 
123
- def scale_emodulus(emodulus, channel_width_in, channel_width_out,
124
- flow_rate_in, flow_rate_out, viscosity_in,
125
- viscosity_out, inplace=False):
139
+ def scale_emodulus(emodulus: npt.NDArray,
140
+ channel_width_in: float,
141
+ channel_width_out: float,
142
+ flow_rate_in: float,
143
+ flow_rate_out: float,
144
+ viscosity_in: float,
145
+ viscosity_out: float | npt.NDArray,
146
+ inplace: bool = False) -> npt.NDArray:
126
147
  """Perform scale conversion for area_um (linear elastic model)
127
148
 
128
149
  The conversion formula is described in :cite:`Mietke2015`.
@@ -131,26 +152,26 @@ def scale_emodulus(emodulus, channel_width_in, channel_width_out,
131
152
 
132
153
  Parameters
133
154
  ----------
134
- emodulus: ndarray
155
+ emodulus
135
156
  Young's Modulus [kPa]
136
- channel_width_in: float
157
+ channel_width_in
137
158
  Original channel width [µm]
138
- channel_width_out: float
159
+ channel_width_out
139
160
  Target channel width [µm]
140
- flow_rate_in: float
161
+ flow_rate_in
141
162
  Original flow rate [µL/s]
142
- flow_rate_out: float
163
+ flow_rate_out
143
164
  Target flow rate [µL/s]
144
- viscosity_in: float
165
+ viscosity_in
145
166
  Original viscosity [mPa*s]
146
- viscosity_out: float or ndarray
167
+ viscosity_out
147
168
  Target viscosity [mPa*s]; This can be an array
148
- inplace: bool
169
+ inplace
149
170
  If True, override input arrays with corrected data
150
171
 
151
172
  Returns
152
173
  -------
153
- emodulus_corr: ndarray
174
+ emodulus_corr
154
175
  Scaled emodulus [kPa]
155
176
  """
156
177
  copy = not inplace
@@ -182,7 +203,10 @@ def scale_emodulus(emodulus, channel_width_in, channel_width_out,
182
203
  return emodulus_corr
183
204
 
184
205
 
185
- def scale_feature(feat, data, inplace=False, **scale_kw):
206
+ def scale_feature(feat: str,
207
+ data: float | npt.NDArray,
208
+ inplace: bool = False,
209
+ **scale_kw) -> npt.NDArray:
186
210
  """Convenience function for scale conversions (linear elastic model)
187
211
 
188
212
  This method wraps around all the other scale_* methods and also
@@ -190,13 +214,13 @@ def scale_feature(feat, data, inplace=False, **scale_kw):
190
214
 
191
215
  Parameters
192
216
  ----------
193
- feat: str
217
+ feat
194
218
  Valid scalar feature name
195
- data: float or ndarray
219
+ data
196
220
  Feature data
197
- inplace: bool
221
+ inplace
198
222
  If True, override input arrays with corrected data
199
- **scale_kw:
223
+ **scale_kw
200
224
  Scale keyword arguments for the wrapped methods
201
225
  """
202
226
  if feat == "area_um":
@@ -212,8 +236,11 @@ def scale_feature(feat, data, inplace=False, **scale_kw):
212
236
  raise KeyError("No recipe to scale feature '{}'!".format(feat))
213
237
 
214
238
 
215
- def scale_volume(volume, channel_width_in, channel_width_out, inplace=False,
216
- **kwargs):
239
+ def scale_volume(volume: npt.NDArray,
240
+ channel_width_in: float,
241
+ channel_width_out: float,
242
+ inplace: bool = False,
243
+ **kwargs) -> npt.NDArray:
217
244
  """Perform scale conversion for volume (linear elastic model)
218
245
 
219
246
  The volume scales with the characteristic length
@@ -223,20 +250,20 @@ def scale_volume(volume, channel_width_in, channel_width_out, inplace=False,
223
250
 
224
251
  Parameters
225
252
  ----------
226
- volume: ndarray
253
+ volume
227
254
  Volume [µm³]
228
- channel_width_in: float
255
+ channel_width_in
229
256
  Original channel width [µm]
230
- channel_width_out: float
257
+ channel_width_out
231
258
  Target channel width [µm]
232
- inplace: bool
259
+ inplace
233
260
  If True, override input arrays with corrected data
234
- kwargs:
261
+ kwargs
235
262
  not used
236
263
 
237
264
  Returns
238
265
  -------
239
- volume_corr: ndarray
266
+ volume_corr
240
267
  Scaled volume [µm³]
241
268
  """
242
269
  copy = not inplace
@@ -5,6 +5,7 @@ from typing import Literal
5
5
  import warnings
6
6
 
7
7
  import numpy as np
8
+ import numpy.typing as npt
8
9
 
9
10
  from ...warn import PipelineWarning
10
11
 
@@ -45,10 +46,17 @@ class TemperatureOutOfRangeWarning(PipelineWarning):
45
46
 
46
47
 
47
48
  def check_temperature(model: str,
48
- temperature: float | np.array,
49
+ temperature: float | npt.NDArray,
49
50
  tmin: float,
50
- tmax: float):
51
- """Raise a TemperatureOutOfRangeWarning if applicable"""
51
+ tmax: float) -> None:
52
+ """Raise a TemperatureOutOfRangeWarning if applicable
53
+
54
+ Raises
55
+ ------
56
+ TemperatureOutOfRangeWarning
57
+ If the given temperature is out of the given range.
58
+
59
+ """
52
60
  if np.min(temperature) < tmin or np.max(temperature) > tmax:
53
61
  warnings.warn(
54
62
  f"For the {model} model, the temperature should be "
@@ -60,11 +68,12 @@ def check_temperature(model: str,
60
68
  def get_viscosity(medium: str = "0.49% MC-PBS",
61
69
  channel_width: float = 20.0,
62
70
  flow_rate: float = 0.16,
63
- temperature: float | np.ndarray = 23.0,
71
+ temperature: float | npt.NDArray = 23.0,
64
72
  model: Literal['herold-2017',
65
73
  'herold-2017-fallback',
66
74
  'buyukurganci-2022',
67
- 'kestin-1978'] = 'herold-2017-fallback'):
75
+ 'kestin-1978'] = 'herold-2017-fallback'
76
+ ) -> float | npt.NDArray:
68
77
  """Returns the viscosity for RT-DC-specific media
69
78
 
70
79
  Media that are not pure (e.g. ketchup or polymer solutions)
@@ -83,16 +92,16 @@ def get_viscosity(medium: str = "0.49% MC-PBS",
83
92
 
84
93
  Parameters
85
94
  ----------
86
- medium: str
95
+ medium
87
96
  The medium to compute the viscosity for; Valid values
88
97
  are defined in :const:`KNOWN_MEDIA`.
89
- channel_width: float
98
+ channel_width
90
99
  The channel width in µm
91
- flow_rate: float
100
+ flow_rate
92
101
  Flow rate in µL/s
93
- temperature: float or ndarray
102
+ temperature
94
103
  Temperature in °C
95
- model: str
104
+ model
96
105
  The model name to use for computing the medium viscosity.
97
106
  For water, this value is ignored, as there is only the
98
107
  'kestin-1978' model :cite:`Kestin_1978`. For MC-PBS media,
@@ -101,7 +110,7 @@ def get_viscosity(medium: str = "0.49% MC-PBS",
101
110
 
102
111
  Returns
103
112
  -------
104
- viscosity: float or ndarray
113
+ viscosity
105
114
  Viscosity in mPa*s
106
115
 
107
116
  Notes
@@ -152,21 +161,23 @@ def get_viscosity(medium: str = "0.49% MC-PBS",
152
161
  return eta
153
162
 
154
163
 
155
- def shear_rate_square_channel(flow_rate, channel_width, flow_index):
164
+ def shear_rate_square_channel(flow_rate: float,
165
+ channel_width: float,
166
+ flow_index: float) -> float:
156
167
  """Returns The wall shear rate of a power law liquid in a squared channel.
157
168
 
158
169
  Parameters
159
170
  ----------
160
- flow_rate: float
171
+ flow_rate
161
172
  Flow rate in µL/s
162
- channel_width: float
173
+ channel_width
163
174
  The channel width in µm
164
- flow_index: float
175
+ flow_index
165
176
  Flow behavior index aka the power law exponent of the shear thinning
166
177
 
167
178
  Returns
168
179
  -------
169
- shear_rate: float
180
+ shear_rate
170
181
  Shear rate in 1/s.
171
182
  """
172
183
  # convert channel width to mm
@@ -180,7 +191,7 @@ def get_viscosity_mc_pbs_buyukurganci_2022(
180
191
  "0.83% MC-PBS"] = "0.49% MC-PBS",
181
192
  channel_width: float = 20.0,
182
193
  flow_rate: float = 0.16,
183
- temperature: float = 23.0):
194
+ temperature: float = 23.0) -> float | npt.NDArray:
184
195
  """Compute viscosity of MC-PBS according to :cite:`Buyukurganci2022`
185
196
 
186
197
  This viscosity model was derived in :cite:`Buyukurganci2022`
@@ -215,7 +226,7 @@ def get_viscosity_mc_pbs_herold_2017(
215
226
  medium: Literal["0.49% MC-PBS", "0.59% MC-PBS"] = "0.49% MC-PBS",
216
227
  channel_width: float = 20.0,
217
228
  flow_rate: float = 0.16,
218
- temperature: float = 23.0):
229
+ temperature: float = 23.0) -> float | npt.NDArray:
219
230
  r"""Compute viscosity of MC-PBS according to :cite:`Herold2017`
220
231
 
221
232
  Note that all the factors in equation 5.2 in :cite:`Herold2017`
@@ -246,7 +257,8 @@ def get_viscosity_mc_pbs_herold_2017(
246
257
  return eta
247
258
 
248
259
 
249
- def get_viscosity_water_kestin_1978(temperature: float = 23.0):
260
+ def get_viscosity_water_kestin_1978(
261
+ temperature: float | npt.NDArray = 23.0) -> float | npt.NDArray:
250
262
  """Compute the viscosity of water according to :cite:`Kestin_1978`"""
251
263
  # see equation (15) in Kestin et al, J. Phys. Chem. 7(3) 1978
252
264
  check_temperature("'kestin-1978' water", temperature, 0, 40)
@@ -1,9 +1,13 @@
1
1
  """Crosstalk-correction for fluorescence data"""
2
+ from __future__ import annotations
2
3
 
3
4
  import numpy as np
5
+ import numpy.typing as npt
4
6
 
5
7
 
6
- def get_compensation_matrix(ct21, ct31, ct12, ct32, ct13, ct23):
8
+ def get_compensation_matrix(
9
+ ct21: float, ct31: float, ct12: float,
10
+ ct32: float, ct13: float, ct23: float) -> npt.NDArray:
7
11
  """Compute crosstalk inversion matrix
8
12
 
9
13
  The spillover matrix is
@@ -18,12 +22,12 @@ def get_compensation_matrix(ct21, ct31, ct12, ct32, ct13, ct23):
18
22
 
19
23
  Parameters
20
24
  ----------
21
- cij: float
25
+ cij
22
26
  Spill from channel i to channel j
23
27
 
24
28
  Returns
25
29
  -------
26
- inv: np.ndarray
30
+ inv
27
31
  Compensation matrix (inverted spillover matrix)
28
32
  """
29
33
  ct11 = 1
@@ -55,18 +59,23 @@ def get_compensation_matrix(ct21, ct31, ct12, ct32, ct13, ct23):
55
59
  return np.linalg.inv(crosstalk)
56
60
 
57
61
 
58
- def correct_crosstalk(fl1, fl2, fl3, fl_channel,
59
- ct21=0, ct31=0, ct12=0, ct32=0, ct13=0, ct23=0):
62
+ def correct_crosstalk(
63
+ fl1: int | float | npt.NDArray,
64
+ fl2: int | float | npt.NDArray,
65
+ fl3: int | float | npt.NDArray,
66
+ fl_channel: int,
67
+ ct21: float = 0, ct31: float = 0, ct12: float = 0,
68
+ ct32: float = 0, ct13: float = 0, ct23: float = 0) -> npt.NDArray:
60
69
  """Perform crosstalk correction
61
70
 
62
71
  Parameters
63
72
  ----------
64
- fli: int, float, or np.ndarray
73
+ fli
65
74
  Measured fluorescence signals
66
- fl_channel: int (1, 2, or 3)
67
- The channel number for which the crosstalk-corrected signal
68
- should be computed
69
- cij: float
75
+ fl_channel
76
+ The channel number (1, 2, or 3) for which the crosstalk-corrected
77
+ signal should be computed
78
+ cij
70
79
  Spill (crosstalk or bleed-through) from channel i to channel j
71
80
  This spill is computed from the fluorescence signal of e.g.
72
81
  single-stained positive control cells; It is defined by the
@@ -1,11 +1,14 @@
1
1
  """Computation of inertia ratio from contour data"""
2
+ from __future__ import annotations
3
+
2
4
  import numpy as np
5
+ import numpy.typing as npt
3
6
  import scipy.spatial as ssp
4
7
 
5
8
 
6
- def cont_moments_cv(cont,
7
- flt_epsilon=1.19209e-07,
8
- dbl_epsilon=2.2204460492503131e-16):
9
+ def cont_moments_cv(cont: npt.NDArray,
10
+ flt_epsilon: float = 1.19209e-07,
11
+ dbl_epsilon: float = 2.2204460492503131e-16) -> dict:
9
12
  """Compute the moments of a contour
10
13
 
11
14
  The moments are computed in the same way as they are computed
@@ -13,11 +16,11 @@ def cont_moments_cv(cont,
13
16
 
14
17
  Parameters
15
18
  ----------
16
- cont: array of shape (N,2)
19
+ cont: ndarray of shape (N,2)
17
20
  The contour for which to compute the moments.
18
- flt_epsilon: float
21
+ flt_epsilon
19
22
  The value of ``FLT_EPSILON`` in OpenCV/gcc.
20
- dbl_epsilon: float
23
+ dbl_epsilon
21
24
  The value of ``DBL_EPSILON`` in OpenCV/gcc.
22
25
 
23
26
  .. versionchanged:: 0.48.2
@@ -28,7 +31,7 @@ def cont_moments_cv(cont,
28
31
 
29
32
  Returns
30
33
  -------
31
- moments: dict
34
+ moments
32
35
  A dictionary of moments. If the moment `m00` is smaller
33
36
  than half of `flt_epsilon`, `None` is returned.
34
37
  """
@@ -120,7 +123,8 @@ def cont_moments_cv(cont,
120
123
  return None
121
124
 
122
125
 
123
- def get_inert_ratio_cvx(cont):
126
+ def get_inert_ratio_cvx(
127
+ cont: npt.NDArray | list[npt.NDArray]) -> float | npt.NDArray:
124
128
  """Compute the inertia ratio of the convex hull of a contour
125
129
 
126
130
  The inertia ratio is computed from the central second order of moments
@@ -190,7 +194,8 @@ def get_inert_ratio_cvx(cont):
190
194
  return inert_ratio_cvx
191
195
 
192
196
 
193
- def get_inert_ratio_prnc(cont):
197
+ def get_inert_ratio_prnc(
198
+ cont: npt.NDArray | list[npt.NDArray]) -> float | npt.NDArray:
194
199
  """Compute principal inertia ratio of a contour
195
200
 
196
201
  The principal inertia ratio is rotation-invariant, which
@@ -256,7 +261,8 @@ def get_inert_ratio_prnc(cont):
256
261
  return inert_ratio_prnc
257
262
 
258
263
 
259
- def get_inert_ratio_raw(cont):
264
+ def get_inert_ratio_raw(
265
+ cont: npt.NDArray | list[npt.NDArray]) -> float | npt.NDArray:
260
266
  """Compute the inertia ratio of a contour
261
267
 
262
268
  The inertia ratio is computed from the central second order of moments
@@ -321,7 +327,8 @@ def get_inert_ratio_raw(cont):
321
327
  return inert_ratio_raw
322
328
 
323
329
 
324
- def get_tilt(cont):
330
+ def get_tilt(
331
+ cont: npt.NDArray | list[npt.NDArray]) -> float | npt.NDArray:
325
332
  """Compute tilt of raw contour relative to channel axis
326
333
 
327
334
  Parameters
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/kde/base.py CHANGED
@@ -288,21 +288,24 @@ class KernelDensityEstimator:
288
288
  xc = xs[~bad]
289
289
  yc = ys[~bad]
290
290
 
291
- xnum = int(np.ceil((xc.max() - xc.min()) / xacc))
292
- 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))
293
295
 
294
- xlin = np.linspace(xc.min(), xc.max(), xnum, endpoint=True)
295
- 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)
296
298
 
297
- xmesh, ymesh = np.meshgrid(xlin, ylin, indexing="ij")
299
+ xmesh, ymesh = np.meshgrid(xlin, ylin, indexing="ij")
298
300
 
299
- kde_fct = methods[kde_type]
300
- if len(x):
301
+ # Compute the KDE for each point on the mesh
302
+ kde_fct = methods[kde_type]
301
303
  density = kde_fct(events_x=xs, events_y=ys,
302
304
  xout=xmesh, yout=ymesh,
303
305
  **kde_kwargs)
304
306
  else:
305
- density = np.array([])
307
+ xmesh, ymesh = np.meshgrid([0, 1], [0, 1], indexing="ij")
308
+ density = np.array(np.nan * xmesh)
306
309
 
307
310
  # Convert mesh back to linear scale if applicable
308
311
  if xscale == "log":