From e03a91f77afa5023cb885357cb2214e57d6b899f Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 10 Oct 2018 13:22:02 +0100 Subject: [PATCH 1/4] Allow using explicit colormapping on non-categorical data --- holoviews/plotting/bokeh/element.py | 10 +++++++++- holoviews/tests/plotting/bokeh/testelementplot.py | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index faa5ab7fd3..26d9279aa5 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -1137,8 +1137,11 @@ def _get_colormapper(self, dim, element, ranges, style, factors=None, colors=Non cmap = colors or style.pop('cmap', 'viridis') nan_colors = {k: rgba_tuple(v) for k, v in self.clipping_colors.items()} - if isinstance(cmap, dict) and factors: + if isinstance(cmap, dict) or factors: + if not factors: + factors = list(cmap) palette = [cmap.get(f, nan_colors.get('NaN', self._default_nan)) for f in factors] + factors = [dim.pprint_value(f) for f in factors] else: categorical = ncolors is not None if isinstance(self.color_levels, int): @@ -1189,6 +1192,11 @@ def _get_color_data(self, element, ranges, style, name='color', factors=None, co mapper = self._get_colormapper(cdim, element, ranges, style, factors, colors) + if not factors and isinstance(mapper, CategoricalColorMapper): + field += '_str' + cdata = [cdim.pprint_value(c) for c in cdata] + factors = mapper.factors + data[field] = cdata if factors is not None and self.show_legend: mapping['legend'] = {'field': field} diff --git a/holoviews/tests/plotting/bokeh/testelementplot.py b/holoviews/tests/plotting/bokeh/testelementplot.py index 08622ef534..4f6694ac68 100644 --- a/holoviews/tests/plotting/bokeh/testelementplot.py +++ b/holoviews/tests/plotting/bokeh/testelementplot.py @@ -1,3 +1,4 @@ +from collections import OrderedDict from nose.plugins.attrib import attr import numpy as np @@ -232,6 +233,18 @@ def test_colormapper_min_max_colors(self): self.assertEqual(cmapper.low_color, 'red') self.assertEqual(cmapper.high_color, 'blue') + def test_explicit_categorical_cmap_on_integer_data(self): + explicit_mapping = OrderedDict([(0, 'blue'), (1, 'red'), (2, 'green'), (3, 'purple')]) + points = Scatter(([0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]), vdims=['y', 'Category']).options( + color_index='Category', cmap=explicit_mapping + ) + plot = bokeh_renderer.get_plot(points) + cmapper = plot.handles['color_mapper'] + cds = plot.handles['cds'] + self.assertEqual(cds.data['Category_str'], ['0', '1', '2', '3']) + self.assertEqual(cmapper.factors, ['0', '1', '2', '3']) + self.assertEqual(cmapper.palette, ['blue', 'red', 'green', 'purple']) + class TestOverlayPlot(TestBokehPlot): From ae814edd3d2b312d11440b61e274e2d8ec328a5a Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 10 Oct 2018 13:26:06 +0100 Subject: [PATCH 2/4] Fixed minor bug --- holoviews/plotting/bokeh/element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index 26d9279aa5..b1346e73ae 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -1137,7 +1137,7 @@ def _get_colormapper(self, dim, element, ranges, style, factors=None, colors=Non cmap = colors or style.pop('cmap', 'viridis') nan_colors = {k: rgba_tuple(v) for k, v in self.clipping_colors.items()} - if isinstance(cmap, dict) or factors: + if isinstance(cmap, dict): if not factors: factors = list(cmap) palette = [cmap.get(f, nan_colors.get('NaN', self._default_nan)) for f in factors] From 52cd6fa0f26372597cbc8077d94cd227937ad3ce Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 10 Oct 2018 19:28:32 +0100 Subject: [PATCH 3/4] Added double trailing underscores to categorized columns in CDS --- holoviews/plotting/bokeh/element.py | 4 ++-- holoviews/tests/plotting/bokeh/testelementplot.py | 2 +- holoviews/tests/plotting/bokeh/testgraphplot.py | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index b1346e73ae..7c247b081c 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -1186,14 +1186,14 @@ def _get_color_data(self, element, ranges, style, name='color', factors=None, co if factors is None and (isinstance(cdata, list) or cdata.dtype.kind in dtypes): factors = list(util.unique_array(cdata)) if factors and int_categories and cdata.dtype.kind == 'i': - field += '_str' + field += '_str__' cdata = [str(f) for f in cdata] factors = [str(f) for f in factors] mapper = self._get_colormapper(cdim, element, ranges, style, factors, colors) if not factors and isinstance(mapper, CategoricalColorMapper): - field += '_str' + field += '_str__' cdata = [cdim.pprint_value(c) for c in cdata] factors = mapper.factors diff --git a/holoviews/tests/plotting/bokeh/testelementplot.py b/holoviews/tests/plotting/bokeh/testelementplot.py index 4f6694ac68..aad836e4c0 100644 --- a/holoviews/tests/plotting/bokeh/testelementplot.py +++ b/holoviews/tests/plotting/bokeh/testelementplot.py @@ -241,7 +241,7 @@ def test_explicit_categorical_cmap_on_integer_data(self): plot = bokeh_renderer.get_plot(points) cmapper = plot.handles['color_mapper'] cds = plot.handles['cds'] - self.assertEqual(cds.data['Category_str'], ['0', '1', '2', '3']) + self.assertEqual(cds.data['Category_str__'], ['0', '1', '2', '3']) self.assertEqual(cmapper.factors, ['0', '1', '2', '3']) self.assertEqual(cmapper.palette, ['blue', 'red', 'green', 'purple']) diff --git a/holoviews/tests/plotting/bokeh/testgraphplot.py b/holoviews/tests/plotting/bokeh/testgraphplot.py index bcf672b414..f316119a3b 100644 --- a/holoviews/tests/plotting/bokeh/testgraphplot.py +++ b/holoviews/tests/plotting/bokeh/testgraphplot.py @@ -142,8 +142,8 @@ def test_graph_edges_categorical_colormapped(self): self.assertIsInstance(cmapper, CategoricalColorMapper) factors = ['0', '1', '2', '3', '4', '5', '6', '7'] self.assertEqual(cmapper.factors, factors) - self.assertEqual(edge_source.data['start_str'], factors) - self.assertEqual(glyph.line_color, {'field': 'start_str', 'transform': cmapper}) + self.assertEqual(edge_source.data['start_str__'], factors) + self.assertEqual(glyph.line_color, {'field': 'start_str__', 'transform': cmapper}) def test_graph_edges_numerically_colormapped(self): g = self.graph4.opts(plot=dict(edge_color_index='Weight'), @@ -202,8 +202,8 @@ def test_graph_edges_categorical_colormapped(self): self.assertIsInstance(cmapper, CategoricalColorMapper) factors = ['0', '1', '2', '3'] self.assertEqual(cmapper.factors, factors) - self.assertEqual(edge_source.data['node1_str'], ['0', '1']) - self.assertEqual(glyph.line_color, {'field': 'node1_str', 'transform': cmapper}) + self.assertEqual(edge_source.data['node1_str__'], ['0', '1']) + self.assertEqual(glyph.line_color, {'field': 'node1_str__', 'transform': cmapper}) def test_graph_nodes_numerically_colormapped(self): g = self.trimesh_weighted.opts(plot=dict(edge_color_index='weight'), @@ -258,6 +258,6 @@ def test_chord_edges_categorically_colormapped(self): self.assertIsInstance(cmapper, CategoricalColorMapper) self.assertEqual(cmapper.palette, ['#FFFFFF', '#000000', '#FFFFFF']) self.assertEqual(cmapper.factors, ['0', '1', '2']) - self.assertEqual(edge_source.data['start_str'], ['0', '0', '1']) - self.assertEqual(glyph.line_color, {'field': 'start_str', 'transform': cmapper}) + self.assertEqual(edge_source.data['start_str__'], ['0', '0', '1']) + self.assertEqual(glyph.line_color, {'field': 'start_str__', 'transform': cmapper}) From 2b028cb7dd9d12d46d4a760532b166103d49332a Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 10 Oct 2018 20:51:55 +0100 Subject: [PATCH 4/4] Updated column categorization field on GraphPlot --- holoviews/plotting/bokeh/graphs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/holoviews/plotting/bokeh/graphs.py b/holoviews/plotting/bokeh/graphs.py index e1bed58485..24ee136a3b 100644 --- a/holoviews/plotting/bokeh/graphs.py +++ b/holoviews/plotting/bokeh/graphs.py @@ -111,7 +111,7 @@ def _get_edge_colors(self, element, ranges, edge_data, edge_mapping, style): cvals = cvals.astype(np.int32) factors = factors.astype(np.int32) if factors.dtype.kind not in 'SU': - field += '_str' + field += '_str__' cvals = [str(f) for f in cvals] factors = (str(f) for f in factors) factors = list(factors)