Skip to content

Commit

Permalink
Changed Image.rtol to a warning and increased default tolerance (#2585)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored and jlstevens committed Apr 20, 2018
1 parent 0066545 commit e2e81b5
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 34 deletions.
4 changes: 4 additions & 0 deletions holoviews/core/data/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def init(cls, eltype, data, kdims, vdims):
data = dict(zip(dimensions, data))
if isinstance(data, dict):
xs, ys = np.asarray(data[kdims[0].name]), np.asarray(data[kdims[1].name])
xvalid = util.validate_regular_sampling(xs, eltype.rtol or util.config.image_rtol)
yvalid = util.validate_regular_sampling(ys, eltype.rtol or util.config.image_rtol)
if not xvalid or not yvalid:
raise ValueError('ImageInterface only supports regularly sampled coordinates')
l, r, xdensity, invertx = util.bound_range(xs, None, eltype._time_unit)
b, t, ydensity, inverty = util.bound_range(ys, None, eltype._time_unit)
kwargs['bounds'] = BoundingBox(points=((l, b), (r, t)))
Expand Down
13 changes: 12 additions & 1 deletion holoviews/core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class Config(param.ParameterizedFunction):
recommended that users switch this on to update any uses of
__call__ as it will be deprecated in future.""")

image_rtol = param.Number(default=10e-6, doc="""
image_rtol = param.Number(default=10e-4, doc="""
The tolerance used to enforce regular sampling for regular,
gridded data where regular sampling is expected. Expressed as the
maximal allowable sampling difference between sample
Expand Down Expand Up @@ -1553,6 +1553,17 @@ def bound_range(vals, density, time_unit='us'):
return low-halfd, high+halfd, density, invert


def validate_regular_sampling(values, rtol=10e-6):
"""
Validates regular sampling of a 1D array ensuring that the difference
in sampling steps is at most rtol times the smallest sampling step.
Returns a boolean indicating whether the sampling is regular.
"""
diffs = np.diff(values)
vals = np.unique(diffs)
return not (len(vals) > 1 and np.abs(vals.min()-vals.max()) > diffs.min()*rtol)


def compute_density(start, end, length, time_unit='us'):
"""
Computes a grid density given the edges and number of samples.
Expand Down
22 changes: 18 additions & 4 deletions holoviews/element/raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
from .chart import Curve
from .graphs import TriMesh
from .tabular import Table
from .util import (compute_slice_bounds, categorical_aggregate2d,
validate_regular_sampling)
from .util import compute_slice_bounds, categorical_aggregate2d


class Raster(Element2D):
Expand Down Expand Up @@ -284,8 +283,23 @@ def __init__(self, data, kdims=None, vdims=None, bounds=None, extents=None,
% (self.shape, len(self.vdims)))

# Ensure coordinates are regularly sampled
validate_regular_sampling(self, 0, self.rtol)
validate_regular_sampling(self, 1, self.rtol)
xdim, ydim = self.kdims
xvals, yvals = (self.dimension_values(d, expanded=False) for d in self.kdims)
xvalid = util.validate_regular_sampling(xvals, self.rtol)
yvalid = util.validate_regular_sampling(yvals, self.rtol)
msg = ("{clsname} dimension{dims} not evenly sampled to relative "
"tolerance of {rtol}. Please use the QuadMesh element for "
"irregularly sampled data or set a higher tolerance on "
"hv.config.image_rtol or the rtol parameter in the "
"{clsname} constructor.")
dims = None
if not xvalid:
dims = ' %s is ' % xdim if yvalid else '(s) %s and %s are' % (xdim, ydim)
elif not yvalid:
dims = ' %s is' % ydim
if dims:
self.warning(msg.format(clsname=type(self).__name__, dims=dims, rtol=self.rtol))



def __setstate__(self, state):
Expand Down
19 changes: 0 additions & 19 deletions holoviews/element/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,22 +302,3 @@ def connect_edges(graph):
end = end_ds.array(end_ds.kdims[:2])
paths.append(np.array([start[0], end[0]]))
return paths


def validate_regular_sampling(img, dimension, rtol=10e-6):
"""
Validates regular sampling of Image elements ensuring that
coordinates the difference in sampling steps is at most rtol times
the smallest sampling step. By default ensures the largest
sampling difference is less than one millionth of the smallest
sampling step.
"""
dim = img.get_dimension(dimension)
diffs = np.diff(img.dimension_values(dim, expanded=False))
vals = np.unique(diffs)
msg = ("{clsname} dimension {dim} is not evenly sampled to rtol "
"tolerance of {rtol}, please use the QuadMesh element for "
"unevenly or irregularly sampled data.")
if len(vals) > 1 and np.abs(vals.min()-vals.max()) > diffs.min()*rtol:
raise ValueError(msg.format(clsname=type(img).__name__,
dim=dim, rtol=rtol))
19 changes: 9 additions & 10 deletions tests/element/testimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import numpy as np
import holoviews as hv
from holoviews.element import Image, Curve
from holoviews.element.comparison import ComparisonTestCase
from ..utils import LoggingComparisonTestCase


class TestImage(ComparisonTestCase):
class TestImage(LoggingComparisonTestCase):

def setUp(self):
self.array1 = np.array([(0, 1, 2), (3, 4, 5)])
Expand Down Expand Up @@ -45,19 +45,18 @@ def test_image_rtol_failure(self):
vals = np.random.rand(20,20)
xs = np.linspace(0,10,20)
ys = np.linspace(0,10,20)
ys[-1] += 0.001

regexp = 'Image dimension ys is not evenly sampled(.+?)'
with self.assertRaisesRegexp(ValueError, regexp):
Image({'vals':vals, 'xs':xs, 'ys':ys}, ['xs','ys'], 'vals')
ys[-1] += 0.1
Image({'vals':vals, 'xs':xs, 'ys':ys}, ['xs','ys'], 'vals')
substr = ('set a higher tolerance on hv.config.image_rtol or '
'the rtol parameter in the Image constructor.')
self.log_handler.assertEndsWith('WARNING', substr)

def test_image_rtol_constructor(self):
vals = np.random.rand(20,20)
xs = np.linspace(0,10,20)
ys = np.linspace(0,10,20)
ys[-1] += 0.001
Image({'vals':vals, 'xs':xs, 'ys':ys}, ['xs','ys'], 'vals', rtol=10e-3)

ys[-1] += 0.01
Image({'vals':vals, 'xs':xs, 'ys':ys}, ['xs','ys'], 'vals', rtol=10e-2)

def test_image_rtol_config(self):
vals = np.random.rand(20,20)
Expand Down

0 comments on commit e2e81b5

Please sign in to comment.