Skip to content

Commit

Permalink
Improved testing
Browse files Browse the repository at this point in the history
  • Loading branch information
ianthomas23 committed Oct 13, 2023
1 parent fb4eb3f commit 662d4fb
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 46 deletions.
10 changes: 5 additions & 5 deletions holoviews/operation/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ def _process(self, element, key=None):
if any(data_is_datetime[:2]) and self.p.filled:
raise RuntimeError("Datetime spatial coordinates are not supported "
"for filled contour calculations.")

try:
from matplotlib.dates import num2date, date2num
except ImportError:
Expand Down Expand Up @@ -602,7 +603,6 @@ def _process(self, element, key=None):

if data_is_datetime[2]:
levels = date2num(levels)
# Should check isdatetime(levels) too?

crange = levels.min(), levels.max()
if self.p.filled:
Expand Down Expand Up @@ -639,14 +639,14 @@ def points_to_datetime(points):
outer_offsets = filled[2][0]
exteriors = []
interiors = []
for i in range(len(outer_offsets)-1): # Loop through exterior boundaries
jstart = outer_offsets[i] # Start boundary index
jend = outer_offsets[i+1] # End boundary index

# Loop through exterior polygon boundaries.
for jstart, jend in zip(outer_offsets[:-1], outer_offsets[1:]):
if exteriors:
exteriors.append(empty)
exteriors.append(points[offsets[jstart]:offsets[jstart + 1]])

# Loop over the (jend-jstart-1) interior boundaries,
# Loop over the (jend-jstart-1) interior boundaries.
interior = [points[offsets[j]:offsets[j + 1]] for j in range(jstart+1, jend)]
interiors.append(interior)
level = (lower_level + upper_level) / 2
Expand Down
144 changes: 103 additions & 41 deletions holoviews/tests/operation/test_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,17 @@ def test_image_gradient(self):
self.assertEqual(op_img, img.clone(np.array([[3.162278, 3.162278], [3.162278, 3.162278]]), group='Gradient'))

def test_image_contours(self):
img = Image(np.array([[0, 1, 0], [3, 4, 5.], [6, 7, 8]]))
img = Image(np.array([[0, 1, 0], [0, 1, 0]]))
op_contours = contours(img, levels=[0.5])
contour = Contours([[(-0.166667, 0.333333, 0.5), (-0.333333, 0.277778, 0.5),
(np.nan, np.nan, 0.5), (0.333333, 0.3, 0.5),
(0.166667, 0.333333, 0.5)]],
# Note multiple lines which are nan-separated.
contour = Contours([[(-0.166667, 0.25, 0.5), (-0.1666667, -0.25, 0.5),
(np.nan, np.nan, 0.5), (0.1666667, -0.25, 0.5),
(0.1666667, 0.25, 0.5)]],
vdims=img.vdims)
self.assertEqual(op_contours, contour)

def test_image_contours_empty(self):
img = Image(np.array([[0, 1, 0], [3, 4, 5.], [6, 7, 8]]))
img = Image(np.array([[0, 1, 0], [0, 1, 0]]))
# Contour level outside of data limits
op_contours = contours(img, levels=[23.0])
contour = Contours(None, vdims=img.vdims)
Expand All @@ -102,56 +103,91 @@ def test_image_contours_no_range(self):
self.assertEqual(op_contours, contour)

def test_image_contours_x_datetime(self):
x = np.array(['2023-09-01', '2023-09-02'], dtype='datetime64')
x = np.array(['2023-09-01', '2023-09-03', '2023-09-05'], dtype='datetime64')
y = [14, 15]
z = np.array([[0, 1], [1, 2]])
z = np.array([[0, 1, 0], [0, 1, 0]])
img = Image((x, y, z))
op_contours = contours(img, levels=[0.5])
expected_x = np.array([dt.datetime(2023, 9, 1, tzinfo=dt.timezone.utc),
dt.datetime(2023, 9, 1, 12, tzinfo=dt.timezone.utc)],
dtype=object)
np.testing.assert_array_equal(op_contours.dimension_values('x'), expected_x)
np.testing.assert_array_almost_equal(op_contours.dimension_values('y'), [14.5, 14.0])
np.testing.assert_array_almost_equal(op_contours.dimension_values('z'), [0.5, 0.5])
# Note multiple lines which are nan-separated.
tz = dt.timezone.utc
expected_x = np.array(
[dt.datetime(2023, 9, 2, tzinfo=tz), dt.datetime(2023, 9, 2, tzinfo=tz), np.nan,
dt.datetime(2023, 9, 4, tzinfo=tz), dt.datetime(2023, 9, 4, tzinfo=tz)],
dtype=object)

# Separately compare nans and datetimes
x = op_contours.dimension_values('x')
mask = np.array([True, True, False, True, True]) # Mask ignoring nans
np.testing.assert_array_equal(x[mask], expected_x[mask])
np.testing.assert_array_equal(x[~mask].astype(float), expected_x[~mask].astype(float))

np.testing.assert_array_almost_equal(op_contours.dimension_values('y').astype(float),
[15, 14, np.nan, 14, 15])
np.testing.assert_array_almost_equal(op_contours.dimension_values('z'), [0.5]*5)

def test_image_contours_y_datetime(self):
x = [14, 15]
y = np.array(['2023-09-01', '2023-09-02'], dtype='datetime64')
z = np.array([[0, 1], [1, 2]])
x = [14, 15, 16]
y = np.array(['2023-09-01', '2023-09-03'], dtype='datetime64')
z = np.array([[0, 1, 0], [0, 1, 0]])
img = Image((x, y, z))
op_contours = contours(img, levels=[0.5])
np.testing.assert_array_almost_equal(op_contours.dimension_values('x'), [14.0, 14.5])
expected_y = np.array([dt.datetime(2023, 9, 1, 12, tzinfo=dt.timezone.utc),
dt.datetime(2023, 9, 1, tzinfo=dt.timezone.utc)],
dtype=object)
np.testing.assert_array_equal(op_contours.dimension_values('y'), expected_y)
np.testing.assert_array_almost_equal(op_contours.dimension_values('z'), [0.5, 0.5])
# Note multiple lines which are nan-separated.
np.testing.assert_array_almost_equal(op_contours.dimension_values('x').astype(float),
[14.5, 14.5, np.nan, 15.5, 15.5])

tz = dt.timezone.utc
expected_y = np.array(
[dt.datetime(2023, 9, 3, tzinfo=tz), dt.datetime(2023, 9, 1, tzinfo=tz), np.nan,
dt.datetime(2023, 9, 1, tzinfo=tz), dt.datetime(2023, 9, 3, tzinfo=tz)],
dtype=object)

# Separately compare nans and datetimes
y = op_contours.dimension_values('y')
mask = np.array([True, True, False, True, True]) # Mask ignoring nans
np.testing.assert_array_equal(y[mask], expected_y[mask])
np.testing.assert_array_equal(y[~mask].astype(float), expected_y[~mask].astype(float))

np.testing.assert_array_almost_equal(op_contours.dimension_values('z'), [0.5]*5)

def test_image_contours_xy_datetime(self):
x = np.array(['2023-09-01', '2023-09-02'], dtype='datetime64')
x = np.array(['2023-09-01', '2023-09-03', '2023-09-05'], dtype='datetime64')
y = np.array(['2023-10-07', '2023-10-08'], dtype='datetime64')
z = np.array([[0, 1], [1, 2]])
z = np.array([[0, 1, 0], [0, 1, 0]])
img = Image((x, y, z))
op_contours = contours(img, levels=[0.5])
expected_x = np.array([dt.datetime(2023, 9, 1, tzinfo=dt.timezone.utc),
dt.datetime(2023, 9, 1, 12, tzinfo=dt.timezone.utc)],
dtype=object)
np.testing.assert_array_equal(op_contours.dimension_values('x'), expected_x)
expected_y = np.array([dt.datetime(2023, 10, 7, 12, tzinfo=dt.timezone.utc),
dt.datetime(2023, 10, 7, tzinfo=dt.timezone.utc)],
dtype=object)
np.testing.assert_array_equal(op_contours.dimension_values('y'), expected_y)
np.testing.assert_array_almost_equal(op_contours.dimension_values('z'), [0.5, 0.5])
# Note multiple lines which are nan-separated.

tz = dt.timezone.utc
expected_x = np.array(
[dt.datetime(2023, 9, 2, tzinfo=tz), dt.datetime(2023, 9, 2, tzinfo=tz), np.nan,
dt.datetime(2023, 9, 4, tzinfo=tz), dt.datetime(2023, 9, 4, tzinfo=tz)],
dtype=object)
expected_y = np.array(
[dt.datetime(2023, 10, 8, tzinfo=tz), dt.datetime(2023, 10, 7, tzinfo=tz), np.nan,
dt.datetime(2023, 10, 7, tzinfo=tz), dt.datetime(2023, 10, 8, tzinfo=tz)],
dtype=object)

# Separately compare nans and datetimes
x = op_contours.dimension_values('x')
mask = np.array([True, True, False, True, True]) # Mask ignoring nans
np.testing.assert_array_equal(x[mask], expected_x[mask])
np.testing.assert_array_equal(x[~mask].astype(float), expected_x[~mask].astype(float))

y = op_contours.dimension_values('y')
np.testing.assert_array_equal(y[mask], expected_y[mask])
np.testing.assert_array_equal(y[~mask].astype(float), expected_y[~mask].astype(float))

np.testing.assert_array_almost_equal(op_contours.dimension_values('z'), [0.5]*5)

def test_image_contours_z_datetime(self):
z = np.array([['2023-09-10', '2023-09-10'], ['2023-09-10', '2023-09-12']], dtype='datetime64')
img = Image(z)
op_contours = contours(img, levels=[np.datetime64('2023-09-11')])
np.testing.assert_array_almost_equal(op_contours.dimension_values('x'), [0.25, 0.0])
np.testing.assert_array_almost_equal(op_contours.dimension_values('y'), [0.0, -0.25])
expected_z = np.array([dt.datetime(2023, 9, 11, tzinfo=dt.timezone.utc),
dt.datetime(2023, 9, 11, tzinfo=dt.timezone.utc)],
dtype=object)
expected_z = np.array([
dt.datetime(2023, 9, 11, 0, 0, tzinfo=dt.timezone.utc),
dt.datetime(2023, 9, 11, 0, 0, tzinfo=dt.timezone.utc)], dtype=object)
np.testing.assert_array_equal(op_contours.dimension_values('z'), expected_z)

def test_qmesh_contours(self):
Expand Down Expand Up @@ -192,6 +228,16 @@ def test_qmesh_curvilinear_edges_contours(self):
self.assertEqual(op_contours, contour)

def test_image_contours_filled(self):
img = Image(np.array([[0, 2, 0], [0, 2, 0]]))
# Two polygons (nan-separated) without holes
op_contours = contours(img, filled=True, levels=[0.5, 1.5])
data = [[(-0.25, -0.25, 1), (-0.08333333, -0.25, 1), (-0.08333333, 0.25, 1),
(-0.25, 0.25, 1), (-0.25, -0.25, 1), (np.nan, np.nan, 1), (0.08333333, -0.25, 1),
(0.25, -0.25, 1), (0.25, 0.25, 1), (0.08333333, 0.25, 1), (0.08333333, -0.25, 1)]]
polys = Polygons(data, vdims=img.vdims[0].clone(range=(0.5, 1.5)))
self.assertEqual(op_contours, polys)

def test_image_contours_filled_with_hole(self):
img = Image(np.array([[0, 0, 0], [0, 1, 0.], [0, 0, 0]]))
# Single polygon with hole
op_contours = contours(img, filled=True, levels=[0.25, 0.75])
Expand All @@ -203,21 +249,37 @@ def test_image_contours_filled(self):
[0.08333333, 0.0], [0.0, -0.08333333]])]]]
np.testing.assert_array_almost_equal(op_contours.holes(), expected_holes)

def test_image_contours_filled_multi_holes(self):
img = Image(np.array([[0, 0, 0, 0, 0], [0, 1, 0, 1, 0], [0, 0, 0, 0, 0]]))
# Single polygon with two holes
op_contours = contours(img, filled=True, levels=[-0.5, 0.5])
data = [[(-0.4, -0.3333333, 0), (-0.2, -0.3333333, 0), (0, -0.3333333, 0),
(0.2, -0.3333333, 0), (0.4, -0.3333333, 0), (0.4, 0, 0), (0.4, 0.3333333, 0),
(0.2, 0.3333333, 0), (0, 0.3333333, 0), (-0.2, 0.3333333, 0), (-0.4, 0.3333333, 0),
(-0.4, 0, 0), (-0.4, -0.3333333, 0)]]
polys = Polygons(data, vdims=img.vdims[0].clone(range=(-0.5, 0.5)))
self.assertEqual(op_contours, polys)
expected_holes = [[[np.array([[-0.2, -0.16666667], [-0.3, 0], [-0.2, 0.16666667], [-0.1, 0],
[-0.2, -0.16666667]]),
np.array([[0.2, -0.16666667], [0.1, 0], [0.2, 0.16666667], [0.3, 0],
[0.2, -0.16666667]])]]]
np.testing.assert_array_almost_equal(op_contours.holes(), expected_holes)

def test_image_contours_filled_empty(self):
img = Image(np.array([[0, 1, 0], [3, 4, 5.], [6, 7, 8]]))
# Contour level outside of data limits
op_contours = contours(img, filled=True, levels=[20.0, 23.0])
polys = Polygons(None, vdims=img.vdims[0].clone(range=(20.0, 23.0)))
self.assertEqual(op_contours, polys)

def test_image_contours_filled_xy_datetime(self):
x = np.array(['2023-09-01', '2023-09-02', '2023-09-03'], dtype='datetime64')
y = np.array(['2023-10-07', '2023-10-08', '2023-10-09'], dtype='datetime64')
z = np.array([[0, 0, 0], [0, 1, 0.], [0, 0, 0]])
def test_image_contours_filled_x_datetime(self):
x = np.array(['2023-09-01', '2023-09-05', '2023-09-09'], dtype='datetime64')
y = np.array([6, 7])
z = np.array([[0, 2, 0], [0, 2, 0]])
img = Image((x, y, z))
msg = 'Datetime spatial coordinates are not supported for filled contour calculations.'
with pytest.raises(RuntimeError, match=msg):
_ = contours(img, filled=True, levels=[0.5, 2.0])
_ = contours(img, filled=True, levels=[0.5, 1.5])

def test_points_histogram(self):
points = Points([float(i) for i in range(10)])
Expand Down

0 comments on commit 662d4fb

Please sign in to comment.