Skip to content

Commit

Permalink
Use robust isfinite utility throughout (#3045)
Browse files Browse the repository at this point in the history
# Conflicts:
#	holoviews/core/util.py
#	holoviews/element/raster.py
#	holoviews/operation/stats.py
#	holoviews/plotting/bokeh/stats.py
#	tests/plotting/bokeh/testareaplot.py
  • Loading branch information
philippjfr committed Oct 25, 2018
1 parent 91f030b commit 34fe7ce
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 19 deletions.
6 changes: 3 additions & 3 deletions holoviews/element/raster.py
Expand Up @@ -281,9 +281,9 @@ def __init__(self, data, kdims=None, vdims=None, bounds=None, extents=None,
if self.interface is ImageInterface and not isinstance(data, np.ndarray):
data_bounds = self.bounds.lbrt()
l, b, r, t = bounds.lbrt()
xdensity = xdensity if xdensity else compute_density(l, r, dim1, self._time_unit)
ydensity = ydensity if ydensity else compute_density(b, t, dim2, self._time_unit)
if not np.isfinite(xdensity) or not np.isfinite(ydensity):
xdensity = xdensity if xdensity else util.compute_density(l, r, dim1, self._time_unit)
ydensity = ydensity if ydensity else util.compute_density(b, t, dim2, self._time_unit)
if not util.isfinite(xdensity) or not util.isfinite(ydensity):
raise ValueError('Density along Image axes could not be determined. '
'If the data contains only one coordinate along the '
'x- or y-axis ensure you declare the bounds and/or '
Expand Down
3 changes: 2 additions & 1 deletion holoviews/operation/stats.py
Expand Up @@ -100,7 +100,8 @@ def _process(self, element, key=None):
elif bin_range[0] == bin_range[1]:
bin_range = (bin_range[0]-0.5, bin_range[1]+0.5)

data = data[np.isfinite(data)] if len(data) else []
element_type = Area if self.p.filled else Curve
data = data[isfinite(data)] if len(data) else []
if len(data) > 1:
kde = stats.gaussian_kde(data)
if self.p.bandwidth:
Expand Down
12 changes: 8 additions & 4 deletions holoviews/plotting/bokeh/chart.py
Expand Up @@ -489,7 +489,9 @@ def _split_area(self, xs, lower, upper):
Splits area plots at nans and returns x- and y-coordinates for
each area separated by nans.
"""
split = np.where(np.isnan(xs) | np.isnan(lower) | np.isnan(upper))[0]
xnan = np.array([np.datetime64('nat') if xs.dtype.kind == 'M' else np.nan])
ynan = np.array([np.datetime64('nat') if lower.dtype.kind == 'M' else np.nan])
split = np.where(~isfinite(xs) | ~isfinite(lower) | ~isfinite(upper))[0]
xvals = np.split(xs, split)
lower = np.split(lower, split)
upper = np.split(upper, split)
Expand All @@ -499,10 +501,12 @@ def _split_area(self, xs, lower, upper):
x, l, u = x[1:], l[1:], u[1:]
if not len(x):
continue
band_x += [np.append(x, x[::-1]), np.array([np.nan])]
band_y += [np.append(l, u[::-1]), np.array([np.nan])]
band_x += [np.append(x, x[::-1]), xnan]
band_y += [np.append(l, u[::-1]), ynan]
if len(band_x):
return np.concatenate(band_x[:-1]), np.concatenate(band_y[:-1])
xs = np.concatenate(band_x[:-1])
ys = np.concatenate(band_y[:-1])
return xs, ys
return [], []

def get_data(self, element, ranges, style):
Expand Down
4 changes: 2 additions & 2 deletions holoviews/plotting/bokeh/raster.py
@@ -1,7 +1,7 @@
import numpy as np
import param

from ...core.util import cartesian_product, dimension_sanitizer
from ...core.util import cartesian_product, dimension_sanitizer, isfinite
from ...element import Raster, RGB, HSV
from .element import ElementPlot, ColorbarPlot, line_properties, fill_properties
from .util import mpl_to_bokeh, colormesh, bokeh_version
Expand Down Expand Up @@ -168,7 +168,7 @@ def get_data(self, element, ranges, style):
xc, yc = [], []
for xs, ys, zval in zip(X, Y, zvals):
xs, ys = xs[:-1], ys[:-1]
if np.isfinite(zval) and all(np.isfinite(xs)) and all(np.isfinite(ys)):
if isfinite(zval) and all(isfinite(xs)) and all(isfinite(ys)):
XS.append(list(xs))
YS.append(list(ys))
mask.append(True)
Expand Down
14 changes: 9 additions & 5 deletions holoviews/plotting/bokeh/stats.py
Expand Up @@ -9,7 +9,7 @@
from ...core.dimension import Dimension
from ...core.ndmapping import sorted_context
from ...core.util import (basestring, dimension_sanitizer, wrap_tuple,
unique_iterator)
unique_iterator, isfinite)
from ...operation.stats import univariate_kde
from .chart import AreaPlot
from .element import (CompositeElementPlot, ColorbarPlot, LegendPlot,
Expand Down Expand Up @@ -174,7 +174,7 @@ def get_data(self, element, ranges, style):

# Compute statistics
vals = g.dimension_values(g.vdims[0])
vals = vals[np.isfinite(vals)]
vals = vals[isfinite(vals)]
if len(vals):
q1, q2, q3 = (np.percentile(vals, q=q)
for q in range(25, 100, 25))
Expand Down Expand Up @@ -324,14 +324,18 @@ def _kde_data(self, el, key, **kwargs):
el = el.clone(vdims=[vdim])
kde = univariate_kde(el, dimension=vdim, **kwargs)
xs, ys = (kde.dimension_values(i) for i in range(2))
ys = (ys/ys.max())*(self.violin_width/2.)
mask = isfinite(ys) & (ys>0) # Mask out non-finite and zero values
xs, ys = xs[mask], ys[mask]
ys = (ys/ys.max())*(self.violin_width/2.) if len(ys) else []
ys = [key+(sign*y,) for sign, vs in ((-1, ys), (1, ys[::-1])) for y in vs]
kde = {'x': np.concatenate([xs, xs[::-1]]), 'y': ys}

bars, segments, scatter = defaultdict(list), defaultdict(list), {}
values = el.dimension_values(vdim)
values = values[np.isfinite(values)]
if self.inner == 'quartiles':
values = values[isfinite(values)]
if not len(values):
pass
elif self.inner == 'quartiles':
for stat_fn in self._stat_fns:
stat = stat_fn(values)
sidx = np.argmin(np.abs(xs-stat))
Expand Down
6 changes: 3 additions & 3 deletions holoviews/plotting/util.py
Expand Up @@ -14,7 +14,7 @@
from ..core.options import Cycle
from ..core.spaces import get_nested_streams
from ..core.util import (match_spec, wrap_tuple, basestring, get_overlay_spec,
unique_iterator, closest_match, is_number)
unique_iterator, closest_match, is_number, isfinite)
from ..streams import LinkedStream

def displayable(obj):
Expand Down Expand Up @@ -348,8 +348,8 @@ def get_sideplot_ranges(plot, element, main, ranges):

def within_range(range1, range2):
"""Checks whether range1 is within the range specified by range2."""
range1 = [r if np.isfinite(r) else None for r in range1]
range2 = [r if np.isfinite(r) else None for r in range2]
range1 = [r if isfinite(r) else None for r in range1]
range2 = [r if isfinite(r) else None for r in range2]
return ((range1[0] is None or range2[0] is None or range1[0] >= range2[0]) and
(range1[1] is None or range2[1] is None or range1[1] <= range2[1]))

Expand Down
25 changes: 24 additions & 1 deletion tests/core/testutils.py
Expand Up @@ -18,7 +18,8 @@
from holoviews.core.util import (
sanitize_identifier_fn, find_range, max_range, wrap_tuple_streams,
deephash, merge_dimensions, get_path, make_path_unique, compute_density,
date_range, dt_to_int, compute_edges, isfinite, cross_index, closest_match
date_range, dt_to_int, compute_edges, isfinite, cross_index, closest_match,
dimension_range
)
from holoviews import Dimension, Element
from holoviews.streams import PointerXY
Expand Down Expand Up @@ -405,6 +406,28 @@ def test_soft_range(self):
self.assertEqual(find_range(self.float_vals, soft_range=(np.NaN, 100)), (-0.1424, 100))


class TestDimensionRange(unittest.TestCase):
"""
Tests for dimension_range function.
"""

def setUp(self):
self.date_range = (np.datetime64(datetime.datetime(2017, 1, 1)),
np.datetime64(datetime.datetime(2017, 1, 2)))
self.date_range2 = (np.datetime64(datetime.datetime(2016, 12, 31)),
np.datetime64(datetime.datetime(2017, 1, 3)))

def test_dimension_range_date_hard_range(self):
drange = dimension_range(self.date_range2[0], self.date_range2[1],
self.date_range, (None, None))
self.assertEqual(drange, self.date_range)

def test_dimension_range_date_soft_range(self):
drange = dimension_range(self.date_range[0], self.date_range[1],
(None, None), self.date_range2)
self.assertEqual(drange, self.date_range2)


class TestMaxRange(unittest.TestCase):
"""
Tests for max_range function.
Expand Down
2 changes: 2 additions & 0 deletions tests/plotting/bokeh/testareaplot.py
@@ -1,3 +1,5 @@
import datetime as dt

import numpy as np

from holoviews.element import Area
Expand Down

0 comments on commit 34fe7ce

Please sign in to comment.