-
-
Notifications
You must be signed in to change notification settings - Fork 402
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
Grid based interface fixes #794
Changes from 21 commits
5428392
a280914
4426274
4124f29
0079735
f8e4ede
33b316a
5fd7ef4
b80437c
a984d64
46bb8fa
98725de
5d08919
75031a5
9004db4
b29cd21
30196f1
cb461b1
4ecb993
2c8aeed
3b5b042
b557f84
3dbdbd4
6e8e46f
309bedc
1ee04d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,7 +56,8 @@ def init(cls, eltype, data, kdims, vdims): | |
data = {k: data[:,i] for i,k in enumerate(dimensions)} | ||
elif isinstance(data, list) and np.isscalar(data[0]): | ||
data = {dimensions[0]: np.arange(len(data)), dimensions[1]: data} | ||
elif not isinstance(data, dict): | ||
elif not any(isinstance(data, tuple(t for t in interface.types if t is not None)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just add a comment to say this is needed as dictionary can incorrectly accept xarray (duck typing fails here!). The comment might also mention that a cleaner approach would be welcome... |
||
for interface in cls.interfaces.values()): | ||
data = {k: v for k, v in zip(dimensions, zip(*data))} | ||
elif isinstance(data, dict) and not all(d in data for d in dimensions): | ||
dict_data = zip(*((util.wrap_tuple(k)+util.wrap_tuple(v)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,7 +71,7 @@ def init(cls, eltype, data, kdims, vdims): | |
if shape != expected[::-1] and not (not expected and shape == (1,)): | ||
raise ValueError('Key dimension values and value array %s ' | ||
'shape do not match. Expected shape %s, ' | ||
'actual shape: %s' % (vdim, expected, shape)) | ||
'actual shape: %s' % (vdim, expected[::-1], shape)) | ||
return data, {'kdims':kdims, 'vdims':vdims}, {} | ||
|
||
|
||
|
@@ -101,19 +101,80 @@ def length(cls, dataset): | |
return np.product([len(dataset.data[d.name]) for d in dataset.kdims]) | ||
|
||
|
||
@classmethod | ||
def get_coords(cls, dataset, dim, ordered=False): | ||
""" | ||
Returns the coordinates along a dimension. | ||
""" | ||
data = dataset.data[dim] | ||
if ordered and np.all(data[1:] < data[:-1]): | ||
data = data[::-1] | ||
return data | ||
|
||
|
||
@classmethod | ||
def expanded_coords(cls, dataset, dim): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't this be folded into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I've never been a fan of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, just coords is fine. |
||
arrays = [cls.get_coords(dataset, d.name, True) | ||
for d in dataset.kdims] | ||
idx = dataset.get_dimension_index(dim) | ||
return util.cartesian_product(arrays)[idx] | ||
|
||
|
||
@classmethod | ||
def canonicalize(cls, dataset, data, coord_dims=None): | ||
""" | ||
Canonicalize takes an array of values as input and | ||
reorients and transposes it to match the canonical | ||
format expected by plotting functions. In addition | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is probably too much of a pain to stick in the docstring but it would be good to describe our canonical format properly somewhere in our docs. Not something that should hold up this PR though! |
||
to the dataset and the particular array to apply | ||
transforms to a list of coord_dims may be supplied | ||
in case the array indexing does not match the key | ||
dimensions of the dataset. | ||
""" | ||
if coord_dims is None: | ||
coord_dims = dataset.dimensions('key', True) | ||
|
||
# Reorient data | ||
invert = False | ||
slices = [] | ||
for d in coord_dims: | ||
coords = cls.get_coords(dataset, d) | ||
if np.all(coords[1:] < coords[:-1]): | ||
slices.append(slice(None, None, -1)) | ||
invert = True | ||
else: | ||
slices.append(slice(None)) | ||
data = data.__getitem__(slices[::-1]) if invert else data | ||
|
||
# Transpose data | ||
dims = [name for name in coord_dims[::-1] | ||
if isinstance(cls.get_coords(dataset, name), np.ndarray)] | ||
dropped = [dims.index(d) for d in dims if d not in dataset.kdims] | ||
inds = [dims.index(kd.name) for kd in dataset.kdims] | ||
inds += dropped | ||
if inds: | ||
data = data.transpose(inds[::-1]) | ||
|
||
# Allow lower dimensional views into data | ||
if len(dataset.kdims) < 2: | ||
data = data.flatten() | ||
elif dropped: | ||
data = data.squeeze(axis=tuple(range(len(dropped)))) | ||
return data | ||
|
||
|
||
@classmethod | ||
def values(cls, dataset, dim, expanded=True, flat=True): | ||
if dim in dataset.kdims: | ||
if not expanded: | ||
return dataset.data[dim] | ||
prod = util.cartesian_product([dataset.data[d.name] for d in dataset.kdims]) | ||
idx = dataset.get_dimension_index(dim) | ||
values = prod[idx] | ||
return values.flatten() if flat else values | ||
else: | ||
if dim in dataset.vdims: | ||
dim = dataset.get_dimension(dim) | ||
values = dataset.data.get(dim.name) | ||
return values.T.flatten() if flat else values | ||
data = dataset.data.get(dim.name) | ||
data = cls.canonicalize(dataset, data) | ||
return data.T.flatten() if flat else data | ||
elif expanded: | ||
data = cls.expanded_coords(dataset, dim) | ||
return data.flatten() if flat else data | ||
else: | ||
return cls.get_coords(dataset, dim, True) | ||
|
||
|
||
@classmethod | ||
|
@@ -282,17 +343,17 @@ def reindex(cls, dataset, kdims, vdims): | |
if k not in dropped_kdims+dropped_vdims} | ||
|
||
if kdims != dataset.kdims: | ||
dropped_axes = tuple(dataset.ndims-dataset.kdims.index(d)-1 | ||
joined_dims = kdims+dropped_kdims | ||
axes = tuple(dataset.ndims-dataset.kdims.index(d)-1 | ||
for d in joined_dims) | ||
dropped_axes = tuple(dataset.ndims-joined_dims.index(d)-1 | ||
for d in dropped_kdims) | ||
old_kdims = [d for d in dataset.kdims if not d in dropped_kdims] | ||
axes = tuple(dataset.ndims-old_kdims.index(d)-1 | ||
for d in kdims) | ||
for vdim in vdims: | ||
vdata = data[vdim.name] | ||
if len(axes) > 1: | ||
vdata = vdata.transpose(axes[::-1]) | ||
if dropped_axes: | ||
vdata = vdata.squeeze(axis=dropped_axes) | ||
if len(axes) > 1: | ||
vdata = np.transpose(vdata, axes) | ||
data[vdim.name] = vdata | ||
return data | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,8 @@ | |
import param | ||
|
||
from ..core import util | ||
from ..core.data import (ArrayInterface, NdElementInterface, DictInterface) | ||
from ..core.data import (ArrayInterface, NdElementInterface, | ||
DictInterface, GridInterface) | ||
from ..core import (Dimension, NdMapping, Element2D, | ||
Overlay, Element, Dataset, NdElement) | ||
from ..core.boundingregion import BoundingRegion, BoundingBox | ||
|
@@ -396,6 +397,8 @@ def __init__(self, data, extents=None, **params): | |
|
||
|
||
def _compute_raster(self): | ||
if issubclass(self.interface, GridInterface): | ||
return self, np.flipud(self.dimension_values(2, flat=False)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps all interfaces could have a property to check to see if this return can be done directly instead of using |
||
d1keys = self.dimension_values(0, False) | ||
d2keys = self.dimension_values(1, False) | ||
coords = [(d1, d2, np.NaN) for d1 in d1keys for d2 in d2keys] | ||
|
@@ -648,16 +651,17 @@ class GridImage(Dataset, Element2D): | |
|
||
group = param.String(default='GridImage', constant=True) | ||
|
||
kdims = param.List(default=['x', 'y'], bounds=(2, 2)) | ||
kdims = param.List(default=[Dimension('x'), Dimension('y')], | ||
bounds=(2, 2)) | ||
|
||
vdims = param.List(default=['z'], bounds=(1, 1)) | ||
vdims = param.List(default=[Dimension('z')], bounds=(1, 1)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought strings were automatically promoted to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not as a default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at various examples in |
||
|
||
def __init__(self, data, **params): | ||
super(GridImage, self).__init__(data, **params) | ||
(l, r), (b, t) = self.interface.range(self, 0), self.interface.range(self, 1) | ||
(ys, xs) = self.dimension_values(2, flat=False).shape | ||
xsampling = (float(r-l)/xs)/2. | ||
ysampling = (float(t-b)/ys)/2. | ||
xsampling = (float(r-l)/(xs-1))/2. | ||
ysampling = (float(t-b)/(ys-1))/2. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was an off-by-one error in the bounds? How come this wasn't flagged up by the tests...or did you update the test data? If not, why isn't bounds being checked in the data comparisons? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was on GridImage, and is currently untested. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah ok, that makes sense. |
||
l, r = l-xsampling, r+xsampling | ||
b, t = b-ysampling, t+ysampling | ||
self.bounds = BoundingBox(points=((l, b), (r, t))) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -183,7 +183,7 @@ def update_handles(self, key, axis, element, ranges, style): | |
class ImagePlot(RasterPlot): | ||
|
||
def get_data(self, element, ranges, style): | ||
data = element.dimension_values(2, flat=False) | ||
data = np.flipud(element.dimension_values(2, flat=False)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How come this doesn't flip Edit: I now realize this isn't |
||
data = np.ma.array(data, mask=np.logical_not(np.isfinite(data))) | ||
vdim = element.vdims[0] | ||
self._norm_kwargs(element, ranges, style, vdim) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks right, but if I am not confused this is a separate fix from the rest of the PR? Looks like the kwargs weren't being passed into the group container when dynamic....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That caused weird bugs on the interfaces before I fixed the grid interfaces. So it's related at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to have the fix! I just wanted to make sure I understood it...