Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement dynamic relabel, redim and select methods #1029

Merged
merged 14 commits into from
Jan 7, 2017
Merged

Conversation

philippjfr
Copy link
Member

@philippjfr philippjfr commented Dec 27, 2016

As outlined in #422 this PR implements some further methods on DynamicMap, ensuring that the relabel and redim methods can be used correctly on a DynamicMap. Dynamic methods like this are easily implemented using the Dynamic utility, which currently lives in hv.util. Since this forces me to use inline imports I'm now considering moving Dynamic into core.

if (depth > 0) and getattr(obj, '_deep_indexable', False):
for k, v in obj.items():
obj[k] = v.relabel(group=group, label=label, depth=depth-1)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reimplemented this because assigning this way is inefficient for an internal operation.

@philippjfr philippjfr changed the title Implement dynamic relabel, map and redim methods Implement dynamic relabel and redim methods Dec 28, 2016
@philippjfr philippjfr added this to the v1.7.0 milestone Dec 28, 2016
@philippjfr philippjfr added tag: API type: feature A major new feature labels Dec 28, 2016
@philippjfr
Copy link
Member Author

Would like to get deep-select and __getitem__ into this PR before merge.

@philippjfr
Copy link
Member Author

@jlstevens This still needs a lot of unit tests for the select and __getitem__ methods, but the actual implementation is now ready for review.

@philippjfr philippjfr changed the title Implement dynamic relabel and redim methods Implement dynamic relabel, redim and select methods Jan 7, 2017
else self.ndims)
ndims = self.ndims
if any(d in self.vdims for d in kwargs):
ndims = len(self.kdims+self.vdims)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this because len(self.dimensions()) includes cdims?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because it can include the deep dimensions as well which should be processed below.

Copy link
Contributor

@jlstevens jlstevens Jan 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Makes sense...

@@ -28,14 +28,17 @@ class Dynamic(param.ParameterizedFunction):
kwargs = param.Dict(default={}, doc="""
Keyword arguments passed to the function.""")

shared_data = param.Boolean(default=False, doc="""
Whether the cloned DynamicMap will share the same data.""")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As .data for a DynamicMap is the callback, it would be good to explain what sharing/not sharing means here. I'm not quite sure what it means to be honest so it might be something we want to document in other places too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.data on a DynamicMap is not the callback but the cache. Will update the docstring to reflect that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, thanks! I suppose you might want to copy the cache in some cases (e.g for relabel).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, it's probably mostly going to be used internally.

@@ -655,7 +654,7 @@ def reset(self):
return self


def _cross_product(self, tuple_key, cache):
def _cross_product(self, tuple_key, cache, data_slice):
"""
Returns a new DynamicMap if the key (tuple form) expresses a
cross product, otherwise returns None. The cache argument is a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to explain the new data_slice argument in the docstring.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, docstrings still need updating. I'd love it if there was some way to inherit method docstrings easily, when you override a method.

@jlstevens
Copy link
Contributor

jlstevens commented Jan 7, 2017

Instead of the inline imports:

   from ..util import Dynamic

Maybe it is time we add core.dynamic which includes DynamicMap and Dynamic? Or would this just result in different import issues?

@philippjfr
Copy link
Member Author

philippjfr commented Jan 7, 2017

Maybe it is time we add core.dynamic which includes DynamicMap and Dynamic? Or would this just result in different import issues?

I think HoloMap.__mul__ would have to inline import it, but I think that may be still be preferable, so I'd vote for it.

self._cache(tuple_key, val)
return val


def select(self, selection_specs=None, **kwargs):
selection = super(DynamicMap, self).select(selection_specs, **kwargs)
def dynamic_select(obj):
Copy link
Contributor

@jlstevens jlstevens Jan 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would dynamic_select have any use on its own in util? It seems to offer a select that works with both dynamic and non dynamic objects...(or at least as an operation that can be used to select on dynamic objects).

elif key == ():
map_slice, data_slice = (), ()
else:
map_slice, data_slice = self._split_index(key)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance _split_index could support the empty tuple directly? What happens with an empty tuple currently? It would be nice if this bit could be:

if key is Ellipsis:
    return self
 map_slice, data_slice = self._split_index(key)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NdMapping.__getitem__ does this with empty tuples:

elif indexslice == () and not self.kdims:
     return self.data[()]

Won't work here unfortunately, but I guess I could still handle it in _split_index, NdMapping would just never use that path.

raise Exception("Slices must be used exclusively or not at all")
if slices:
return self._slice_bounded(tuple_key)
sliced = None
Copy link
Contributor

@jlstevens jlstevens Jan 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stray line that can be removed? (sliced=None)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, thanks.

from ..util import Dynamic
def dynamic_slice(obj):
return obj[data_slice]
return Dynamic(product, operation=dynamic_slice, shared_data=True)
Copy link
Contributor

@jlstevens jlstevens Jan 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget...is there a good reason why this can't be a lambda? (replacing dynamic_slice?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No probably could be.

@jlstevens
Copy link
Contributor

jlstevens commented Jan 7, 2017

I think HoloMap.mul would have to inline import it, but I think that may be still be preferable, so I'd vote for it.

Ok, go for it!

Edit: You can see I accidentally closed the PR after posting this!

@jlstevens jlstevens closed this Jan 7, 2017
@jlstevens jlstevens reopened this Jan 7, 2017
else:
from ..util import Dynamic
def dynamic_slice(obj):
return obj[data_slice]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can also be replaced with a lambda...

@jlstevens
Copy link
Contributor

Looks good!

I've finished making my current set of suggestions for this PR. I do think adding core.dynamic makes sense to do now as core.spaces is getting quite long...

@philippjfr
Copy link
Member Author

Ok, go for it!

I was wrong, the circular imports between dynamic.py and operation.py would be pretty bad.

@jlstevens
Copy link
Contributor

Shame... any other suggestions for breaking up this file and splitting the dynamic stuff out?

@philippjfr
Copy link
Member Author

Shame... any other suggestions for breaking up this file and splitting the dynamic stuff out?

Would have to think about it. Might open an issue.

@jlstevens
Copy link
Contributor

All looking good! Merging.

@jlstevens jlstevens merged commit 43774fc into master Jan 7, 2017
@philippjfr philippjfr deleted the dynamic_methods branch January 27, 2017 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tag: API type: feature A major new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants