Skip to content

Commit

Permalink
Allow using explicit colormapping on non-categorical data (#3071)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored and jlstevens committed Oct 10, 2018
1 parent af065b7 commit d91c86f
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 9 deletions.
12 changes: 10 additions & 2 deletions holoviews/plotting/bokeh/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
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):
Expand Down Expand Up @@ -1183,12 +1186,17 @@ 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__'
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}
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/bokeh/graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 13 additions & 0 deletions holoviews/tests/plotting/bokeh/testelementplot.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections import OrderedDict
from nose.plugins.attrib import attr

import numpy as np
Expand Down Expand Up @@ -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):

Expand Down
12 changes: 6 additions & 6 deletions holoviews/tests/plotting/bokeh/testgraphplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down Expand Up @@ -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'),
Expand Down Expand Up @@ -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})

0 comments on commit d91c86f

Please sign in to comment.