Skip to content

Commit

Permalink
Bumped version to 1.8.4
Browse files Browse the repository at this point in the history
  • Loading branch information
jlstevens committed Sep 13, 2017
1 parent a66181c commit f927852
Show file tree
Hide file tree
Showing 26 changed files with 277 additions and 85 deletions.
26 changes: 26 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,29 @@
Version 1.8.4
=============

This bugfix release includes a number of critical fixes for compatiblity
with bokeh 0.12.9 along with various other bug fixes. Many thanks to our
users for various detailed bug reports, feedback and contributions.

Fixes:

- Fixes to register BoundsXY stream.
([\#1826](https://github.com/ioam/holoviews/pull/1826))
- Fix for Bounds streams on bokeh server.
([\#1883](https://github.com/ioam/holoviews/pull/1883))
- Compatibility with matplotlib 2.1
([\#1842](https://github.com/ioam/holoviews/pull/1842))
- Fixed bug in scrubber widget and support for scrubbing discrete
DynamicMaps ([\#1832](https://github.com/ioam/holoviews/pull/1832))
- Various fixes for compatibility with bokeh 0.12.9
([\#1849](https://github.com/ioam/holoviews/pull/1849),
[\#1866](https://github.com/ioam/holoviews/pull/1886))
- Fixes for setting QuadMesh ranges.
([\#1876](https://github.com/ioam/holoviews/pull/1876))
- Fixes for inverting Image/RGB/Raster axes in bokeh.
([\#1872](https://github.com/ioam/holoviews/pull/1872))


Version 1.8.3
=============

Expand Down
2 changes: 1 addition & 1 deletion doc/about.rst
@@ -1,7 +1,7 @@
About Us
========

HoloViews is supported and maintained by `Continuum Analytics <https://continuum.io>`_.
HoloViews is supported and maintained by `Anaconda <https://www.anaconda.com>`_.

The primary developers are Jean-Luc Stevens, Philipp Rudiger, and
James A. Bednar, with bug reports and patches from numerous members of
Expand Down
3 changes: 2 additions & 1 deletion examples/README.md
Expand Up @@ -10,6 +10,7 @@ This directory contains all the notebooks built as part of the
* ``getting_started``: Notebooks used in the [getting started](http://holoviews.org/getting_started/index.html) guide.
* `reference`: Notebooks shown in the website [reference gallery](http://holoviews.org/reference/index.html)
* ``topics``: Notebooks shown in the [showcase](http://holoviews.org/reference/showcase/index.html)
* ``user_guide``: Notebooks used in the [user guide](http://holoviews.org/user_guide/index.html).

## Contributing to examples

Expand All @@ -22,4 +23,4 @@ consider submitting a PR.

Lastly, if you find a particular notebook that does not seem to be
working, please file an
[issue](https://github.com/ioam/holoviews/issues).
[issue](https://github.com/ioam/holoviews/issues).
2 changes: 1 addition & 1 deletion examples/user_guide/Deploying_Bokeh_Apps.ipynb
Expand Up @@ -283,7 +283,7 @@
"import holoviews as hv\n",
"import holoviews.plotting.bokeh\n",
"\n",
"renderer = hv.renderers('bokeh')\n",
"renderer = hv.renderer('bokeh')\n",
"\n",
"points = hv.Points(np.random.randn(1000,2 )).opts(plot=dict(tools=['box_select', 'lasso_select']))\n",
"selection = hv.streams.Selection1D(source=points)\n",
Expand Down
2 changes: 1 addition & 1 deletion holoviews/__init__.py
Expand Up @@ -9,7 +9,7 @@

import param

__version__ = param.Version(release=(1,8,3), fpath=__file__,
__version__ = param.Version(release=(1,8,4), fpath=__file__,
commit="$Format:%h$", reponame='holoviews')

from .core import archive, config # noqa (API import)
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/dimension.py
Expand Up @@ -485,7 +485,7 @@ class LabelledData(param.Parameterized):
label = param.String(default='', constant=True, doc="""
Optional label describing the data, typically reflecting where
or how it was measured. The label should allow a specific
measurement or dataset to be referenced for a given group..""")
measurement or dataset to be referenced for a given group.""")

_deep_indexable = False

Expand Down
6 changes: 5 additions & 1 deletion holoviews/core/spaces.py
Expand Up @@ -518,6 +518,10 @@ def __call__(self, *args, **kwargs):

try:
ret = self.callable(*args, **kwargs)
except KeyError:
# KeyError is caught separately because it is used to signal
# invalid keys on DynamicMap and should not warn
raise
except:
posstr = ', '.join(['%r' % el for el in self.args]) if self.args else ''
kwstr = ', '.join('%s=%r' % (k,v) for k,v in self.kwargs.items())
Expand Down Expand Up @@ -1055,7 +1059,7 @@ def _cache(self, key, val):
if len(self) >= cache_size:
first_key = next(k for k in self.data)
self.data.pop(first_key)
self.data[key] = val
self[key] = val


def map(self, map_fn, specs=None, clone=True):
Expand Down
7 changes: 6 additions & 1 deletion holoviews/core/traversal.py
Expand Up @@ -8,7 +8,7 @@
from operator import itemgetter

from .dimension import Dimension
from .util import merge_dimensions
from .util import merge_dimensions, cartesian_product

try:
import itertools.izip as zip
Expand Down Expand Up @@ -89,6 +89,11 @@ def unique_dimkeys(obj, default_dim='Frame'):
if not matches:
unique_keys.append(padded_key)

# Add cartesian product of DynamicMap values to keys
values = [d.values for d in all_dims]
if obj.traverse(lambda x: x, ['DynamicMap']) and values and all(values):
unique_keys += list(zip(*cartesian_product(values)))

with item_check(False):
sorted_keys = NdMapping({key: None for key in unique_keys},
kdims=all_dims).data.keys()
Expand Down
24 changes: 15 additions & 9 deletions holoviews/operation/datashader.py
Expand Up @@ -215,8 +215,8 @@ def get_agg_data(cls, obj, category=None):

for d in (x, y):
if df[d].dtype.kind == 'M':
param.warning('Casting %s dimension data to integer '
'datashader cannot process datetime data ')
param.main.warning('Casting %s dimension data to integer; '
'datashader cannot process datetime data', d)
df[d] = df[d].astype('int64') / 1000000.

return x, y, Dataset(df, kdims=kdims, vdims=vdims), glyph
Expand Down Expand Up @@ -356,7 +356,13 @@ class shade(Operation):
"""

cmap = param.ClassSelector(default=fire, class_=(Iterable, Callable, dict), doc="""
Iterable or callable which returns colors as hex colors.
Iterable or callable which returns colors as hex colors, to
be used for the colormap of single-layer datashader output.
Callable type must allow mapping colors between 0 and 1.""")

color_key = param.ClassSelector(class_=(Iterable, Callable, dict), doc="""
Iterable or callable which returns colors as hex colors, to
be used for the color key of categorical datashader output.
Callable type must allow mapping colors between 0 and 1.""")

normalization = param.ClassSelector(default='eq_hist',
Expand Down Expand Up @@ -430,15 +436,15 @@ def _process(self, element, key=None):
if element.ndims > 2:
kdims = element.kdims[1:]
categories = array.shape[-1]
if not self.p.cmap:
if not self.p.color_key:
pass
elif isinstance(self.p.cmap, dict):
shade_opts['color_key'] = self.p.cmap
elif isinstance(self.p.cmap, Iterable):
elif isinstance(self.p.color_key, dict):
shade_opts['color_key'] = self.p.color_key
elif isinstance(self.p.color_key, Iterable):
shade_opts['color_key'] = [c for i, c in
zip(range(categories), self.p.cmap)]
zip(range(categories), self.p.color_key)]
else:
colors = [self.p.cmap(s) for s in np.linspace(0, 1, categories)]
colors = [self.p.color_key(s) for s in np.linspace(0, 1, categories)]
shade_opts['color_key'] = map(self.rgb2hex, colors)
elif not self.p.cmap:
pass
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/bokeh/annotation.py
Expand Up @@ -172,7 +172,7 @@ def _init_glyph(self, plot, mapping, properties, key):
"""
Returns a Bokeh glyph object.
"""
properties.pop('legend')
properties.pop('legend', None)
if key == 'arrow':
properties.pop('source')
arrow_end = mapping.pop('arrow_end')
Expand Down
45 changes: 34 additions & 11 deletions holoviews/plotting/bokeh/callbacks.py
Expand Up @@ -544,15 +544,29 @@ class PointerXCallback(PointerXYCallback):
"""

attributes = {'x': 'cb_obj.x'}

extra_models= ['x_range']
code = """
if (x_range.type.endsWith('Range1d')) {
if (cb_obj.x < x_range.start) {
data['x'] = x_range.start }
else if (cb_obj.x > x_range.end) {
data['x'] = x_range.end }}
"""

class PointerYCallback(PointerXYCallback):
"""
Returns the mouse x/y-position on mousemove event.
"""

attributes = {'y': 'cb_obj.y'}

extra_models= ['y_range']
code = """
if (y_range.type.endsWith('Range1d')) {
if (cb_obj.y < y_range.start) {
data['y'] = y_range.start }
else if (cb_obj.y > y_range.end) {
data['y'] = y_range.end }}
"""

class DrawCallback(PointerXYCallback):
on_events = ['pan', 'panstart', 'panend']
Expand Down Expand Up @@ -692,11 +706,14 @@ class BoundsCallback(Callback):
"""
Returns the bounds of a box_select tool.
"""
attributes = {'x0': 'cb_data.geometry.x0',
'x1': 'cb_data.geometry.x1',
'y0': 'cb_data.geometry.y0',
'y1': 'cb_data.geometry.y1'}
models = ['box_select']
attributes = {'x0': 'cb_obj.geometry.x0',
'x1': 'cb_obj.geometry.x1',
'y0': 'cb_obj.geometry.y0',
'y1': 'cb_obj.geometry.y1'}
models = ['plot']
extra_models = ['box_select']
on_events = ['selectiongeometry']
skip = ["!box_select || !box_select.active"]

def _process_msg(self, msg):
if all(c in msg for c in ['x0', 'y0', 'x1', 'y1']):
Expand All @@ -710,8 +727,11 @@ class BoundsXCallback(Callback):
Returns the bounds of a xbox_select tool.
"""

attributes = {'x0': 'cb_data.geometry.x0', 'x1': 'cb_data.geometry.x1'}
models = ['xbox_select']
attributes = {'x0': 'cb_obj.geometry.x0', 'x1': 'cb_obj.geometry.x1'}
models = ['plot']
extra_models = ['xbox_select']
on_events = ['selectiongeometry']
skip = ["!xbox_select || !xbox_select.active"]

def _process_msg(self, msg):
if all(c in msg for c in ['x0', 'x1']):
Expand All @@ -725,8 +745,11 @@ class BoundsYCallback(Callback):
Returns the bounds of a ybox_select tool.
"""

attributes = {'y0': 'cb_data.geometry.y0', 'y1': 'cb_data.geometry.y1'}
models = ['ybox_select']
attributes = {'y0': 'cb_obj.geometry.y0', 'y1': 'cb_obj.geometry.y1'}
models = ['plot']
extra_models = ['ybox_select']
on_events = ['selectiongeometry']
skip = ["!ybox_select || !ybox_select.active"]

def _process_msg(self, msg):
if all(c in msg for c in ['y0', 'y1']):
Expand Down
59 changes: 48 additions & 11 deletions holoviews/plotting/bokeh/chart.py
Expand Up @@ -655,6 +655,24 @@ def get_extents(self, element, ranges):
x1 = xdim.pprint_value(extents[2])
return (x0, y0, x1, y1)


def _get_factors(self, element):
"""
Get factors for categorical axes.
"""
xdim, ydim = element.dimensions()[:2]
gdim = element.get_dimension(self.group_index)
xvals = element.dimension_values(0, False)
xvals = [x if xvals.dtype.kind in 'SU' else xdim.pprint_value(x) for x in xvals]
if bokeh_version >= '0.12.7' and gdim:
gvals = element.dimension_values(gdim, False)
gvals = [g if gvals.dtype.kind in 'SU' else gdim.pprint_value(g) for g in gvals]
coords = ([(x, g) for x in xvals for g in gvals], [])
else:
coords = ([x.replace(':', ';') for x in xvals], [])
if self.invert_axes: coords = coords[::-1]
return coords

def _get_axis_labels(self, *args, **kwargs):
"""
Override axis mapping by setting the first key and value
Expand All @@ -663,15 +681,20 @@ def _get_axis_labels(self, *args, **kwargs):
element = self.current_frame
if self.batched:
element = element.last
return (dim_axis_label(element.kdims[0]),
dim_axis_label(element.vdims[0]), None)
xlabel = dim_axis_label(element.kdims[0])
gdim = element.get_dimension(self.group_index)
if bokeh_version >= '0.12.7' and gdim:
xlabel = ', '.join([xlabel, dim_axis_label(gdim)])
return (xlabel, dim_axis_label(element.vdims[0]), None)

def get_group(self, xvals, nshift, ngroups, width, xdim):
"""
Adjust x-value positions on categorical axes to stop
x-axis overlapping. Currently bokeh uses a suffix
of the format ':%f' with a floating value to set up
offsets within a single category.
Needed for bokeh version <0.12.7
"""
adjusted_xvals = []
gwidth = float(width)/ngroups
Expand Down Expand Up @@ -742,7 +765,7 @@ def get_data(self, element, ranges, empty):
mapping = {'x': xdim.name, 'top': 'top',
'bottom': 'bottom', 'width': width}
elif grouping == 'grouped':
if len(grouped):
if len(grouped) and bokeh_version < '0.12.7':
gwidth = width / float(len(grouped))
else:
gwidth = width
Expand Down Expand Up @@ -775,6 +798,11 @@ def get_data(self, element, ranges, empty):
for i, (k, ds) in enumerate(grouped.items()):
xs = ds.dimension_values(xdim)
ys = ds.dimension_values(ydim)
k = k[0] if isinstance(k, tuple) else k
if group_dim:
gval = k if isinstance(k, basestring) else group_dim.pprint_value(k)
if bokeh_version < '0.12.7':
gval = gval.replace(':', ';')

# Apply stacking or grouping
if grouping == 'stacked':
Expand All @@ -784,7 +812,11 @@ def get_data(self, element, ranges, empty):
data[xdim.name].append(xs)
if hover: data[ydim.name].append(ys)
elif grouping == 'grouped':
xoffsets = self.get_group(xs, i, len(grouped), width, xdim)
if bokeh_version >= '0.12.7':
xoffsets = [(x if xs.dtype.kind in 'SU' else xdim.pprint_value(x), gval)
for x in xs]
else:
xoffsets = self.get_group(xs, i, len(grouped), width, xdim)
data['xoffsets'].append(xoffsets)
data[ydim.name].append(ys)
if hover: data[xdim.name].append(xs)
Expand All @@ -794,8 +826,6 @@ def get_data(self, element, ranges, empty):

# Add group dimension to data
if group_dim and group_dim not in ds.dimensions():
k = k[0] if isinstance(k, tuple) else k
gval = group_dim.pprint_value(k).replace(':', ';')
ds = ds.add_dimension(group_dim.name, ds.ndims, gval)

# Get colormapper
Expand Down Expand Up @@ -922,9 +952,13 @@ def _get_factors(self, element):
if not element.kdims:
return [element.label], []
else:
factors = [', '.join([d.pprint_value(v).replace(':', ';')
for d, v in zip(element.kdims, key)])
for key in element.groupby(element.kdims).data.keys()]
if bokeh_version < '0.12.7':
factors = [', '.join([d.pprint_value(v).replace(':', ';')
for d, v in zip(element.kdims, key)])
for key in element.groupby(element.kdims).data.keys()]
else:
factors = [tuple(d.pprint_value(v) for d, v in zip(element.kdims, key))
for key in element.groupby(element.kdims).data.keys()]
if self.invert_axes:
return [], factors
else:
Expand Down Expand Up @@ -969,8 +1003,11 @@ def get_data(self, element, ranges=None, empty=False):
for key, g in groups.items():
# Compute group label
if element.kdims:
label = ', '.join([d.pprint_value(v).replace(':', ';')
for d, v in zip(element.kdims, key)])
if bokeh_version < '0.12.7':
label = ', '.join([d.pprint_value(v).replace(':', ';')
for d, v in zip(element.kdims, key)])
else:
label = tuple(d.pprint_value(v) for d, v in zip(element.kdims, key))
else:
label = key

Expand Down

0 comments on commit f927852

Please sign in to comment.