diff --git a/doc/Tutorials/Columnar_Data.ipynb b/doc/Tutorials/Columnar_Data.ipynb index b048eee040..5c3b8b8f6e 100644 --- a/doc/Tutorials/Columnar_Data.ipynb +++ b/doc/Tutorials/Columnar_Data.ipynb @@ -853,7 +853,7 @@ }, "outputs": [], "source": [ - "%%opts Scatter [scaling_factor=1.4] (color=Palette('Set3') edgecolors='k')\n", + "%%opts Scatter [scaling_method='width' scaling_factor=2] (color=Palette('Set3') edgecolors='k')\n", "gdp_unem_scatter = macro.to.scatter('Year', ['GDP Growth', 'Unemployment'])\n", "gdp_unem_scatter.overlay('Country')" ] @@ -873,7 +873,7 @@ }, "outputs": [], "source": [ - "%%opts NdOverlay [legend_cols=2] Scatter [size_index=1 scaling_factor=1.3] (color=Palette('Blues'))\n", + "%%opts NdOverlay [legend_cols=2] Scatter [size_index=1] (color=Palette('Blues'))\n", "macro.to.scatter('GDP Growth', 'Unemployment', ['Year']).overlay()" ] }, diff --git a/doc/Tutorials/Elements.ipynb b/doc/Tutorials/Elements.ipynb index bca51af95b..8e1fe7733b 100644 --- a/doc/Tutorials/Elements.ipynb +++ b/doc/Tutorials/Elements.ipynb @@ -565,7 +565,7 @@ "source": [ "The ``Scatter`` object expresses a dependent relationship between *x* and *y*, making it useful for combining with other similar ``Chart`` types, while the ``Points`` object expresses the relationship of two independent keys *x* and *y* with optional ``vdims`` (zero in this case), which makes ``Points`` objects meaningful to combine with the ``Raster`` types below.\n", "\n", - "Of course, the ``vdims`` need not be empty for ``Points``; here is an example with two additional quantities for each point, as ``value_dimension``s *z* and α visualized as the color and size of the dots, respectively:" + "Of course, the ``vdims`` need not be empty for ``Points``; here is an example with two additional quantities for each point, as ``value_dimension``s *z* and α visualized as the color and size of the dots, respectively. The point sizes can be tweaked using the option `scaling_factor`, which determines the amount by which each point width or area is scaled, depending on the value of `scaling_method`." ] }, { @@ -576,7 +576,7 @@ }, "outputs": [], "source": [ - "%%opts Points [color_index=2 size_index=3 scaling_factor=50]\n", + "%%opts Points [color_index=2 size_index=3 scaling_method=\"width\" scaling_factor=10]\n", "np.random.seed(10)\n", "data = np.random.rand(100,4)\n", "\n", diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py index a4e485948e..b03f1d379d 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -13,22 +13,21 @@ class PointPlot(ElementPlot): - color_index = param.Integer(default=3, doc=""" + color_index = param.Integer(default=3, allow_None=True, doc=""" Index of the dimension from which the color will the drawn""") - size_index = param.Integer(default=2, doc=""" + size_index = param.Integer(default=2, allow_None=True, doc=""" Index of the dimension from which the sizes will the drawn.""") - radius_index = param.Integer(default=None, doc=""" - Index of the dimension from which the sizes will the drawn.""") + scaling_method = param.ObjectSelector(default="area", + objects=["width", "area"], + doc=""" + Determines whether the `scaling_factor` should be applied to + the width or area of each point (default: "area").""") scaling_factor = param.Number(default=1, bounds=(1, None), doc=""" - If values are supplied the area of the points is computed relative - to the marker size. It is then multiplied by scaling_factor to the power - of the ratio between the smallest point and all other points. - For values of 1 scaling by the values is disabled, a factor of 2 - allows for linear scaling of the area and a factor of 4 linear - scaling of the point width.""") + Scaling factor which is applied to either the width or area + of each point, depending on the value of `scaling_method`.""") size_fn = param.Callable(default=np.abs, doc=""" Function applied to size values before applying scaling, @@ -49,7 +48,7 @@ def get_data(self, element, ranges=None, empty=False): data = {} cmap = style.get('palette', style.get('cmap', None)) - if self.color_index < len(dims) and cmap: + if self.color_index is not None and self.color_index < len(dims) and cmap: map_key = 'color_' + dims[self.color_index] mapping['color'] = map_key if empty: @@ -59,7 +58,7 @@ def get_data(self, element, ranges=None, empty=False): colors = element.dimension_values(self.color_index) crange = ranges.get(dims[self.color_index], None) data[map_key] = map_colors(colors, crange, cmap) - if self.size_index < len(dims) and self.scaling_factor != 1: + if self.size_index is not None and self.size_index < len(dims): map_key = 'size_' + dims[self.size_index] mapping['size'] = map_key if empty: @@ -67,8 +66,9 @@ def get_data(self, element, ranges=None, empty=False): else: ms = style.get('size', 1) sizes = element.dimension_values(self.size_index) - data[map_key] = compute_sizes(sizes, self.size_fn, - self.scaling_factor, ms) + data[map_key] = np.sqrt(compute_sizes(sizes, self.size_fn, + self.scaling_factor, + self.scaling_method, ms)) data[dims[0]] = [] if empty else element.dimension_values(0) data[dims[1]] = [] if empty else element.dimension_values(1) diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py index 6fcb41c8ea..0b10ae8b95 100644 --- a/holoviews/plotting/mpl/chart.py +++ b/holoviews/plotting/mpl/chart.py @@ -542,19 +542,21 @@ class PointPlot(ChartPlot, ColorbarPlot): how point magnitudes are rendered to different colors. """ - color_index = param.Integer(default=3, doc=""" + color_index = param.Integer(default=3, allow_None=True, doc=""" Index of the dimension from which the color will the drawn""") - size_index = param.Integer(default=2, doc=""" + size_index = param.Integer(default=2, allow_None=True, doc=""" Index of the dimension from which the sizes will the drawn.""") - scaling_factor = param.Number(default=1, bounds=(1, None), doc=""" - If values are supplied the area of the points is computed relative - to the marker size. It is then multiplied by scaling_factor to the power - of the ratio between the smallest point and all other points. - For values of 1 scaling by the values is disabled, a factor of 2 - allows for linear scaling of the area and a factor of 4 linear - scaling of the point width.""") + scaling_method = param.ObjectSelector(default="area", + objects=["width", "area"], + doc=""" + Determines whether the `scaling_factor` should be applied to + the width or area of each point (default: "area").""") + + scaling_factor = param.Number(default=1, bounds=(0, None), doc=""" + Scaling factor which is applied to either the width or area + of each point, depending on the value of `scaling_method`.""") show_grid = param.Boolean(default=True, doc=""" Whether to draw grid lines at the tick positions.""") @@ -584,7 +586,7 @@ def initialize_plot(self, ranges=None): cs = points.dimension_values(self.color_index) style = self.style[self.cyclic_index] - if self.size_index < ndims and self.scaling_factor > 1: + if self.size_index is not None and self.size_index < ndims: style['s'] = self._compute_size(points, style) color = style.pop('color', None) @@ -609,7 +611,7 @@ def initialize_plot(self, ranges=None): def _compute_size(self, element, opts): sizes = element.dimension_values(self.size_index) ms = opts.pop('s') if 's' in opts else plt.rcParams['lines.markersize'] - return compute_sizes(sizes, self.size_fn, self.scaling_factor, ms) + return compute_sizes(sizes, self.size_fn, self.scaling_factor, self.scaling_method, ms) def update_handles(self, axis, element, key, ranges=None): @@ -617,10 +619,10 @@ def update_handles(self, axis, element, key, ranges=None): paths.set_offsets(element.array(dimensions=[0, 1])) dims = element.dimensions(label=True) ndims = len(dims) - if self.size_index < ndims: + if self.size_index is not None and self.size_index < ndims: opts = self.style[self.cyclic_index] paths.set_sizes(self._compute_size(element, opts)) - if self.color_index < ndims: + if self.color_index is not None and self.color_index < ndims: cs = element.dimension_values(self.color_index) val_dim = dims[self.color_index] paths.set_clim(ranges[val_dim]) diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py index 82f6aa1b5e..420f335592 100644 --- a/holoviews/plotting/util.py +++ b/holoviews/plotting/util.py @@ -1,3 +1,4 @@ +import math import param from ..core import (HoloMap, DynamicMap, CompositeOverlay, Layout, @@ -74,14 +75,22 @@ def undisplayable_info(obj, html=False): ['%s' % error, remedy, '%s' % info]))) -def compute_sizes(sizes, size_fn, scaling, base_size): +def compute_sizes(sizes, size_fn, scaling_factor, scaling_method, base_size): """ Scales point sizes according to a scaling factor, base size and size_fn, which will be applied before scaling. """ + if scaling_method == 'area': + pass + elif scaling_method == 'width': + scaling_factor = scaling_factor**2 + else: + raise ValueError( + 'Invalid value for argument "scaling_method": "{}". ' + 'Valid values are: "width", "area".'.format(scaling_method)) sizes = size_fn(sizes) - return (base_size*scaling**sizes) + return (base_size*scaling_factor*sizes) def get_sideplot_ranges(plot, element, main, ranges):