Skip to content

Commit

Permalink
Merge 0e0d98f into 27da6a3
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Feb 7, 2022
2 parents 27da6a3 + 0e0d98f commit 61ef3a4
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 99 deletions.
7 changes: 5 additions & 2 deletions holoviews/core/data/ibis.py
Expand Up @@ -34,8 +34,11 @@ def has_rowid(cls):

@classmethod
def is_rowid_zero_indexed(cls, data):
from ibis.client import find_backends, validate_backends
(backend,) = validate_backends(list(find_backends(data)))
try:
from ibis.client import find_backends, validate_backends
(backend,) = validate_backends(list(find_backends(data)))
except Exception:
backend = data._find_backend()
return type(backend).__module__ in cls.zero_indexed_backend_modules

@classmethod
Expand Down
14 changes: 11 additions & 3 deletions holoviews/core/data/xarray.py
Expand Up @@ -217,9 +217,17 @@ def retrieve_unit_and_label(dim):
# not need to be canonicalized
if any(len(da.coords[c].shape) > 1 for c in da.coords):
continue
undeclared = [
c for c in da.coords if c not in kdims and len(da[c].shape) == 1 and
da[c].shape[0] > 1]
undeclared = []
for c in da.coords:
if c in kdims or len(da[c].shape) != 1 or da[c].shape[0] <= 1:
# Skip if coord is declared, represents irregular coordinates or is constant
continue
elif all(d in kdims for d in da[c].dims):
continue # Skip if coord is alias for another dimension
elif any(all(d in da[kd.name].dims for d in da[c].dims) for kd in kdims):
# Skip if all the dims on the coord are present on another coord
continue
undeclared.append(c)
if undeclared:
raise DataError(
'The coordinates on the %r DataArray do not match the '
Expand Down
45 changes: 41 additions & 4 deletions holoviews/core/util.py
Expand Up @@ -1569,17 +1569,27 @@ def is_param_method(obj, has_deps=False):
def resolve_dependent_value(value):
"""Resolves parameter dependencies on the supplied value
Resolves parameter values, Parameterized instance methods and
parameterized functions with dependencies on the supplied value.
Resolves parameter values, Parameterized instance methods,
parameterized functions with dependencies on the supplied value,
including such parameters embedded in a list or tuple.
Args:
value: A value which will be resolved
Returns:
A new dictionary where any parameter dependencies have been
A new value where any parameter dependencies have been
resolved.
"""
range_widget = False
if isinstance(value, list):
value = [resolve_dependent_value(v) for v in value]
elif isinstance(value, tuple):
value = tuple(resolve_dependent_value(v) for v in value)
elif isinstance(value, dict):
value = {
resolve_dependent_value(k): resolve_dependent_value(v) for k, v in value.items()
}

if 'panel' in sys.modules:
from panel.widgets import RangeSlider, Widget
range_widget = isinstance(value, RangeSlider)
Expand Down Expand Up @@ -1614,7 +1624,7 @@ def resolve_dependent_kwargs(kwargs):
kwargs (dict): A dictionary of keyword arguments
Returns:
A new dictionary with where any parameter dependencies have been
A new dictionary where any parameter dependencies have been
resolved.
"""
return {k: resolve_dependent_value(v) for k, v in kwargs.items()}
Expand Down Expand Up @@ -2294,3 +2304,30 @@ def cast_array_to_int64(array):
category=FutureWarning,
)
return array.astype('int64')


def flatten(line):
"""
Flatten an arbitrarily nested sequence.
Inspired by: pd.core.common.flatten
Parameters
----------
line : sequence
The sequence to flatten
Notes
-----
This only flattens list, tuple, and dict sequences.
Returns
-------
flattened : generator
"""

for element in line:
if any(isinstance(element, tp) for tp in (list, tuple, dict)):
yield from flatten(element)
else:
yield element
2 changes: 1 addition & 1 deletion holoviews/operation/datashader.py
Expand Up @@ -2,7 +2,7 @@

import warnings

from collections import Callable
from collections.abc import Callable
from functools import partial

import param
Expand Down
16 changes: 9 additions & 7 deletions holoviews/plotting/bokeh/element.py
Expand Up @@ -2,6 +2,8 @@

import sys
import warnings

from itertools import chain
from types import FunctionType

import param
Expand Down Expand Up @@ -2317,13 +2319,13 @@ def _get_factors(self, overlay, ranges):
if el is not None:
elranges = util.match_spec(el, ranges)
xfs, yfs = sp._get_factors(el, elranges)
xfactors.append(xfs)
yfactors.append(yfs)
if xfactors:
xfactors = np.concatenate(xfactors)
if yfactors:
yfactors = np.concatenate(yfactors)
return util.unique_array(xfactors), util.unique_array(yfactors)
if len(xfs):
xfactors.append(xfs)
if len(yfs):
yfactors.append(yfs)
xfactors = list(util.unique_iterator(chain(*xfactors)))
yfactors = list(util.unique_iterator(chain(*yfactors)))
return xfactors, yfactors


def _get_axis_dims(self, element):
Expand Down
4 changes: 2 additions & 2 deletions holoviews/plotting/mpl/element.py
Expand Up @@ -174,7 +174,7 @@ def _finalize_axis(self, key, element=None, title=None, dimensions=None, ranges=
if self.logy:
axis.set_yscale('log')

if not isinstance(self.projection, str) and self.projection == '3d':
if not (isinstance(self.projection, str) and self.projection == '3d'):
self._set_axis_position(axis, 'x', self.xaxis)
self._set_axis_position(axis, 'y', self.yaxis)

Expand Down Expand Up @@ -330,7 +330,7 @@ def _set_axis_limits(self, axis, view, subplots, ranges):
coords = [coord if isinstance(coord, np.datetime64) or np.isreal(coord) else np.NaN for coord in extents]
coords = [date2num(util.dt64_to_dt(c)) if isinstance(c, np.datetime64) else c
for c in coords]
if isinstance(self.projection, str) and self.projection == '3d' or len(extents) == 6:
if (isinstance(self.projection, str) and self.projection == '3d') or len(extents) == 6:
l, b, zmin, r, t, zmax = coords
if self.invert_zaxis or any(p.invert_zaxis for p in subplots):
zmin, zmax = zmax, zmin
Expand Down
2 changes: 1 addition & 1 deletion holoviews/streams.py
Expand Up @@ -770,7 +770,7 @@ def hashkey(self):
for p in self.parameters:
pkey = (p.owner, p.name)
pname = self._rename.get(pkey, p.name)
key = ' '.join([p.owner.name, pname])
key = ' '.join([str(id(p.owner)), pname])
if self._rename.get(pkey, True) is not None:
hashkey[key] = getattr(p.owner, p.name)
hashkey['_memoize_key'] = self._memoize_counter
Expand Down
8 changes: 8 additions & 0 deletions holoviews/tests/core/data/testxarrayinterface.py
Expand Up @@ -60,6 +60,14 @@ def get_multi_dim_irregular_dataset(self):
'time': pd.date_range('2014-09-06', periods=3),
'reference_time': pd.Timestamp('2014-09-05')})

def test_ignore_dependent_dimensions_if_not_specified(self):
coords = OrderedDict([('time', [0, 1]), ('lat', [0, 1]), ('lon', [0, 1])])
da = xr.DataArray(np.arange(8).reshape((2, 2, 2)),
coords, ['time', 'lat', 'lon']).assign_coords(
lat1=xr.DataArray([2,3], dims=['lat']))
assert Dataset(da, ['time', 'lat', 'lon'], vdims='value').kdims == ['time', 'lat', 'lon']
assert Dataset(da, ['time', 'lat1', 'lon'], vdims='value').kdims == ['time', 'lat1', 'lon']

def test_xarray_dataset_irregular_shape(self):
ds = Dataset(self.get_multi_dim_irregular_dataset())
shape = ds.interface.shape(ds, gridded=True)
Expand Down
23 changes: 20 additions & 3 deletions holoviews/tests/core/testapply.py
@@ -1,12 +1,14 @@
import numpy as np
import pandas as pd
import param

from panel.widgets import TextInput
from panel.widgets import RadioButtonGroup, TextInput

from holoviews import Dataset, util
from holoviews.core.spaces import DynamicMap, HoloMap
from holoviews.element import Image, Curve
from holoviews.element import Curve, Image
from holoviews.element.comparison import ComparisonTestCase
from holoviews.streams import Params, ParamMethod
from holoviews.streams import ParamMethod, Params


class ParamClass(param.Parameterized):
Expand Down Expand Up @@ -275,3 +277,18 @@ def test_dmap_apply_dynamic_with_param_method(self):
self.assertEqual(applied[1], self.dmap[1].relabel('Test!'))
pinst.label = 'Another label'
self.assertEqual(applied[1], self.dmap[1].relabel('Another label!'))


def test_nested_widgets():
df = pd._testing.makeDataFrame()
column = RadioButtonGroup(value="A", options=list("ABC"))
ds = Dataset(df)
transform = util.transform.df_dim("*").groupby(["D", column]).mean()

params = list(transform.params.values())
assert len(params) == 1
assert params[0] == column.param.value

df1 = transform.apply(ds, keep_index=True, compute=False)
df2 = df.groupby(["D", "A"]).mean()
pd.testing.assert_frame_equal(df1, df2)
14 changes: 12 additions & 2 deletions holoviews/tests/plotting/bokeh/testoverlayplot.py
Expand Up @@ -3,15 +3,15 @@

from holoviews.core import NdOverlay, HoloMap, DynamicMap, Overlay
from holoviews.core.options import Cycle
from holoviews.element import Curve, Points, ErrorBars, Scatter, Text, VLine
from holoviews.element import Bars, Curve, ErrorBars, HLine, Points, Scatter, Text, VLine
from holoviews.streams import Stream, Tap
from holoviews.util import Dynamic

from ...utils import LoggingComparisonTestCase
from .testplot import TestBokehPlot, bokeh_renderer

try:
from bokeh.models import FixedTicker, HoverTool, FactorRange, Range1d
from bokeh.models import FixedTicker, HoverTool, FactorRange, Span, Range1d
except:
pass

Expand Down Expand Up @@ -192,6 +192,16 @@ def test_points_errorbars_text_ndoverlay_categorical_xaxis(self):
for xs, factor in zip(error_plot.handles['source'].data['base'], factors):
self.assertEqual(factor, xs)

def test_overlay_categorical_two_level(self):
bars = Bars([('A', 'a', 1), ('B', 'b', 2), ('A', 'b', 3), ('B', 'a', 4)],
kdims=['Upper', 'Lower'])

plot = bokeh_renderer.get_plot(bars * HLine(2))
x_range = plot.handles['x_range']
assert isinstance(x_range, FactorRange)
assert x_range.factors == [('A', 'a'), ('A', 'b'), ('B', 'a'), ('B', 'b')]
assert isinstance(plot.state.renderers[-1], Span)

def test_points_errorbars_text_ndoverlay_categorical_xaxis_invert_axes(self):
overlay = NdOverlay({i: Points(([chr(65+i)]*10,np.random.randn(10)))
for i in range(5)})
Expand Down

0 comments on commit 61ef3a4

Please sign in to comment.