From 14ed2affa7e3cf028fa59ea38764d6b674daa8a6 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Thu, 31 Dec 2015 23:29:35 +0100 Subject: [PATCH 1/7] Decouple scaling method from scaling factor (as per issue #363). --- holoviews/plotting/bokeh/chart.py | 16 +++++++++------- holoviews/plotting/mpl/chart.py | 16 +++++++++------- holoviews/plotting/util.py | 13 +++++++++++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py index 8d6f9d6adb..ab4ccf20fd 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -21,13 +21,15 @@ class PointPlot(ElementPlot): 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, @@ -67,7 +69,7 @@ def get_data(self, element, ranges=None, empty=False): 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) + 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 d3bf86e5a4..f509b5f882 100644 --- a/holoviews/plotting/mpl/chart.py +++ b/holoviews/plotting/mpl/chart.py @@ -523,13 +523,15 @@ class PointPlot(ChartPlot, ColorbarPlot): size_index = param.Integer(default=2, 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`.""") 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): 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): diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py index 82f6aa1b5e..80366af302 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': + scaling_factor = math.sqrt(scaling_factor) + elif scaling_method == 'width': + pass # can use scaling factor unchanged in this case + 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): From 3ce7ec02929559cbda8a67fdcfedd83799483f33 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Sun, 3 Jan 2016 18:05:07 +0100 Subject: [PATCH 2/7] Explain new options scaling_method and scaling_factor in Elements tutorial. --- doc/Tutorials/Elements.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Tutorials/Elements.ipynb b/doc/Tutorials/Elements.ipynb index f30bf8b45a..524cf213a5 100644 --- a/doc/Tutorials/Elements.ipynb +++ b/doc/Tutorials/Elements.ipynb @@ -509,7 +509,7 @@ "source": [ "Thus 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`." ] }, { @@ -520,7 +520,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=50]\n", "np.random.seed(10)\n", "data = np.random.rand(100,4)\n", "\n", From a43ab2b7ed50e7ef8d8865f6bddc275590680a71 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Sun, 3 Jan 2016 18:34:18 +0100 Subject: [PATCH 3/7] Adjust scaling factors in pandas conversion tutorial. --- doc/Tutorials/Pandas_Conversion.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Tutorials/Pandas_Conversion.ipynb b/doc/Tutorials/Pandas_Conversion.ipynb index 2ce13d99f9..ff4094b11e 100644 --- a/doc/Tutorials/Pandas_Conversion.ipynb +++ b/doc/Tutorials/Pandas_Conversion.ipynb @@ -519,7 +519,7 @@ }, "outputs": [], "source": [ - "%%opts Scatter [scaling_factor=1.4] (color=Palette('Set3') edgecolors='k')\n", + "%%opts Scatter [scaling_factor=15.0] (color=Palette('Set3') edgecolors='k')\n", "gdp_unem_scatter = macro.scatter('year', ['GDP Growth', 'Unemployment'])\n", "gdp_unem_scatter.overlay('country')" ] @@ -539,7 +539,7 @@ }, "outputs": [], "source": [ - "%%opts Scatter [size_index=1 scaling_factor=1.3] (color=Palette('Dark2'))\n", + "%%opts Scatter [size_index=1 scaling_factor=3.0] (color=Palette('Dark2'))\n", "macro.scatter('GDP Growth', 'Unemployment').overlay('country')" ] }, @@ -565,7 +565,7 @@ }, "outputs": [], "source": [ - "%%opts Curve (color='k') Scatter [color_index=2 size_index=2 scaling_factor=1.4] (cmap='Blues' edgecolors='k')\n", + "%%opts Curve (color='k') Scatter [color_index=2 size_index=2 scaling_factor=10.0] (cmap='Blues' edgecolors='k')\n", "macro_overlay = gdp_curves * gdp_unem_scatter\n", "annotations = hv.Arrow(1973, 8, 'Oil Crisis', 'v') * hv.Arrow(1975, 6, 'Stagflation', 'v') *\\\n", "hv.Arrow(1979, 8, 'Energy Crisis', 'v') * hv.Arrow(1981.9, 5, 'Early Eighties\\n Recession', 'v')\n", From 601ee093f9dfeab72eb72a78b67df137a64214c0 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Sun, 3 Jan 2016 19:11:54 +0100 Subject: [PATCH 4/7] Use slightly larger scaling factor to achieve similar distribution of point sizes as previously. --- doc/Tutorials/Pandas_Conversion.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Tutorials/Pandas_Conversion.ipynb b/doc/Tutorials/Pandas_Conversion.ipynb index ff4094b11e..d685eb76b6 100644 --- a/doc/Tutorials/Pandas_Conversion.ipynb +++ b/doc/Tutorials/Pandas_Conversion.ipynb @@ -565,7 +565,7 @@ }, "outputs": [], "source": [ - "%%opts Curve (color='k') Scatter [color_index=2 size_index=2 scaling_factor=10.0] (cmap='Blues' edgecolors='k')\n", + "%%opts Curve (color='k') Scatter [color_index=2 size_index=2 scaling_factor=20.0] (cmap='Blues' edgecolors='k')\n", "macro_overlay = gdp_curves * gdp_unem_scatter\n", "annotations = hv.Arrow(1973, 8, 'Oil Crisis', 'v') * hv.Arrow(1975, 6, 'Stagflation', 'v') *\\\n", "hv.Arrow(1979, 8, 'Energy Crisis', 'v') * hv.Arrow(1981.9, 5, 'Early Eighties\\n Recession', 'v')\n", From da245a9d8b05703d10b79c39291888ae2d44c824 Mon Sep 17 00:00:00 2001 From: Maximilian Albert Date: Tue, 5 Jan 2016 01:03:09 +0100 Subject: [PATCH 5/7] Adapt bounds for scaling_factor (it can now be any non-zero number). --- holoviews/plotting/mpl/chart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py index f509b5f882..97c9282527 100644 --- a/holoviews/plotting/mpl/chart.py +++ b/holoviews/plotting/mpl/chart.py @@ -529,7 +529,7 @@ class PointPlot(ChartPlot, ColorbarPlot): 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=""" + 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`.""") @@ -561,7 +561,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 < ndims: style['s'] = self._compute_size(points, style) color = style.pop('color', None) From 6aa9d1feb84fc25d916e22e360e1e881cc3d46fc Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 5 Feb 2016 00:47:42 +0000 Subject: [PATCH 6/7] Fixes for compute_size function in mpl, bokeh and tutorials --- doc/Tutorials/Columnar_Data.ipynb | 4 ++-- doc/Tutorials/Elements.ipynb | 2 +- holoviews/plotting/bokeh/chart.py | 7 ++++--- holoviews/plotting/util.py | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) 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 f5bb07b89e..8e1fe7733b 100644 --- a/doc/Tutorials/Elements.ipynb +++ b/doc/Tutorials/Elements.ipynb @@ -576,7 +576,7 @@ }, "outputs": [], "source": [ - "%%opts Points [color_index=2 size_index=3 scaling_method=\"width\" 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 77c6219518..26fa7f658b 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -61,7 +61,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 < len(dims): map_key = 'size_' + dims[self.size_index] mapping['size'] = map_key if empty: @@ -69,8 +69,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, self.scaling_method, 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/util.py b/holoviews/plotting/util.py index 80366af302..420f335592 100644 --- a/holoviews/plotting/util.py +++ b/holoviews/plotting/util.py @@ -82,9 +82,9 @@ def compute_sizes(sizes, size_fn, scaling_factor, scaling_method, base_size): scaling. """ if scaling_method == 'area': - scaling_factor = math.sqrt(scaling_factor) + pass elif scaling_method == 'width': - pass # can use scaling factor unchanged in this case + scaling_factor = scaling_factor**2 else: raise ValueError( 'Invalid value for argument "scaling_method": "{}". ' From 1ac5876dd41ba149618e83d18f6497ee31a53d59 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 5 Feb 2016 01:12:17 +0000 Subject: [PATCH 7/7] Ensured color and size_index are handled correctly when None --- holoviews/plotting/bokeh/chart.py | 11 ++++------- holoviews/plotting/mpl/chart.py | 10 +++++----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py index 26fa7f658b..b03f1d379d 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -13,13 +13,10 @@ 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=""" - Index of the dimension from which the sizes will the drawn.""") - - radius_index = param.Integer(default=None, doc=""" + size_index = param.Integer(default=2, allow_None=True, doc=""" Index of the dimension from which the sizes will the drawn.""") scaling_method = param.ObjectSelector(default="area", @@ -51,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: @@ -61,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): + 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: diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py index 283ff52912..0b10ae8b95 100644 --- a/holoviews/plotting/mpl/chart.py +++ b/holoviews/plotting/mpl/chart.py @@ -542,10 +542,10 @@ 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_method = param.ObjectSelector(default="area", @@ -586,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: + if self.size_index is not None and self.size_index < ndims: style['s'] = self._compute_size(points, style) color = style.pop('color', None) @@ -619,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])