diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py index c0c0d5467d..de18337d96 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -227,238 +227,3 @@ def get_data(self, element, ranges=None, empty=False): err_xs.append((x, x)) err_ys.append((y - neg, y + pos)) return (dict(xs=err_xs, ys=err_ys), self._mapping) - - - -class SpikesPlot(PathPlot): - - color_index = param.Integer(default=1, doc=""" - Index of the dimension from which the color will the drawn""") - - spike_length = param.Number(default=0.5, doc=""" - The length of each spike if Spikes object is one dimensional.""") - - position = param.Number(default=0., doc=""" - The position of the lower end of each spike.""") - - style_opts = (['color', 'cmap', 'palette'] + line_properties) - - def get_extents(self, element, ranges): - l, b, r, t = super(SpikesPlot, self).get_extents(element, ranges) - if len(element.dimensions()) == 1: - b, t = self.position, self.position+self.spike_length - return l, b, r, t - - - def get_data(self, element, ranges=None, empty=False): - style = self.style[self.cyclic_index] - dims = element.dimensions(label=True) - - pos = self.position - if empty: - xs, ys, keys = [], [], [] - mapping = dict(xs=dims[0], ys=dims[1] if len(dims) > 1 else 'heights') - elif len(dims) > 1: - xs, ys = zip(*(((x, x), (pos, pos+y)) - for x, y in element.array())) - mapping = dict(xs=dims[0], ys=dims[1]) - keys = (dims[0], dims[1]) - else: - height = self.spike_length - xs, ys = zip(*(((x[0], x[0]), (pos, pos+height)) - for x in element.array())) - mapping = dict(xs=dims[0], ys='heights') - keys = (dims[0], 'heights') - - if not empty and self.invert_axes: keys = keys[::-1] - data = dict(zip(keys, (xs, ys))) - - cmap = style.get('palette', style.get('cmap', None)) - if self.color_index < len(dims) and cmap: - cdim = dims[self.color_index] - map_key = 'color_' + cdim - mapping['color'] = map_key - if empty: - colors = [] - else: - cmap = get_cmap(cmap) - cvals = element.dimension_values(cdim) - crange = ranges.get(cdim, None) - colors = map_colors(cvals, crange, cmap) - data[map_key] = colors - - return data, mapping - - - -class SideSpikesPlot(SpikesPlot): - """ - SpikesPlot with useful defaults for plotting adjoined rug plot. - """ - - xaxis = param.ObjectSelector(default='top-bare', - objects=['top', 'bottom', 'bare', 'top-bare', - 'bottom-bare', None], doc=""" - Whether and where to display the xaxis, bare options allow suppressing - all axis labels including ticks and xlabel. Valid options are 'top', - 'bottom', 'bare', 'top-bare' and 'bottom-bare'.""") - - yaxis = param.ObjectSelector(default='right-bare', - objects=['left', 'right', 'bare', 'left-bare', - 'right-bare', None], doc=""" - Whether and where to display the yaxis, bare options allow suppressing - all axis labels including ticks and ylabel. Valid options are 'left', - 'right', 'bare' 'left-bare' and 'right-bare'.""") - - height = param.Integer(default=80, doc="Height of plot") - - width = param.Integer(default=80, doc="Width of plot") - - - - - -class ChartPlot(ElementPlot): - - - def initialize_plot(self, ranges=None, plot=None, plots=None, source=None): - """ - Initializes a new plot object with the last available frame. - """ - # Get element key and ranges for frame - element = self.hmap.last - key = self.keys[-1] - ranges = self.compute_ranges(self.hmap, key, ranges) - ranges = match_spec(element, ranges) - self.current_ranges = ranges - self.current_frame = element - self.current_key = key - - # Initialize plot, source and glyph - if plot is not None: - raise Exception("Can't overlay Bokeh Charts based plot properties") - - init_element = element.clone(element.interface.concat(self.hmap.values())) - plot = self._init_chart(init_element, ranges) - self.handles['plot'] = plot - self.handles['glyph_renderers'] = [r for r in plot.renderers - if isinstance(r, GlyphRenderer)] - self._update_chart(element, ranges) - - # Update plot, source and glyph - self.drawn = True - - return plot - - def update_frame(self, key, ranges=None, plot=None, element=None): - """ - Updates an existing plot with data corresponding - to the key. - """ - element = self._get_frame(key) - if not element: - if self.dynamic and self.overlaid: - self.current_key = key - element = self.current_frame - else: - element = self._get_frame(key) - else: - self.current_key = key - self.current_frame = element - - self.set_param(**self.lookup_options(element, 'plot').options) - ranges = self.compute_ranges(self.hmap, key, ranges) - ranges = match_spec(element, ranges) - self.current_ranges = ranges - - self._update_chart(element, ranges) - - - def _update_chart(self, element, ranges): - new_chart = self._init_chart(element, ranges) - old_chart = self.handles['plot'] - old_renderers = old_chart.select(type=GlyphRenderer) - new_renderers = new_chart.select(type=GlyphRenderer) - - old_chart.y_range.update(**new_chart.y_range.properties_with_values()) - updated = [] - for new_r in new_renderers: - for old_r in old_renderers: - if type(old_r.glyph) == type(new_r.glyph): - old_renderers.pop(old_renderers.index(old_r)) - new_props = new_r.properties_with_values() - source = new_props.pop('data_source') - old_r.glyph.update(**new_r.glyph.properties_with_values()) - old_r.update(**new_props) - old_r.data_source.data.update(source.data) - updated.append(old_r) - break - - for old_r in old_renderers: - if old_r not in updated: - emptied = {k: [] for k in old_r.data_source.data} - old_r.data_source.data.update(emptied) - - - @property - def current_handles(self): - plot = self.handles['plot'] - sources = plot.select(type=ColumnDataSource) - return sources - - -class BoxPlot(ChartPlot): - """ - BoxPlot generates a box and whisker plot from a BoxWhisker - Element. This allows plotting the median, mean and various - percentiles. Displaying outliers is currently not supported - as they cannot be consistently updated. - """ - - def _init_chart(self, element, ranges): - plot = BokehBoxPlot(element.dframe(), - label=element.dimensions('key', True), - values=element.dimensions('value', True)[0]) - - # Disable outliers for now as they cannot be consistently updated. - plot.renderers = [r for r in plot.renderers - if not (isinstance(r, GlyphRenderer) and - isinstance(r.glyph, Circle))] - return plot - - -class BarPlot(ChartPlot): - """ - BoxPlot generates a box and whisker plot from a BoxWhisker - Element. This allows plotting the median, mean and various - percentiles. Displaying outliers is currently not supported - as they cannot be consistently updated. - """ - - group_index = param.Integer(default=0, doc=""" - Index of the dimension in the supplied Bars - Element, which will be laid out into groups.""") - - category_index = param.Integer(default=1, doc=""" - Index of the dimension in the supplied Bars - Element, which will be laid out into categories.""") - - stack_index = param.Integer(default=2, doc=""" - Index of the dimension in the supplied Bars - Element, which will stacked.""") - - def _init_chart(self, element, ranges): - kdims = element.dimensions('key', True) - vdim = element.dimensions('value', True)[0] - kwargs = {} - if self.group_index < element.ndims: - kwargs['group'] = kdims[self.group_index] - if self.category_index < element.ndims: - kwargs['label'] = kdims[self.category_index] - if self.stack_index < element.ndims: - kwargs['stack'] = kdims[self.stack_index] - crange = Range1d(*ranges.get(vdim)) - plot = Bar(element.dframe(), values=vdim, - continuous_range=crange, **kwargs) - return plot - diff --git a/holoviews/plotting/mpl/__init__.py b/holoviews/plotting/mpl/__init__.py index d6051cba1a..7b4edc6c7a 100644 --- a/holoviews/plotting/mpl/__init__.py +++ b/holoviews/plotting/mpl/__init__.py @@ -108,8 +108,6 @@ def grid_selector(grid): VectorField: VectorFieldPlot, ErrorBars: ErrorPlot, Spread: SpreadPlot, - BoxWhisker: BoxPlot, - Spikes: SpikesPlot, # General plots GridSpace: GridPlot, diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py index 2fd4202e6e..22c01d7956 100644 --- a/holoviews/plotting/mpl/chart.py +++ b/holoviews/plotting/mpl/chart.py @@ -976,161 +976,3 @@ def update_handles(self, axis, element, key, ranges=None): bar[0].set_height(height) bar[0].set_y(prev) prev += height if np.isfinite(height) else 0 - - - -class SpikesPlot(PathPlot): - - aspect = param.Parameter(default='square', doc=""" - The aspect ratio mode of the plot. Allows setting an - explicit aspect ratio as width/height as well as - 'square' and 'equal' options.""") - - color_index = param.Integer(default=1, doc=""" - Index of the dimension from which the color will the drawn""") - - spike_length = param.Number(default=0.1, doc=""" - The length of each spike if Spikes object is one dimensional.""") - - position = param.Number(default=0., doc=""" - The position of the lower end of each spike.""") - - style_opts = PathPlot.style_opts + ['cmap'] - - def initialize_plot(self, ranges=None): - lines = self.hmap.last - key = self.keys[-1] - - ranges = self.compute_ranges(self.hmap, key, ranges) - ranges = match_spec(lines, ranges) - style = self.style[self.cyclic_index] - label = lines.label if self.show_legend else '' - - data, array, clim = self.get_data(lines, ranges) - if array is not None: - style['array'] = array - style['clim'] = clim - - line_segments = LineCollection(data, label=label, - zorder=self.zorder, **style) - self.handles['artist'] = line_segments - self.handles['axis'].add_collection(line_segments) - - return self._finalize_axis(key, ranges=ranges) - - - def get_data(self, element, ranges): - dimensions = element.dimensions(label=True) - ndims = len(dimensions) - - pos = self.position - if ndims > 1: - data = [[(x, pos), (x, pos+y)] for x, y in element.array()] - else: - height = self.spike_length - data = [[(x[0], pos), (x[0], pos+height)] for x in element.array()] - - if self.invert_axes: - data = [(line[0][::-1], line[1][::-1]) for line in data] - - array, clim = None, None - if self.color_index < ndims: - cdim = dimensions[self.color_index] - array = element.dimension_values(cdim) - clime = ranges[cdim] - return data, array, clim - - - def update_handles(self, axis, element, key, ranges=None): - artist = self.handles['artist'] - data, array, clim = self.get_data(element, ranges) - artist.set_paths(data) - visible = self.style[self.cyclic_index].get('visible', True) - artist.set_visible(visible) - if array is not None: - artist.set_clim(clim) - artist.set_array(array) - - -class SideSpikesPlot(SpikesPlot): - - aspect = param.Parameter(default='auto', doc=""" - Aspect ratios on SideHistogramPlot should be determined by the - AdjointLayoutPlot.""") - - bgcolor = param.Parameter(default=(1, 1, 1, 0), doc=""" - Make plot background invisible.""") - - show_title = param.Boolean(default=False, doc=""" - Titles should be disabled on all SidePlots to avoid clutter.""") - - show_frame = param.Boolean(default=False) - - show_xlabel = param.Boolean(default=False, doc=""" - Whether to show the x-label of the plot. Disabled by default - because plots are often too cramped to fit the title correctly.""") - - xaxis = param.ObjectSelector(default='bare', - objects=['top', 'bottom', 'bare', 'top-bare', - 'bottom-bare', None], doc=""" - Whether and where to display the xaxis, bare options allow suppressing - all axis labels including ticks and xlabel. Valid options are 'top', - 'bottom', 'bare', 'top-bare' and 'bottom-bare'.""") - - yaxis = param.ObjectSelector(default='bare', - objects=['left', 'right', 'bare', 'left-bare', - 'right-bare', None], doc=""" - Whether and where to display the yaxis, bare options allow suppressing - all axis labels including ticks and ylabel. Valid options are 'left', - 'right', 'bare' 'left-bare' and 'right-bare'.""") - - - - -class BoxPlot(ChartPlot): - """ - BoxPlot plots the ErrorBar Element type and supporting - both horizontal and vertical error bars via the 'horizontal' - plot option. - """ - - style_opts = ['notch', 'sym', 'vert', 'whis', 'bootstrap', - 'conf_intervals', 'widths', 'showmeans', - 'show_caps', 'showfliers', 'boxprops', - 'whiskerprops', 'capprops', 'flierprops', - 'medianprops', 'meanprops', 'meanline'] - - - def initialize_plot(self, ranges=None): - element = self.hmap.last - axis = self.handles['axis'] - key = self.keys[-1] - - ranges = self.compute_ranges(self.hmap, key, ranges) - ranges = match_spec(element, ranges) - - xlabel = ','.join([str(d) for d in element.kdims]) - - self.handles['artist'] = self.get_artist(element, axis) - - return self._finalize_axis(self.keys[-1], ranges=ranges, xlabel=xlabel) - - def get_artist(self, element, axis): - dims = element.dimensions() - groups = element.groupby(element.kdims) - - data, labels = [], [] - for key, group in groups.data.items(): - label = ','.join([d.pprint_value(v) for d, v in zip(groups.kdims, key)]) - data.append(group[group.vdims[0]]) - labels.append(label) - boxplot = axis.boxplot(data, labels=labels, **self.style[self.cyclic_index]) - return boxplot - - - def update_handles(self, axis, element, key, ranges=None): - for k, group in self.handles['artist'].items(): - for v in group: - v.remove() - self.handles['artist'] = self.get_artist(element, axis) -