Skip to content

Commit

Permalink
Merge 1ee50bd into 509d906
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Feb 3, 2019
2 parents 509d906 + 1ee50bd commit f622abd
Show file tree
Hide file tree
Showing 38 changed files with 191 additions and 136 deletions.
4 changes: 3 additions & 1 deletion holoviews/core/data/__init__.py
Expand Up @@ -428,7 +428,9 @@ def reindex(self, kdims=None, vdims=None):
data = self.interface.reindex(self, key_dims, val_dims)
datatype = self.datatype
if gridded and dropped:
datatype = [dt for dt in datatype if not self.interface.interfaces[dt].gridded]
interfaces = self.interface.interfaces
datatype = [dt for dt in datatype if not
getattr(interfaces.get(dt, None), 'gridded', True)]
return self.clone(data, kdims=key_dims, vdims=val_dims,
new_type=new_type, datatype=datatype)

Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/data/dask.py
Expand Up @@ -44,7 +44,7 @@ class DaskInterface(PandasInterface):

@classmethod
def loaded(cls):
return 'dask' in sys.modules
return 'dask' in sys.modules and 'pandas' in sys.modules

@classmethod
def applies(cls, obj):
Expand Down
5 changes: 4 additions & 1 deletion holoviews/core/data/dictionary.py
Expand Up @@ -60,7 +60,10 @@ def init(cls, eltype, data, kdims, vdims):
elif isinstance(data, list) and data == []:
data = OrderedDict([(d, []) for d in dimensions])
elif isinstance(data, list) and isscalar(data[0]):
data = {dimensions[0]: np.arange(len(data)), dimensions[1]: data}
if eltype._auto_indexable_1d:
data = {dimensions[0]: np.arange(len(data)), dimensions[1]: data}
else:
data = {dimensions[0]: data}
elif (isinstance(data, list) and isinstance(data[0], tuple) and len(data[0]) == 2
and any(isinstance(v, tuple) for v in data[0])):
dict_data = zip(*((util.wrap_tuple(k)+util.wrap_tuple(v))
Expand Down
14 changes: 11 additions & 3 deletions holoviews/core/util.py
Expand Up @@ -812,7 +812,7 @@ def isscalar(val):
"""
Value is scalar or None
"""
return val is None or np.isscalar(val)
return val is None or np.isscalar(val) or isinstance(val, datetime_types)


def isnumeric(val):
Expand Down Expand Up @@ -965,7 +965,12 @@ def range_pad(lower, upper, padding=None, log=False):
center = (log_min+log_max) / 2.0
start, end = np.power(10, center-lspan/2.), np.power(10, center+uspan/2.)
else:
span = (upper-lower)
if isinstance(lower, datetime_types) and not isinstance(lower, cftime_types):
# Ensure timedelta can be safely divided
lower, upper = np.datetime64(lower), np.datetime64(upper)
span = (upper-lower).astype('>m8[ns]')
else:
span = (upper-lower)
lpad = span*(padding[0])
upad = span*(padding[1])
start, end = lower-lpad, upper+upad
Expand Down Expand Up @@ -1391,7 +1396,7 @@ def is_dataframe(data):
Checks whether the supplied data is of DataFrame type.
"""
dd = None
if 'dask' in sys.modules:
if 'dask' in sys.modules and 'pandas' in sys.modules:
import dask.dataframe as dd
return((pd is not None and isinstance(data, pd.DataFrame)) or
(dd is not None and isinstance(data, dd.DataFrame)))
Expand Down Expand Up @@ -1874,6 +1879,9 @@ def dt_to_int(value, time_unit='us'):
elif isinstance(value, cftime_types):
return cftime_to_timestamp(value, time_unit)

if isinstance(value, dt.date):
value = dt.datetime(*value.timetuple()[:6])

# Handle datetime64 separately
if isinstance(value, np.datetime64):
try:
Expand Down
2 changes: 1 addition & 1 deletion holoviews/element/raster.py
Expand Up @@ -518,7 +518,7 @@ def sample(self, samples=[], **kwargs):
(coords, type(self).__name__, self.bounds.lbrt()))

data = self.interface.ndloc(self, (yidx, xidx))
return self.clone(data, new_type=Table, datatype=['dataframe', 'dict'])
return self.clone(data, new_type=Table, datatype=['dataframe', 'dictionary'])


def closest(self, coords=[], **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion holoviews/operation/element.py
Expand Up @@ -772,7 +772,7 @@ def _process_layer(self, element, key=None):
dtype = x.dtype
is_datetime = dtype.kind == 'M' or isinstance(x[0], datetime_types)
if is_datetime:
dt_type = dtype if dtype.kind == 'M' else 'datetime64[ns]'
dt_type = 'datetime64[ns]'
x = x.astype(dt_type).astype('int64')
dvals = tuple(element.dimension_values(d) for d in element.dimensions()[1:])
xs, dvals = INTERPOLATE_FUNCS[self.p.interpolation](x.astype('f'), dvals)
Expand Down
7 changes: 5 additions & 2 deletions holoviews/plotting/bokeh/element.py
Expand Up @@ -217,8 +217,10 @@ def _get_hover_data(self, data, element, dimensions=None):
dim = util.dimension_sanitizer(d.name)
if dim not in data:
data[dim] = element.dimension_values(d)
if isinstance(data[dim], np.ndarray) and data[dim].dtype.kind == 'M':
data[dim+'_dt_strings'] = [d.pprint_value(v) for v in data[dim]]
values = data[dim]
if (values.dtype.kind == 'M' or (
len(values) and isinstance(values[0], util.datetime_types))):
data[dim+'_dt_strings'] = [d.pprint_value(v) for v in values]

for k, v in self.overlay_dims.items():
dim = util.dimension_sanitizer(k.name)
Expand Down Expand Up @@ -653,6 +655,7 @@ def _update_range(self, axis_range, low, high, factors, invert, shared, log, str
elif (low == high and low is not None):
if isinstance(low, util.datetime_types):
offset = np.timedelta64(500, 'ms')
low, high = np.datetime64(low), np.datetime64(high)
low -= offset
high += offset
else:
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/bokeh/stats.py
Expand Up @@ -106,7 +106,7 @@ def _apply_transforms(self, element, data, ranges, style, group=None):
if isinstance(agg, Dimensioned):
element = agg
else:
element = element.clone([(element,)])
element = element.clone([(agg,)])
return super(BoxWhiskerPlot, self)._apply_transforms(element, data, ranges, style, group)

def _get_factors(self, element):
Expand Down
6 changes: 3 additions & 3 deletions holoviews/plotting/mpl/chart.py
Expand Up @@ -82,7 +82,7 @@ def get_data(self, element, ranges, style):
xs = element.dimension_values(0)
ys = element.dimension_values(1)
dims = element.dimensions()
if xs.dtype.kind == 'M':
if xs.dtype.kind == 'M' or (len(xs) and isinstance(xs[0], datetime_types)):
dimtype = element.get_dimension_type(0)
dt_format = Dimension.type_formatters.get(dimtype, '%Y-%m-%d %H:%M:%S')
dims[0] = dims[0](value_format=DateFormatter(dt_format))
Expand All @@ -91,7 +91,7 @@ def get_data(self, element, ranges, style):

def init_artists(self, ax, plot_args, plot_kwargs):
xs, ys = plot_args
if xs.dtype.kind == 'M':
if xs.dtype.kind == 'M' or (len(xs) and isinstance(xs[0], datetime_types)):
artist = ax.plot_date(xs, ys, '-', **plot_kwargs)[0]
else:
artist = ax.plot(xs, ys, **plot_kwargs)[0]
Expand Down Expand Up @@ -1189,7 +1189,7 @@ def get_data(self, element, ranges, style):
cols = []
for i, vs in enumerate((xs, ys)):
vs = np.array(vs)
if vs.dtype.kind == 'M' and i < len(dims):
if (vs.dtype.kind == 'M' or (len(vs) and isinstance(vs[0], datetime_types))) and i < len(dims):
dt_format = Dimension.type_formatters[np.datetime64]
dims[i] = dims[i](value_format=DateFormatter(dt_format))
vs = np.array([dt_to_int(v, 'D') for v in vs])
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/util.py
Expand Up @@ -736,7 +736,7 @@ def list_cmaps(provider=None, records=False, name=None, category=None, source=No
# cmap_info stores only non-reversed info, so construct
# suitable values for reversed version if appropriate
r=r._replace(name=aname)
if aname.endswith('_r') and (r.category is not 'Diverging'):
if aname.endswith('_r') and (r.category != 'Diverging'):
if r.bg=='light':
r=r._replace(bg='dark')
elif r.bg=='dark':
Expand Down
15 changes: 8 additions & 7 deletions holoviews/tests/core/data/base.py
Expand Up @@ -3,8 +3,7 @@
"""

import datetime
from nose.plugins.attrib import attr
from unittest import SkipTest
from unittest import SkipTest, skipIf

import numpy as np

Expand All @@ -21,6 +20,8 @@
except:
pd = None

pd_skip = skipIf(pd is None, "pandas is not available")



class DatatypeContext(object):
Expand Down Expand Up @@ -384,11 +385,13 @@ def test_dataset_get_array_by_dimension(self):
arr = self.dataset_hm.array(['x'])
self.assertEqual(arr, self.xs[:, np.newaxis])

@pd_skip
def test_dataset_get_dframe(self):
df = self.dataset_hm.dframe()
self.assertEqual(df.x.values, self.xs)
self.assertEqual(df.y.values, self.y_ints)

@pd_skip
def test_dataset_get_dframe_by_dimension(self):
df = self.dataset_hm.dframe(['x'])
self.assertEqual(df, pd.DataFrame({'x': self.xs}, dtype=df.dtypes[0]))
Expand Down Expand Up @@ -424,17 +427,15 @@ def init_column_data(self):
# Test the constructor to be supported by all interfaces supporting
# heterogeneous column types.

@pd_skip
def test_dataset_dataframe_init_ht(self):
"Tests support for heterogeneous DataFrames"
if pd is None:
raise SkipTest("Pandas not available")
dataset = Dataset(pd.DataFrame({'x':self.xs, 'y':self.ys}), kdims=['x'], vdims=['y'])
self.assertTrue(isinstance(dataset.data, self.data_type))

@pd_skip
def test_dataset_dataframe_init_ht_alias(self):
"Tests support for heterogeneous DataFrames"
if pd is None:
raise SkipTest("Pandas not available")
dataset = Dataset(pd.DataFrame({'x':self.xs, 'y':self.ys}),
kdims=[('x', 'X')], vdims=[('y', 'Y')])
self.assertTrue(isinstance(dataset.data, self.data_type))
Expand Down Expand Up @@ -496,7 +497,7 @@ def test_dataset_range_with_dimension_range(self):

# Operations

@attr(optional=1) # Uses pandas
@pd_skip
def test_dataset_redim_with_alias_dframe(self):
test_df = pd.DataFrame({'x': range(10), 'y': range(0,20,2)})
dataset = Dataset(test_df, kdims=[('x', 'X-label')], vdims=['y'])
Expand Down
2 changes: 0 additions & 2 deletions holoviews/tests/core/data/testdaskinterface.py
@@ -1,4 +1,3 @@
from nose.plugins.attrib import attr
from unittest import SkipTest

import numpy as np
Expand All @@ -14,7 +13,6 @@
from .testpandasinterface import PandasInterfaceTests


@attr(optional=1)
class DaskDatasetTest(PandasInterfaceTests):
"""
Test of the pandas DaskDataset interface.
Expand Down
12 changes: 7 additions & 5 deletions holoviews/tests/core/data/testgridinterface.py
Expand Up @@ -2,7 +2,7 @@

from collections import OrderedDict
from itertools import product
from unittest import SkipTest
from unittest import SkipTest, skipIf

import numpy as np
from holoviews.core.data import Dataset
Expand All @@ -14,6 +14,9 @@
except ImportError:
da = None

pd_skip = skipIf(pd is None, "pandas is not available")


from .base import (
GriddedInterfaceTests, InterfaceTests, HomogeneousColumnTests, DatatypeContext
)
Expand All @@ -28,20 +31,18 @@ class GridInterfaceTests(GriddedInterfaceTests, HomogeneousColumnTests, Interfac
data_type = (OrderedDict, dict)
element = Dataset

@pd_skip
def test_dataset_dataframe_init_hm(self):
"Tests support for homogeneous DataFrames"
if pd is None:
raise SkipTest("Pandas not available")
exception = "None of the available storage backends "\
"were able to support the supplied data format."
with self.assertRaisesRegexp(Exception, exception):
Dataset(pd.DataFrame({'x':self.xs, 'x2':self.xs_2}),
kdims=['x'], vdims=['x2'])

@pd_skip
def test_dataset_dataframe_init_hm_alias(self):
"Tests support for homogeneous DataFrames"
if pd is None:
raise SkipTest("Pandas not available")
exception = "None of the available storage backends "\
"were able to support the supplied data format."
with self.assertRaisesRegexp(Exception, exception):
Expand Down Expand Up @@ -432,6 +433,7 @@ def test_dataset_groupby_drop_dims_dynamic_with_vdim(self):
partial = ds.to(Dataset, kdims=['Val'], vdims=['Val2'], groupby='y', dynamic=True)
self.assertEqual(partial[19]['Val'], array[:, -1, :].T.flatten().compute())

@pd_skip
def test_dataset_get_dframe(self):
df = self.dataset_hm.dframe()
self.assertEqual(df.x.values, self.xs)
Expand Down
2 changes: 1 addition & 1 deletion holoviews/tests/core/data/testmultiinterface.py
Expand Up @@ -129,7 +129,7 @@ def test_multi_array_redim(self):
arrays = [np.column_stack([np.arange(i, i+2), np.arange(i, i+2)]) for i in range(2)]
mds = Path(arrays, kdims=['x', 'y'], datatype=['multitabular']).redim(x='x2')
for i, ds in enumerate(mds.split()):
self.assertEqual(ds, Path(arrays[i], kdims=['x2', 'y'], datatype=['dask']))
self.assertEqual(ds, Path(arrays[i], kdims=['x2', 'y']))

def test_multi_mixed_interface_raises(self):
arrays = [np.random.rand(10, 2) if j else {'x': range(10), 'y': range(10)}
Expand Down
2 changes: 0 additions & 2 deletions holoviews/tests/core/data/testpandasinterface.py
@@ -1,4 +1,3 @@
from nose.plugins.attrib import attr
from unittest import SkipTest

import numpy as np
Expand All @@ -18,7 +17,6 @@
from .base import HeterogeneousColumnTests, InterfaceTests


@attr(optional=1)
class PandasInterfaceTests(HeterogeneousColumnTests, InterfaceTests):
"""
Test for the PandasInterface.
Expand Down
6 changes: 0 additions & 6 deletions holoviews/tests/core/data/testxarrayinterface.py
@@ -1,6 +1,5 @@
import datetime as dt
from collections import OrderedDict
from nose.plugins.attrib import attr
from unittest import SkipTest

import numpy as np
Expand All @@ -21,7 +20,6 @@
from .testgridinterface import GridInterfaceTests


@attr(optional=1)
class XArrayInterfaceTests(GridInterfaceTests):
"""
Tests for xarray interface
Expand Down Expand Up @@ -233,7 +231,6 @@ def test_dataset_sample_hm_alias(self):



@attr(optional=1)
class DaskXArrayInterfaceTest(XArrayInterfaceTests):
"""
Tests for XArray interface wrapping dask arrays
Expand Down Expand Up @@ -290,7 +287,6 @@ def test_xarray_dataset_with_scalar_dim_canonicalize(self):



@attr(optional=1)
class Image_XArrayInterfaceTests(Image_ImageInterfaceTests):

datatype = 'xarray'
Expand Down Expand Up @@ -358,7 +354,6 @@ def test_dataarray_with_some_coords(self):
Image(xrarr, kdims=['x', 'y'])


@attr(optional=1)
class RGB_XArrayInterfaceTests(RGB_ImageInterfaceTests):

datatype = 'xarray'
Expand All @@ -369,7 +364,6 @@ def init_data(self):
self.rgb_array[:, :, 1], self.rgb_array[:, :, 2]))


@attr(optional=1)
class HSV_XArrayInterfaceTest(HSV_ImageInterfaceTests):

datatype = 'xarray'
Expand Down
2 changes: 1 addition & 1 deletion holoviews/tests/core/testndmapping.py
Expand Up @@ -132,7 +132,7 @@ def test_idxmapping_unsorted_clone(self):

def test_idxmapping_groupby_unsorted(self):
data = [(('B', 2), 1), (('C', 2), 2), (('A', 1), 3)]
grouped = MultiDimensionalMapping(data, sort=False, kdims=['X', 'Y']).groupby('Y')
grouped = NdMapping(data, sort=False, kdims=['X', 'Y']).groupby('Y')
self.assertEquals(grouped.keys(), [1, 2])
self.assertEquals(grouped.values()[0].keys(), ['A'])
self.assertEquals(grouped.last.keys(), ['B', 'C'])
Expand Down

0 comments on commit f622abd

Please sign in to comment.