diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index b2f25a070c..51e6cda8c7 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -50,10 +50,11 @@ def shape(cls, dataset, gridded=False): if kd.name in array.dims][::-1] if not all(d in names for d in array.dims): array = np.squeeze(array) - try: - array = array.transpose(*names, transpose_coords=False) - except: - array = array.transpose(*names) # Handle old xarray + if len(names) > 1: + try: + array = array.transpose(*names, transpose_coords=False) + except: + array = array.transpose(*names) # Handle old xarray shape = array.shape if gridded: return shape @@ -218,6 +219,10 @@ def range(cls, dataset, dimension): da = dask_array_module() if da and isinstance(dmin, da.Array): dmin, dmax = da.compute(dmin, dmax) + if isinstance(dmin, np.ndarray) and dmin.shape == (): + dmin = dmin[()] + if isinstance(dmax, np.ndarray) and dmax.shape == (): + dmax = dmax[()] dmin = dmin if np.isscalar(dmin) or isinstance(dmin, util.datetime_types) else dmin.item() dmax = dmax if np.isscalar(dmax) or isinstance(dmax, util.datetime_types) else dmax.item() return dmin, dmax @@ -390,8 +395,10 @@ def ndloc(cls, dataset, indices): sampled = (all(isinstance(ind, np.ndarray) and ind.dtype.kind != 'b' for ind in adjusted_indices) and len(indices) == len(kdims)) if sampled or (all_scalar and len(indices) == len(kdims)): + import xarray as xr if all_scalar: isel = {k: [v] for k, v in isel.items()} - return dataset.data.isel_points(**isel).to_dataframe().reset_index() + selected = dataset.data.isel({k: xr.DataArray(v) for k, v in isel.items()}) + return selected.to_dataframe().reset_index() else: return dataset.data.isel(**isel) diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py index f72ded0ac7..f5966f4b81 100644 --- a/holoviews/plotting/util.py +++ b/holoviews/plotting/util.py @@ -522,6 +522,20 @@ def map_colors(arr, crange, cmap, hex=True): return arr +def resample_palette(palette, ncolors, categorical, cmap_categorical): + """ + Resample the number of colors in a palette to the selected number. + """ + if len(palette) != ncolors: + if categorical and cmap_categorical: + palette = [palette[i%len(palette)] for i in range(ncolors)] + else: + lpad, rpad = -0.5, 0.49999999999 + indexes = np.linspace(lpad, (len(palette)-1)+rpad, ncolors) + palette = [palette[int(np.round(v))] for v in indexes] + return palette + + def mplcmap_to_palette(cmap, ncolors=None, categorical=False): """ Converts a matplotlib colormap to palette of RGB hex strings." @@ -550,6 +564,22 @@ def mplcmap_to_palette(cmap, ncolors=None, categorical=False): return [rgb2hex(c) for c in cmap(np.linspace(0, 1, ncolors))] +def colorcet_cmap_to_palette(cmap, ncolors=None, categorical=False): + from colorcet import palette + + categories = ['glasbey'] + + ncolors = ncolors or 256 + cmap_categorical = any(c in cmap for c in categories) + + if cmap.endswith('_r'): + palette = list(reversed(palette[cmap[:-2]])) + else: + palette = palette[cmap] + + return resample_palette(palette, ncolors, categorical, cmap_categorical) + + def bokeh_palette_to_palette(cmap, ncolors=None, categorical=False): from bokeh import palettes @@ -589,14 +619,7 @@ def bokeh_palette_to_palette(cmap, ncolors=None, categorical=False): palette = palette(ncolors) if reverse: palette = palette[::-1] - if len(palette) != ncolors: - if categorical and cmap_categorical: - palette = [palette[i%len(palette)] for i in range(ncolors)] - else: - lpad, rpad = -0.5, 0.49999999999 - indexes = np.linspace(lpad, (len(palette)-1)+rpad, ncolors) - palette = [palette[int(np.round(v))] for v in indexes] - return palette + return resample_palette(palette, ncolors, categorical, cmap_categorical) def linear_gradient(start_hex, finish_hex, n=10): @@ -865,16 +888,12 @@ def process_cmap(cmap, ncolors=None, provider=None, categorical=False): mpl_cmaps = _list_cmaps('matplotlib') bk_cmaps = _list_cmaps('bokeh') cet_cmaps = _list_cmaps('colorcet') - if provider=='matplotlib' or (provider is None and (cmap in mpl_cmaps or cmap.lower() in mpl_cmaps)): + if provider == 'matplotlib' or (provider is None and (cmap in mpl_cmaps or cmap.lower() in mpl_cmaps)): palette = mplcmap_to_palette(cmap, ncolors, categorical) - elif provider=='bokeh' or (provider is None and (cmap in bk_cmaps or cmap.capitalize() in bk_cmaps)): + elif provider == 'bokeh' or (provider is None and (cmap in bk_cmaps or cmap.capitalize() in bk_cmaps)): palette = bokeh_palette_to_palette(cmap, ncolors, categorical) - elif provider=='colorcet' or (provider is None and cmap in cet_cmaps): - from colorcet import palette - if cmap.endswith('_r'): - palette = list(reversed(palette[cmap[:-2]])) - else: - palette = palette[cmap] + elif provider == 'colorcet' or (provider is None and cmap in cet_cmaps): + palette = colorcet_cmap_to_palette(cmap, ncolors, categorical) else: raise ValueError("Supplied cmap %s not found among %s colormaps." % (cmap,providers_checked)) diff --git a/holoviews/streams.py b/holoviews/streams.py index 24d8a64c14..39b2996916 100644 --- a/holoviews/streams.py +++ b/holoviews/streams.py @@ -338,10 +338,12 @@ def source(self): @source.setter def source(self, source): - if self.source: + if self.source is not None: source_list = self.registry[self.source] if self in source_list: source_list.remove(self) + if not source_list: + self.registry.pop(self.source) if source is None: self._source = None diff --git a/holoviews/tests/teststreams.py b/holoviews/tests/teststreams.py index 3107f985ae..113af287a7 100644 --- a/holoviews/tests/teststreams.py +++ b/holoviews/tests/teststreams.py @@ -627,6 +627,15 @@ def test_source_empty_element(self): stream = PointerX(source=points) self.assertIs(stream.source, points) + def test_source_empty_element_remap(self): + points = Points([]) + stream = PointerX(source=points) + self.assertIs(stream.source, points) + curve = Curve([]) + stream.source = curve + self.assertNotIn(points, Stream.registry) + self.assertIn(curve, Stream.registry) + def test_source_empty_dmap(self): points_dmap = DynamicMap(lambda x: Points([]), kdims=['X']) stream = PointerX(source=points_dmap) diff --git a/holoviews/util/__init__.py b/holoviews/util/__init__.py index 98e9a53c4b..d4f5eac895 100644 --- a/holoviews/util/__init__.py +++ b/holoviews/util/__init__.py @@ -921,6 +921,11 @@ def _eval_kwargs(self): for k, v in self.p.kwargs.items(): if util.is_param_method(v): v = v() + elif isinstance(v, FunctionType) and hasattr(v, '_dinfo'): + deps = v._dinfo + args = (getattr(p.owner, p.name) for p in deps.get('dependencies', [])) + kwargs = {k: getattr(p.owner, p.name) for k, p in deps.get('kw', {}).items()} + v = v(*args, **kwargs) evaled_kwargs[k] = v return evaled_kwargs