diff --git a/holoviews/core/spaces.py b/holoviews/core/spaces.py index 33e9b0cb52..fdacae92fb 100644 --- a/holoviews/core/spaces.py +++ b/holoviews/core/spaces.py @@ -817,6 +817,22 @@ def _cache(self, key, val): self.data[key] = val + def map(self, map_fn, specs=None, clone=True): + """ + Recursively replaces elements using a map function when the + specification applies. Extends regular map with functionality + to dynamically apply functions. + """ + deep_mapped = super(DynamicMap, self).map(map_fn, specs, clone) + if isinstance(deep_mapped, type(self)): + from ..util import Dynamic + def apply_map(obj): + return obj.map(map_fn, specs, clone) + dmap = Dynamic(deep_mapped, shared_data=True, operation=apply_map) + return dmap + return deep_mapped + + def relabel(self, label=None, group=None, depth=1): """ Assign a new label and/or group to an existing LabelledData diff --git a/tests/testdynamic.py b/tests/testdynamic.py index 13c200a872..93998b2d47 100644 --- a/tests/testdynamic.py +++ b/tests/testdynamic.py @@ -1,7 +1,7 @@ from collections import deque import numpy as np -from holoviews import Dimension, NdLayout, GridSpace +from holoviews import Dimension, NdLayout, GridSpace, Layout from holoviews.core.spaces import DynamicMap, HoloMap, Callable from holoviews.element import Image, Scatter, Curve, Text from holoviews.streams import PositionXY, PositionX, PositionY @@ -85,6 +85,30 @@ def test_deep_select_slice_kdim_no_match(self): dmap = DynamicMap(fn, kdims=[Dimension('Test', range=(10, 20))]) self.assertEqual(dmap.select(DynamicMap, x=(5, 10))[10], fn(10)) + def test_deep_map_apply_element_function(self): + fn = lambda i: Curve(np.arange(i)) + dmap = DynamicMap(fn, kdims=[Dimension('Test', range=(10, 20))]) + mapped = dmap.map(lambda x: x.clone(x.data*2), Curve) + curve = fn(10) + self.assertEqual(mapped[10], curve.clone(curve.data*2)) + + def test_deep_map_apply_dmap_function(self): + fn = lambda i: Curve(np.arange(i)) + dmap1 = DynamicMap(fn, kdims=[Dimension('Test', range=(10, 20))]) + dmap2 = DynamicMap(fn, kdims=[Dimension('Test', range=(10, 20))]) + mapped = (dmap1 + dmap2).map(lambda x: x[10], DynamicMap) + self.assertEqual(mapped, Layout([('DynamicMap.I', fn(10)), + ('DynamicMap.II', fn(10))])) + + def test_deep_map_apply_dmap_function_no_clone(self): + fn = lambda i: Curve(np.arange(i)) + dmap1 = DynamicMap(fn, kdims=[Dimension('Test', range=(10, 20))]) + dmap2 = DynamicMap(fn, kdims=[Dimension('Test', range=(10, 20))]) + layout = (dmap1 + dmap2) + mapped = layout.map(lambda x: x[10], DynamicMap, clone=False) + self.assertIs(mapped, layout) + + class DynamicTestCallableBounded(ComparisonTestCase):