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

Datetime improvements #2267

Merged
merged 4 commits into from
Jan 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions holoviews/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from datetime import date, datetime

from .boundingregion import * # noqa (API import)
from .data import * # noqa (API import)
from .dimension import * # noqa (API import)
Expand All @@ -24,6 +26,8 @@
Dimension.type_formatters[np.float32] = "%.5g"
Dimension.type_formatters[np.float64] = "%.5g"
Dimension.type_formatters[np.datetime64] = '%Y-%m-%d %H:%M:%S'
Dimension.type_formatters[datetime] = '%Y-%m-%d %H:%M:%S'
Dimension.type_formatters[date] = '%Y-%m-%d'

try:
import pandas as pd
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/data/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ def range(cls, dataset, dimension):
column = cls.coords(dataset, dimension, expanded=expanded, edges=True)
else:
column = dataset.dimension_values(dimension)
if dataset.get_dimension_type(dimension) is np.datetime64:
if column.dtype.kind == 'M':
return column.min(), column.max()
elif len(column) == 0:
return np.NaN, np.NaN
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/data/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def indexed(cls, dataset, selection):
@classmethod
def range(cls, dataset, dimension):
column = dataset.dimension_values(dimension)
if dataset.get_dimension_type(dimension) is np.datetime64:
if column.dtype.kind == 'M':
return column.min(), column.max()
elif len(column) == 0:
return np.NaN, np.NaN
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/dimension.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ def pprint_value(self, value):
if callable(formatter):
return formatter(value)
elif isinstance(formatter, basestring):
if isinstance(value, dt.datetime):
if isinstance(value, (dt.datetime, dt.date)):
return value.strftime(formatter)
elif isinstance(value, np.datetime64):
return dt64_to_dt(value).strftime(formatter)
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
except:
import builtins as builtins # noqa (compatibility)

datetime_types = (np.datetime64, dt.datetime)
datetime_types = (np.datetime64, dt.datetime, dt.date)
timedelta_types = (np.timedelta64, dt.timedelta,)

try:
Expand Down
24 changes: 14 additions & 10 deletions holoviews/plotting/bokeh/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,22 +292,26 @@ def _axes_props(self, plots, subplots, element, ranges):
if plots and self.shared_axes and not norm_opts.get('axiswise', False):
plot_ranges = self._merge_ranges(plots, xlabel, ylabel)

if el.get_dimension_type(0) in util.datetime_types:
x_axis_type = 'datetime'
else:
x_axis_type = 'log' if self.logx else 'auto'

if len(dims) > 1 and el.get_dimension_type(1) in util.datetime_types:
y_axis_type = 'datetime'
else:
y_axis_type = 'log' if self.logy else 'auto'

# Get the Element that determines the range and get_extents
range_el = el if self.batched and not isinstance(self, OverlayPlot) else element
l, b, r, t = self.get_extents(range_el, ranges)
if self.invert_axes:
l, b, r, t = b, l, t, r

xtype = el.get_dimension_type(0)
if ((xtype is np.object_ and type(l) in util.datetime_types) or
xtype in util.datetime_types):
x_axis_type = 'datetime'
else:
x_axis_type = 'log' if self.logx else 'auto'

y_axis_type = 'log' if self.logy else 'auto'
if len(dims) > 1:
ytype = el.get_dimension_type(1)
if ((ytype is np.object_ and type(b) in util.datetime_types)
or ytype in util.datetime_types):
y_axis_type = 'datetime'

# Declare shared axes
if 'x_range' in plot_ranges:
self._shared['x'] = True
Expand Down
40 changes: 30 additions & 10 deletions holoviews/plotting/bokeh/tabular.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from bokeh.models.widgets import DataTable, TableColumn

import param

from ...core import Dataset
from bokeh.models.widgets import (
DataTable, TableColumn, NumberEditor, NumberFormatter, DateFormatter,
TimeEditor, StringFormatter, StringEditor, IntEditor
)

from ...core import Dataset, Dimension
from ...element import ItemTable
from ...streams import Buffer
from ...core.util import dimension_sanitizer, datetime_types
from ..plot import GenericElementPlot
from .plot import BokehPlot

Expand Down Expand Up @@ -46,12 +50,8 @@ def _execute_hooks(self, element):


def get_data(self, element, ranges, style):
dims = element.dimensions()
mapping = {d.name: d.name for d in dims}
data = {d: element.dimension_values(d) for d in dims}
data = {d.name: values if values.dtype.kind in "if" else list(map(d.pprint_value, values))
for d, values in data.items()}
return data, mapping, style
return ({dimension_sanitizer(d.name): element.dimension_values(d)
for d in element.dimensions()}, {}, style)


def initialize_plot(self, ranges=None, plot=None, plots=None, source=None):
Expand All @@ -70,8 +70,28 @@ def initialize_plot(self, ranges=None, plot=None, plots=None, source=None):
source = self._init_datasource(data)
self.handles['source'] = source

columns = []
dims = element.dimensions()
columns = [TableColumn(field=d.name, title=d.pprint_label) for d in dims]
for d in dims:
col = dimension_sanitizer(d.name)
kind = data[col].dtype.kind
if kind == 'i':
formatter = NumberFormatter()
editor = IntEditor()
elif kind == 'f':
formatter = NumberFormatter(format='0,0.0[00000]')
editor = NumberEditor()
elif kind == 'M' or (kind == 'O' and type(data[col][0]) in datetime_types):
dimtype = element.get_dimension_type(0)
dformat = Dimension.type_formatters.get(dimtype, '%Y-%m-%d %H:%M:%S')
formatter = DateFormatter(format=dformat)
editor = TimeEditor()
else:
formatter = StringFormatter()
editor = StringEditor()
column = TableColumn(field=d.name, title=d.pprint_label,
editor=editor, formatter=formatter)
columns.append(column)
style['reorderable'] = False
table = DataTable(source=source, columns=columns, height=self.height,
width=self.width, **style)
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/bokeh/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ def date_to_integer(date):
date = dt64_to_dt(date)
elif pd and isinstance(date, pd.Timestamp):
date = date.to_pydatetime()
if isinstance(date, dt.datetime):
if isinstance(date, (dt.datetime, dt.date)):
dt_int = time.mktime(date.timetuple())*1000
else:
raise ValueError('Datetime type not recognized')
Expand Down
3 changes: 2 additions & 1 deletion holoviews/plotting/mpl/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ def get_data(self, element, ranges, style):
ys = element.dimension_values(1)
dims = element.dimensions()
if xs.dtype.kind == 'M':
dt_format = Dimension.type_formatters[np.datetime64]
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))
coords = (ys, xs) if self.invert_axes else (xs, ys)
return coords, style, {'dimensions': dims}
Expand Down