Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes for handling coloring in plotting #1249

Merged
merged 4 commits into from
Apr 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions holoviews/plotting/bokeh/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ def get_batched_data(self, overlay, ranges=None, empty=False):
styles = styles.max_cycles(len(self.ordering))

for (key, el), zorder in zip(overlay.data.items(), zorders):
self.set_param(**self.lookup_options(el, 'plot').options)
eldata, elmapping = self.get_data(el, ranges, empty)
for k, eld in eldata.items():
data[k].append(eld)
Expand Down Expand Up @@ -490,7 +491,20 @@ class SpikesPlot(PathPlot, ColorbarPlot):
def get_extents(self, element, ranges):
l, b, r, t = super(SpikesPlot, self).get_extents(element, ranges)
if len(element.dimensions()) == 1:
b, t = self.position, self.position+self.spike_length
if self.batched:
bs, ts = [], []
# Iterate over current NdOverlay and compute extents
# from position and length plot options
for el in self.current_frame.values():
opts = self.lookup_options(el, 'plot').options
pos = opts.get('position', self.position)
length = opts.get('spike_length', self.spike_length)
bs.append(pos)
ts.append(pos+length)
b = np.nanmin(bs)
t = np.nanmax(ts)
else:
b, t = self.position, self.position+self.spike_length
else:
b = np.nanmin([0, b])
t = np.nanmax([0, t])
Expand All @@ -505,11 +519,11 @@ def get_data(self, element, ranges=None, empty=False):
if empty:
xs, ys = [], []
elif len(dims) > 1:
xs, ys = zip(*(((x, x), (pos+y, pos))
xs, ys = zip(*((np.array([x, x]), np.array([pos+y, pos]))
for x, y in element.array(dims[:2])))
else:
height = self.spike_length
xs, ys = zip(*(((x[0], x[0]), (pos+height, pos))
xs, ys = zip(*((np.array([x[0], x[0]]), np.array([pos+height, pos]))
for x in element.array(dims[:1])))

if not empty and self.invert_axes: xs, ys = ys, xs
Expand Down
6 changes: 4 additions & 2 deletions holoviews/plotting/bokeh/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def get_batched_data(self, element, ranges=None, empty=False):
styles = styles.max_cycles(len(self.ordering))

for (key, el), zorder in zip(element.data.items(), zorders):
self.set_param(**self.lookup_options(el, 'plot').options)
self.overlay_dims = dict(zip(element.kdims, key))
eldata, elmapping = self.get_data(el, ranges, empty)
for k, eld in eldata.items():
Expand Down Expand Up @@ -86,9 +87,10 @@ def get_data(self, element, ranges=None, empty=False):

if element.vdims and element.level is not None:
cdim = element.vdims[0]
dim_name = util.dimension_sanitizer(cdim.name)
cmapper = self._get_colormapper(cdim, element, ranges, style)
data[cdim.name] = [] if empty else element.dimension_values(2)
mapping['fill_color'] = {'field': cdim.name,
data[dim_name] = [] if empty else [element.level for _ in range(len(xs))]
mapping['fill_color'] = {'field': dim_name,
'transform': cmapper}

if any(isinstance(t, HoverTool) for t in self.state.tools):
Expand Down
18 changes: 12 additions & 6 deletions holoviews/plotting/bokeh/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

from ...core.options import abbreviated_exception
from ...core.overlay import Overlay
from ...core.util import basestring
from ...core.util import basestring, unique_array

from ..util import dim_axis_label

Expand Down Expand Up @@ -567,7 +567,7 @@ def expand_batched_style(style, opts, mapping, nvals):
alias = 'alpha'
else:
alias = None
if opt not in style:
if opt not in style or opt in mapping:
continue
elif opt == alias:
if alias in applied_styles:
Expand Down Expand Up @@ -595,14 +595,20 @@ def expand_batched_style(style, opts, mapping, nvals):
def filter_batched_data(data, mapping):
"""
Iterates over the data and mapping for a ColumnDataSource and
replaces columns with repeating values with scalar
replaces columns with repeating values with a scalar. This is
purely and optimization for scalar types.
"""
for k, v in list(mapping.items()):
if isinstance(v, dict) and 'field' in v:
if 'transform' in v:
continue
v = v['field']
elif not isinstance(v, basestring):
continue
values = data[v]
if len(np.unique(values)) == 1:
mapping[k] = values[0]
del data[v]
try:
if len(unique_array(values)) == 1:
mapping[k] = values[0]
del data[v]
except:
pass
2 changes: 1 addition & 1 deletion holoviews/plotting/mpl/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ def _norm_kwargs(self, element, ranges, opts, vdim):
opts['vmax'] = clim[1]

# Check whether the colorbar should indicate clipping
values = element.dimension_values(vdim)
values = np.asarray(element.dimension_values(vdim))
if values.dtype.kind not in 'OSUM':
el_min, el_max = np.nanmin(values), np.nanmax(values)
else:
Expand Down
51 changes: 51 additions & 0 deletions tests/testplotinstantiation.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,15 @@ def test_points_rcparams_used(self):
lines = ax.get_xgridlines()
self.assertEqual(lines[0].get_color(), 'red')

def test_polygons_colored(self):
polygons = NdOverlay({j: Polygons([[(i**j, i) for i in range(10)]], level=j)
for j in range(5)})
plot = mpl_renderer.get_plot(polygons)
for j, splot in enumerate(plot.subplots.values()):
artist = splot.handles['artist']
self.assertEqual(artist.get_array(), np.array([j]))
self.assertEqual(artist.get_clim(), (0, 4))



class TestBokehPlotInstantiation(ComparisonTestCase):
Expand Down Expand Up @@ -328,6 +337,15 @@ def test_batched_plot(self):
extents = plot.get_extents(overlay, {})
self.assertEqual(extents, (0, 0, 98, 98))

def test_batched_spike_plot(self):
overlay = NdOverlay({i: Spikes([i], kdims=['Time'])(plot=dict(position=0.1*i,
spike_length=0.1,
show_legend=False))
for i in range(10)})
plot = bokeh_renderer.get_plot(overlay)
extents = plot.get_extents(overlay, {})
self.assertEqual(extents, (0, 0, 9, 1))

def test_batched_points_size_and_color(self):
opts = {'NdOverlay': dict(plot=dict(legend_limit=0)),
'Points': dict(style=dict(size=Cycle(values=[1, 2])))}
Expand Down Expand Up @@ -506,6 +524,39 @@ def _test_colormapping(self, element, dim, log=False):
mapper_type = LogColorMapper if log else LinearColorMapper
self.assertTrue(isinstance(cmapper, mapper_type))

def test_polygons_colored(self):
polygons = NdOverlay({j: Polygons([[(i**j, i) for i in range(10)]], level=j)
for j in range(5)})
plot = bokeh_renderer.get_plot(polygons)
for i, splot in enumerate(plot.subplots.values()):
cmapper = splot.handles['color_mapper']
self.assertEqual(cmapper.low, 0)
self.assertEqual(cmapper.high, 4)
source = splot.handles['source']
self.assertEqual(source.data['Value'], np.array([i]))

def test_polygons_colored_batched(self):
polygons = NdOverlay({j: Polygons([[(i**j, i) for i in range(10)]], level=j)
for j in range(5)})(plot=dict(legend_limit=0))
plot = list(bokeh_renderer.get_plot(polygons).subplots.values())[0]
cmapper = plot.handles['color_mapper']
self.assertEqual(cmapper.low, 0)
self.assertEqual(cmapper.high, 4)
source = plot.handles['source']
self.assertEqual(source.data['Value'], list(range(5)))

def test_polygons_colored_batched_unsanitized(self):
polygons = NdOverlay({j: Polygons([[(i**j, i) for i in range(10)] for i in range(2)],
level=j, vdims=['some ? unescaped name'])
for j in range(5)})(plot=dict(legend_limit=0))
plot = list(bokeh_renderer.get_plot(polygons).subplots.values())[0]
cmapper = plot.handles['color_mapper']
self.assertEqual(cmapper.low, 0)
self.assertEqual(cmapper.high, 4)
source = plot.handles['source']
self.assertEqual(source.data['some_question_mark_unescaped_name'],
[j for i in range(5) for j in [i, i]])

def test_points_colormapping(self):
points = Points(np.random.rand(10, 4), vdims=['a', 'b'])(plot=dict(color_index=3))
self._test_colormapping(points, 3)
Expand Down