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)