|
27 | 27 | _COLORS = {"r": "tab:red", "g": "tab:green", "b": "tab:blue"}
|
28 | 28 |
|
29 | 29 |
|
30 |
| -def _get_bins(data: npt.NDArray[Any]) -> npt.NDArray[Any]: |
| 30 | +def _get_bins( |
| 31 | + data: npt.NDArray[Any], |
| 32 | + num_bins: int = 100, |
| 33 | + start: Optional[Union[int, float]] = None, |
| 34 | + stop: Optional[Union[int, float]] = None, |
| 35 | +) -> npt.NDArray[Any]: |
| 36 | + """Create evenly spaced bins with a given interval. |
| 37 | +
|
| 38 | + If `start` or `stop` are `None`, they will be set based on the minimum |
| 39 | + and maximum values, respectively, of the data. |
| 40 | +
|
| 41 | + Parameters |
| 42 | + ---------- |
| 43 | + data : napari.layers.Layer.data |
| 44 | + Napari layer data. |
| 45 | + num_bins : integer, optional |
| 46 | + Number of evenly-spaced bins to create. |
| 47 | + start : integer or real, optional |
| 48 | + Start bin edge. Defaults to the minimum value of `data`. |
| 49 | + stop : integer or real, optional |
| 50 | + Stop bin edge. Defaults to the maximum value of `data`. |
| 51 | +
|
| 52 | + Returns |
| 53 | + ------- |
| 54 | + bin_edges : numpy.ndarray |
| 55 | + Array of evenly spaced bin edges. |
| 56 | + """ |
| 57 | + start = np.min(data) if start is None else start |
| 58 | + stop = np.max(data) if stop is None else stop |
| 59 | + |
31 | 60 | if data.dtype.kind in {"i", "u"}:
|
32 | 61 | # Make sure integer data types have integer sized bins
|
33 |
| - step = np.ceil(np.ptp(data) / 100) |
34 |
| - return np.arange(np.min(data), np.max(data) + step, step) |
| 62 | + step = np.ceil((stop - start) / num_bins) |
| 63 | + return np.arange(start, stop + step, step) |
35 | 64 | else:
|
36 |
| - # For other data types, just have 100 evenly spaced bins |
37 |
| - # (and 101 bin edges) |
38 |
| - return np.linspace(np.min(data), np.max(data), 101) |
| 65 | + # For other data types we can use exactly `num_bins` bins |
| 66 | + # (and `num_bins` + 1 bin edges) |
| 67 | + return np.linspace(start, stop, num_bins + 1) |
39 | 68 |
|
40 | 69 |
|
41 | 70 | class HistogramWidget(SingleAxesWidget):
|
@@ -217,15 +246,12 @@ def draw(self) -> None:
|
217 | 246 |
|
218 | 247 | # Important to calculate bins after slicing 3D data, to avoid reading
|
219 | 248 | # whole cube into memory.
|
220 |
| - if data.dtype.kind in {"i", "u"}: |
221 |
| - # Make sure integer data types have integer sized bins |
222 |
| - step = abs(self.bins_stop - self.bins_start) // (self.bins_num) |
223 |
| - step = max(1, step) |
224 |
| - bins = np.arange(self.bins_start, self.bins_stop + step, step) |
225 |
| - else: |
226 |
| - bins = np.linspace( |
227 |
| - self.bins_start, self.bins_stop, self.bins_num + 1 |
228 |
| - ) |
| 249 | + bins = _get_bins( |
| 250 | + data, |
| 251 | + num_bins=self.bins_num, |
| 252 | + start=self.bins_start, |
| 253 | + stop=self.bins_stop, |
| 254 | + ) |
229 | 255 |
|
230 | 256 | if layer.rgb:
|
231 | 257 | # Histogram RGB channels independently
|
|
0 commit comments