Skip to content

Commit

Permalink
Fixed various issues for binned data
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Nov 29, 2017
1 parent 5b9193d commit dc1ca4b
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 118 deletions.
2 changes: 1 addition & 1 deletion holoviews/core/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ def __getitem__(self, slices):
value_select = slices[self.ndims]
elif len(slices) == self.ndims+1 and isinstance(slices[self.ndims],
(Dimension,str)):
raise Exception("%r is not an available value dimension" % slices[self.ndims])
raise IndexError("%r is not an available value dimension" % slices[self.ndims])
else:
selection = dict(zip(self.dimensions(label=True), slices))
data = self.select(**selection)
Expand Down
67 changes: 57 additions & 10 deletions holoviews/core/data/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ def init(cls, eltype, data, kdims, vdims):
d for d in kdims + vdims]
if isinstance(data, tuple):
data = {d: v for d, v in zip(dimensions, data)}
elif isinstance(data, list) and data == []:
data = OrderedDict([(d, []) for d in dimensions])
elif not any(isinstance(data, tuple(t for t in interface.types if t is not None))
for interface in cls.interfaces.values()):
data = {k: v for k, v in zip(dimensions, zip(*data))}
elif isinstance(data, np.ndarray):
if data.ndim == 1:
if eltype._auto_indexable_1d and len(kdims)+len(vdims)>1:
data = np.column_stack([np.arange(len(data)), data])
else:
data = np.atleast_2d(data).T
data = {k: data[:,i] for i,k in enumerate(dimensions)}
elif isinstance(data, list) and data == []:
data = {d: np.array([]) for d in dimensions[:ndims]}
data.update({d: np.empty((0,) * ndims) for d in dimensions[ndims:]})
Expand All @@ -75,8 +87,14 @@ def init(cls, eltype, data, kdims, vdims):
for vdim in vdim_names:
shape = data[vdim].shape
error = DataError if len(shape) > 1 else ValueError
if (all((s!=e and (s+1)!=e) for s, e in zip(shape, expected[::-1])) and
not (not expected and shape == (1,))):
if (not expected and shape == (1,)):
pass
elif len(shape) != len(expected):
raise error('The shape of the %s value array does not '
'match the expected dimensionality indicated '
'by the key dimensions. Expected %d-D array, '
'found %d-D array.' % (vdim, len(expected), len(shape)))
elif any((s!=e and (s+1)!=e) for s, e in zip(shape, expected[::-1])):
raise error('Key dimension values and value array %s '
'shapes do not match. Expected shape %s, '
'actual shape: %s' % (vdim, expected[::-1], shape), cls)
Expand Down Expand Up @@ -104,15 +122,16 @@ def dimension_type(cls, dataset, dim):

@classmethod
def shape(cls, dataset, gridded=False):
shape = dataset.data[dataset.vdims[0].name].shape
if gridded:
return dataset.data[dataset.vdims[0].name].shape
return shape
else:
return (cls.length(dataset), len(dataset.dimensions()))
return (np.product(shape), len(dataset.dimensions()))


@classmethod
def length(cls, dataset):
return np.product([len(dataset.data[d.name]) for d in dataset.kdims])
return cls.shape(dataset)[0]


@classmethod
Expand All @@ -127,8 +146,11 @@ def coords(cls, dataset, dim, ordered=False, expanded=False, edges=False):
if expanded:
return util.expand_grid_coords(dataset, dim)
data = dataset.data[dim.name]
if ordered and np.all(data[1:] < data[:-1]):
data = data[::-1]
shape = cls.shape(dataset, True)
isedges = dim in dataset.kdims and len(data) == (shape[dataset.ndims-idx-1]+1)
isedges = (dim in dataset.kdims and len(shape) == dataset.ndims
and len(data) == (shape[dataset.ndims-idx-1]+1))
if edges and not isedges:
data = util.compute_edges(data)
elif not edges and isedges:
Expand Down Expand Up @@ -309,9 +331,9 @@ def key_select_mask(cls, dataset, values, ind):
mask = None
else:
index_mask = values == ind
if dataset.ndims == 1 and np.sum(index_mask) == 0:
if (dataset.ndims == 1 or dataset._binned) and np.sum(index_mask) == 0:
data_index = np.argmin(np.abs(values - ind))
mask = np.zeros(len(dataset), dtype=np.bool)
mask = np.zeros(len(values), dtype=np.bool)
mask[data_index] = True
else:
mask = index_mask
Expand All @@ -331,11 +353,36 @@ def select(cls, dataset, selection_mask=None, **selection):
for d in dimensions]
data = {}
value_select = []
for dim, ind in selection:
values = cls.values(dataset, dim, False)
for (dim, ind) in selection:
values = cls.coords(dataset, dim, False)
mask = cls.key_select_mask(dataset, values, ind)
if mask is None:
mask = np.ones(values.shape, dtype=bool)
if dataset._binned:
edges = cls.coords(dataset, dim, False, edges=True)
inds = np.argwhere(mask)
if np.isscalar(ind):
emin, emax = edges.min(), edges.max()
if ind < emin:
raise IndexError("Index %s less than lower bound "
"of %s for %s dimension." % (ind, emin, dim))
elif ind >= emax:
raise IndexError("Index %s more than or equal to upper bound "
"of %s for %s dimension." % (ind, emax, dim))
idx = max([np.digitize([ind], edges)[0]-1, 0])
mask = np.zeros(len(values), dtype=np.bool)
if np.sum(ind == values):
mask[idx] = True
values = edges[idx:idx+2]
elif len(inds):
mask[idx] = True
values = edges[idx: idx+2]
else:
values = edges[:0]
elif len(inds):
values = edges[inds.min(): inds.max()+2]
else:
values = edges[0:0]
else:
values = values[mask]
value_select.append(mask)
Expand Down
9 changes: 2 additions & 7 deletions holoviews/element/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,9 +536,7 @@ def compare_trisurface(cls, el1, el2, msg='Trisurface'):

@classmethod
def compare_histogram(cls, el1, el2, msg='Histogram'):
cls.compare_dimensioned(el1, el2)
cls.compare_arrays(el1.edges, el2.edges, ' '.join([msg, 'edges']))
cls.compare_arrays(el1.values, el2.values, ' '.join([msg, 'values']))
cls.compare_dataset(el1, el2, msg)

@classmethod
def compare_points(cls, el1, el2, msg='Points'):
Expand Down Expand Up @@ -593,10 +591,7 @@ def compare_raster(cls, el1, el2, msg='Raster'):

@classmethod
def compare_quadmesh(cls, el1, el2, msg='QuadMesh'):
cls.compare_dimensioned(el1, el2)
cls.compare_arrays(el1.data[0], el2.data[0], ' '.join([msg, 'x-data']))
cls.compare_arrays(el1.data[1], el2.data[1], ' '.join([msg, 'y-data']))
cls.compare_arrays(el1.data[2], el2.data[2], ' '.join([msg, 'z-data']))
cls.compare_dataset(el1, el2, msg)

@classmethod
def compare_heatmap(cls, el1, el2, msg='HeatMap'):
Expand Down
7 changes: 2 additions & 5 deletions holoviews/plotting/bokeh/raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,8 @@ def get_data(self, element, ranges, style):
zdata = element.dimension_values(z, flat=False)
widths = np.diff(element.interface.coords(element, x, edges=True))
heights = np.diff(element.interface.coords(element, y, edges=True))
if self.invert_axes:
xvals, yvals, zvals = xvals.T.flatten(), yvals.T.flatten(), zdata.flatten()
xvals, yvals, widths, heights = yvals, xvals, heights, widths
else:
xs, ys, zvals = xvals.flatten(), yvals.flatten(), zdata.T.flatten()
xs, ys = xvals.flatten(), yvals.flatten()
zvals = zdata.flatten() if self.invert_axes else zdata.T.flatten()
ws, hs = cartesian_product([widths, heights], copy=True)
data = {x.name: xs, y.name: ys, z.name: zvals,
'widths': ws, 'heights': hs}
Expand Down
16 changes: 3 additions & 13 deletions tests/testcomparisonchart.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,26 +89,16 @@ def test_histograms_equal_2(self):
self.assertEqual(self.hist2, self.hist2)

def test_histograms_unequal_1(self):
try:
with self.assertRaises(AssertionError):
self.assertEqual(self.hist1, self.hist2)
except AssertionError as e:
if not str(e).startswith("Histogram edges not almost equal to 6 decimals"):
raise self.failureException("Histogram edge data mismatch error not raised.")

def test_histograms_unequal_2(self):
try:
with self.assertRaises(AssertionError):
self.assertEqual(self.hist1, self.hist3)
except AssertionError as e:
if not str(e).startswith("Histogram edges not almost equal to 6 decimals"):
raise self.failureException("Histogram edge data mismatch error not raised.")

def test_histograms_unequal_3(self):
try:
with self.assertRaises(AssertionError):
self.assertEqual(self.hist1, self.hist4)
except AssertionError as e:
if not str(e).startswith("Histogram values not almost equal to 6 decimals"):
raise self.failureException("Histogram value data mismatch error not raised.")




Expand Down
25 changes: 24 additions & 1 deletion tests/testcoreutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
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
date_range, dt_to_int, compute_edges
)
from holoviews import Dimension, Element
from holoviews.streams import PointerXY
Expand Down Expand Up @@ -591,3 +591,26 @@ def test_date_range_1_sec(self):
drange = date_range(start, end, 10)
self.assertEqual(drange[0], start+np.timedelta64(50, 'ms'))
self.assertEqual(drange[-1], end-np.timedelta64(50, 'ms'))


class TestComputeEdges(ComparisonTestCase):
"""
Tests for compute_edges function.
"""

def setUp(self):
self.array1 = [.5, 1.5, 2.5]
self.array2 = [.5, 1.0000001, 1.5]
self.array3 = [1, 2, 4]

def test_simple_edges(self):
self.assertEqual(compute_edges(self.array1),
np.array([0, 1, 2, 3]))

def test_close_edges(self):
self.assertEqual(compute_edges(self.array2),
np.array([0.25, 0.75, 1.25, 1.75]))

def test_uneven_edges(self):
self.assertEqual(compute_edges(self.array3),
np.array([0.5, 1.5, 3.0, 5.0]))
16 changes: 8 additions & 8 deletions tests/testdataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -1264,14 +1264,6 @@ def setUp(self):
self.init_grid_data()
self.init_column_data()

def test_dataset_array_init_hm(self):
"Tests support for arrays (homogeneous)"
exception = "None of the available storage backends "\
"were able to support the supplied data format."
with self.assertRaisesRegexp(Exception, exception):
Dataset(np.column_stack([self.xs, self.xs_2]),
kdims=['x'], vdims=['x2'])

def test_dataset_dataframe_init_hm(self):
"Tests support for homogeneous DataFrames"
if pd is None:
Expand Down Expand Up @@ -1458,6 +1450,10 @@ def setUp(self):
self.init_column_data()
self.init_grid_data()

def test_dataset_array_init_hm(self):
"Tests support for arrays (homogeneous)"
raise SkipTest("Not supported")

# Disabled tests for NotImplemented methods
def test_dataset_add_dimensions_values_hm(self):
raise SkipTest("Not supported")
Expand Down Expand Up @@ -1544,6 +1540,10 @@ def test_xarray_dataset_with_scalar_dim_canonicalize(self):
expected = np.array([[0, 1], [2, 3], [4, 5]])
self.assertEqual(canonical, expected)

def test_dataset_array_init_hm(self):
"Tests support for arrays (homogeneous)"
raise SkipTest("Not supported")

# Disabled tests for NotImplemented methods
def test_dataset_add_dimensions_values_hm(self):
raise SkipTest("Not supported")
Expand Down
12 changes: 8 additions & 4 deletions tests/testelementconstructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,17 @@ def test_hist_yvalues_construct(self):

def test_hist_curve_construct(self):
hist = Histogram(Curve(([0.1, 0.3, 0.5], [2.1, 2.2, 3.3])))
self.assertEqual(hist.data[0], np.array([2.1, 2.2, 3.3]))
self.assertEqual(hist.data[1], np.array([0, 0.2, 0.4, 0.6]))
values = hist.dimension_values(1)
edges = hist.interface.coords(hist, hist.kdims[0], edges=True)
self.assertEqual(values, np.array([2.1, 2.2, 3.3]))
self.assertEqual(edges, np.array([0, 0.2, 0.4, 0.6]))

def test_hist_curve_int_edges_construct(self):
hist = Histogram(Curve(range(3)))
self.assertEqual(hist.data[0], np.arange(3))
self.assertEqual(hist.data[1], np.array([-.5, .5, 1.5, 2.5]))
values = hist.dimension_values(1)
edges = hist.interface.coords(hist, hist.kdims[0], edges=True)
self.assertEqual(values, np.arange(3))
self.assertEqual(edges, np.array([-.5, .5, 1.5, 2.5]))

def test_heatmap_construct(self):
hmap = HeatMap([('A', 'a', 1), ('B', 'b', 2)])
Expand Down
Loading

0 comments on commit dc1ca4b

Please sign in to comment.