diff --git a/holoviews/operation/datashader.py b/holoviews/operation/datashader.py index c54d6ef038..52619d18af 100644 --- a/holoviews/operation/datashader.py +++ b/holoviews/operation/datashader.py @@ -346,9 +346,14 @@ def _aggregate_ndoverlay(self, element, agg_fn): x, y = element.last.dimensions()[0:2] info = self._get_sampling(element, x, y) (x_range, y_range), (xs, ys), (width, height), (xtype, ytype) = info + if xtype == 'datetime': + x_range = tuple((np.array(x_range)/10e5).astype('datetime64[us]')) + if ytype == 'datetime': + y_range = tuple((np.array(y_range)/10e5).astype('datetime64[us]')) agg_params = dict({k: v for k, v in dict(self.get_param_values(), **self.p).items() if k in aggregate.params()}, x_range=x_range, y_range=y_range) + bbox = BoundingBox(points=[(x_range[0], y_range[0]), (x_range[1], y_range[1])]) # Optimize categorical counts by aggregating them individually if isinstance(agg_fn, ds.count_cat): @@ -359,7 +364,11 @@ def _aggregate_ndoverlay(self, element, agg_fn): else: grouped = element.groupby([agg_fn.column], container_type=NdOverlay, group_type=NdOverlay) - return grouped.clone({k: agg_fn1(v) for k, v in grouped.items()}) + groups = [] + for k, v in grouped.items(): + agg = agg_fn1(v) + groups.append((k, agg.clone(agg.data, bounds=bbox))) + return grouped.clone(groups) # Create aggregate instance for sum, count operations, breaking mean # into two aggregates @@ -402,7 +411,8 @@ def _aggregate_ndoverlay(self, element, agg_fn): # Fill masked with with NaNs if is_sum: agg.data[column].values[mask] = np.NaN - return agg + + return agg.clone(bounds=bbox) def _process(self, element, key=None): @@ -410,7 +420,8 @@ def _process(self, element, key=None): category = agg_fn.column if isinstance(agg_fn, ds.count_cat) else None if (isinstance(element, NdOverlay) and - ((isinstance(agg_fn, (ds.count, ds.sum, ds.mean)) and agg_fn.column not in element.kdims) or + ((isinstance(agg_fn, (ds.count, ds.sum, ds.mean)) and + (agg_fn.column is None or agg_fn.column not in element.kdims)) or (isinstance(agg_fn, ds.count_cat) and agg_fn.column in element.kdims))): return self._aggregate_ndoverlay(element, agg_fn) @@ -418,6 +429,7 @@ def _process(self, element, key=None): x, y, data, glyph = self._precomputed[element._plot_id] else: x, y, data, glyph = self.get_agg_data(element, category) + if self.p.precompute: self._precomputed[element._plot_id] = x, y, data, glyph (x_range, y_range), (xs, ys), (width, height), (xtype, ytype) = self._get_sampling(element, x, y) diff --git a/tests/operation/testdatashader.py b/tests/operation/testdatashader.py index 08f856d7c7..beb7ea8fc2 100644 --- a/tests/operation/testdatashader.py +++ b/tests/operation/testdatashader.py @@ -91,6 +91,26 @@ def test_aggregate_curve_datetimes_microsecond_timebase(self): datatype=['xarray'], bounds=bounds, vdims='Count') self.assertEqual(img, expected) + def test_aggregate_ndoverlay_count_cat_datetimes_microsecond_timebase(self): + dates = pd.date_range(start="2016-01-01", end="2016-01-03", freq='1D') + xstart = np.datetime64('2015-12-31T23:59:59.723518000', 'us') + xend = np.datetime64('2016-01-03T00:00:00.276482000', 'us') + curve = Curve((dates, [1, 2, 3])) + curve2 = Curve((dates, [3, 2, 1])) + ndoverlay = NdOverlay({0: curve, 1: curve2}, 'Cat') + imgs = aggregate(ndoverlay, aggregator=ds.count_cat('Cat'), width=2, height=2, + x_range=(xstart, xend), dynamic=False) + bounds = (np.datetime64('2015-12-31T23:59:59.723518'), 1.0, + np.datetime64('2016-01-03T00:00:00.276482'), 3.0) + dates = [np.datetime64('2016-01-01T11:59:59.861759000',), + np.datetime64('2016-01-02T12:00:00.138241000')] + expected = Image((dates, [1.5, 2.5], [[1, 0], [0, 2]]), + datatype=['xarray'], bounds=bounds, vdims='Count') + expected2 = Image((dates, [1.5, 2.5], [[0, 1], [1, 1]]), + datatype=['xarray'], bounds=bounds, vdims='Count') + self.assertEqual(imgs[0], expected) + self.assertEqual(imgs[1], expected2) + def test_aggregate_ndoverlay(self): ds = Dataset([(0.2, 0.3, 0), (0.4, 0.7, 1), (0, 0.99, 2)], kdims=['x', 'y', 'z']) ndoverlay = ds.to(Points, ['x', 'y'], [], 'z').overlay()