Skip to content

Commit

Permalink
Merged master
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Aug 17, 2019
2 parents aec6b7e + 343f28d commit 9295d20
Show file tree
Hide file tree
Showing 39 changed files with 1,199 additions and 758 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ jobs:
- &doc_build
<<: *default
stage: docs_dev
env: DESC="docs" CHANS_DEV="-c pyviz/label/dev" HV_DOC_GALLERY='false' HV_REQUIREMENTS="doc"
env: DESC="docs" CHANS_DEV="-c pyviz/label/dev" HV_DOC_GALLERY='false' HV_DOC_REF_GALLERY='false' HV_REQUIREMENTS="doc" PANEL_EMBED='true' PANEL_EMBED_JSON='true' PANEL_EMBED_JSON_PREFIX='json'
script:
- bokeh sampledata
- conda install -c conda-forge awscli
Expand Down Expand Up @@ -171,7 +171,7 @@ jobs:

- <<: *gallery_build
stage: gallery_daily
env: DESC="gallery" CHANS_DEV="-c pyviz/label/dev" HV_DOC_GALLERY='true' HV_REQUIREMENTS="doc" BUCKET="build."
env: DESC="gallery" CHANS_DEV="-c pyviz/label/dev" HV_DOC_GALLERY='false' HV_DOC_REF_GALLERY='false' HV_REQUIREMENTS="doc" BUCKET="build."

- <<: *doc_build
stage: docs
Expand Down
2 changes: 1 addition & 1 deletion doc/nbpublisher
37 changes: 36 additions & 1 deletion examples/user_guide/15-Large_Data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,42 @@
"source": [
"In all three of the above plots, `rasterize()` is being called to aggregate the data (a large set of x,y locations) into a rectangular grid, with each grid cell counting up the number of points that fall into it. In the plot on the left, only `rasterize()` is done, and the resulting numeric array of counts is passed to Bokeh for colormapping. Bokeh can then use dynamic (client-side, browser-based) operations in JavaScript, allowing users to have dynamic control over even static HTML plots. For instance, in this case, users can use the Box Select tool and select a range of the histogram shown, dynamically remapping the colors used in the plot to cover the selected range.\n",
"\n",
"The other two plots should be identical. In both cases, the numerical array output of `rasterize()` is mapped into RGB colors by Datashader itself, in Python (\"server-side\"), which allows special Datashader computations like the histogram-equalization in the above plots and the \"spreading\" discussed below. The `shade()` and `datashade()` operations accept a `cmap` argument that lets you control the colormap used, which can be selected to match the HoloViews/Bokeh `cmap` option but is strictly independent of it. See ``hv.help(rasterize)``, ``hv.help(shade)``, and ``hv.help(datashade)`` for options that can be selected, and the [Datashader web site](http://datashader.org) for all the details. You can also try the lower-level ``hv.aggregate()`` (for points and lines) and ``hv.regrid()` (for image/raster data) operations, which may provide more control."
"The other two plots should be identical. In both cases, the numerical array output of `rasterize()` is mapped into RGB colors by Datashader itself, in Python (\"server-side\"), which allows special Datashader computations like the histogram-equalization in the above plots and the \"spreading\" discussed below. The `shade()` and `datashade()` operations accept a `cmap` argument that lets you control the colormap used, which can be selected to match the HoloViews/Bokeh `cmap` option but is strictly independent of it. See ``hv.help(rasterize)``, ``hv.help(shade)``, and ``hv.help(datashade)`` for options that can be selected, and the [Datashader web site](http://datashader.org) for all the details. The lower-level `aggregate()` and `regrid()` give more control over how the data is aggregated.\n",
"\n",
"Since datashader only sends the data currently in view to the plotting backend, the default behavior is to rescale colormap to the range of the visible data as the zoom level changes. This behavior may not be desirable when working with images; to instead use a fixed colormap range, the `clim` parameter can be passed to the `bokeh` backend via the `opts()` method. Note that this approach works with `rasterize()` where the colormapping is done by the `bokeh` backend. With `datashade()`, the colormapping is done with the `shade()` function which takes a `clims` parameter directly instead of passing additional parameters to the backend via `opts()`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"n = 10_000\n",
"\n",
"# Strong signal on top\n",
"rs = np.random.RandomState(101010)\n",
"x = rs.pareto(n, n)\n",
"y = x + rs.standard_normal(n)\n",
"img1, *_ = np.histogram2d(x, y, bins=60)\n",
"\n",
"# Weak signal in the middle\n",
"x2 = rs.standard_normal(n)\n",
"y2 = 5 * x + 10 * rs.standard_normal(n)\n",
"img2, *_ = np.histogram2d(x2, y2, bins=60)\n",
"\n",
"img = img1 + img2\n",
"hv_img = hv.Image(img).opts(active_tools=['wheel_zoom'])\n",
"auto_scale_grid = rasterize(hv_img).opts(title='Automatic color range rescaling')\n",
"fixed_scale_grid = rasterize(hv_img).opts(title='Fixed color range', clim=(img.min(), img.max()))\n",
"auto_scale_grid + fixed_scale_grid; # Output supressed and gif shown below instead"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![](http://assets.holoviews.org/gifs/guides/user_guide/Large_Data/rasterize_color_range.gif)"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion examples/user_guide/17-Dashboards.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
"\n",
"smoothed = rolling(stock_dmap, rolling_window=rolling_window.param.value)\n",
"\n",
"pn.Row(pn.WidgetBox('## Stock Explorer', symbol, variable, window), smoothed.opts(width=500))"
"pn.Row(pn.WidgetBox('## Stock Explorer', symbol, variable, rolling_window), smoothed.opts(width=500))"
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions examples/user_guide/Plots_and_Renderers.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Rendering plots containing ``HoloMap`` and ``DynamicMap`` objects will automatically generate a widget class instance, which you can get a handle on with the ``get_widget`` method:"
"Rendering plots containing ``HoloMap`` and ``DynamicMap`` objects will automatically generate a Panel HoloViews pane which can be rendered in the notebook, saved or rendered as a server app:"
]
},
{
Expand All @@ -401,7 +401,7 @@
"metadata": {},
"outputs": [],
"source": [
"display_html(renderer.static_html(holomap), raw=True)"
"html = renderer.static_html(holomap)"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/data/dask.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class DaskInterface(PandasInterface):

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

@classmethod
def applies(cls, obj):
Expand Down
2 changes: 2 additions & 0 deletions holoviews/core/data/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ def groupby(cls, dataset, dim_names, container_type, group_type, **kwargs):

@classmethod
def key_select_mask(cls, dataset, values, ind):
if util.pd and values.dtype.kind == 'M':
ind = util.parse_datetime_selection(ind)
if isinstance(ind, tuple):
ind = slice(*ind)
if isinstance(ind, get_array_types()):
Expand Down
33 changes: 19 additions & 14 deletions holoviews/core/data/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,30 +291,35 @@ def select_mask(cls, dataset, selection):
have been selected.
"""
mask = np.ones(len(dataset), dtype=np.bool)
for dim, k in selection.items():
if isinstance(k, tuple):
k = slice(*k)
for dim, sel in selection.items():
if isinstance(sel, tuple):
sel = slice(*sel)
arr = cls.values(dataset, dim)
if isinstance(k, slice):
if util.isdatetime(arr) and util.pd:
try:
sel = util.parse_datetime_selection(sel)
except:
pass
if isinstance(sel, slice):
with warnings.catch_warnings():
warnings.filterwarnings('ignore', r'invalid value encountered')
if k.start is not None:
mask &= k.start <= arr
if k.stop is not None:
mask &= arr < k.stop
elif isinstance(k, (set, list)):
if sel.start is not None:
mask &= sel.start <= arr
if sel.stop is not None:
mask &= arr < sel.stop
elif isinstance(sel, (set, list)):
iter_slcs = []
for ik in k:
for ik in sel:
with warnings.catch_warnings():
warnings.filterwarnings('ignore', r'invalid value encountered')
iter_slcs.append(arr == ik)
mask &= np.logical_or.reduce(iter_slcs)
elif callable(k):
mask &= k(arr)
elif callable(sel):
mask &= sel(arr)
else:
index_mask = arr == k
index_mask = arr == sel
if dataset.ndims == 1 and np.sum(index_mask) == 0:
data_index = np.argmin(np.abs(arr - k))
data_index = np.argmin(np.abs(arr - sel))
mask = np.zeros(len(dataset), dtype=np.bool)
mask[data_index] = True
else:
Expand Down
27 changes: 26 additions & 1 deletion holoviews/core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1830,7 +1830,7 @@ def dt64_to_dt(dt64):
Safely converts NumPy datetime64 to a datetime object.
"""
ts = (dt64 - np.datetime64('1970-01-01T00:00:00')) / np.timedelta64(1, 's')
return dt.datetime.utcfromtimestamp(ts)
return dt.datetime(1970,1,1,0,0,0) + dt.timedelta(seconds=ts)


def is_nan(x):
Expand Down Expand Up @@ -1910,6 +1910,31 @@ def date_range(start, end, length, time_unit='us'):
return start+step/2.+np.arange(length)*step


def parse_datetime(date):
"""
Parses dates specified as string or integer or pandas Timestamp
"""
if pd is None:
raise ImportError('Parsing dates from strings requires pandas')
return pd.to_datetime(date).to_datetime64()


def parse_datetime_selection(sel):
"""
Parses string selection specs as datetimes.
"""
if isinstance(sel, basestring) or isdatetime(sel):
sel = parse_datetime(sel)
if isinstance(sel, slice):
if isinstance(sel.start, basestring) or isdatetime(sel.start):
sel = slice(parse_datetime(sel.start), sel.stop)
if isinstance(sel.stop, basestring) or isdatetime(sel.stop):
sel = slice(sel.start, parse_datetime(sel.stop))
if isinstance(sel, (set, list)):
sel = [parse_datetime(v) if isinstance(v, basestring) else v for v in sel]
return sel


def dt_to_int(value, time_unit='us'):
"""
Converts a datetime type to an integer with the supplied time unit.
Expand Down
18 changes: 2 additions & 16 deletions holoviews/element/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,7 @@ class Chart(Dataset, Element2D):
__abstract = True

def __getitem__(self, index):
sliced = super(Chart, self).__getitem__(index)
if not isinstance(sliced, Chart):
return sliced

if not isinstance(index, tuple): index = (index,)
ndims = len(self.extents)//2
lower_bounds, upper_bounds = [None]*ndims, [None]*ndims
for i, slc in enumerate(index[:ndims]):
if isinstance(slc, slice):
lbound = self.extents[i]
ubound = self.extents[ndims:][i]
lower_bounds[i] = lbound if slc.start is None else slc.start
upper_bounds[i] = ubound if slc.stop is None else slc.stop
sliced.extents = tuple(lower_bounds+upper_bounds)
return sliced
return super(Chart, self).__getitem__(index)


class Scatter(Chart):
Expand All @@ -69,7 +55,7 @@ class Scatter(Chart):
location along the x-axis while the first value dimension
represents the location of the point along the y-axis.
"""

group = param.String(default='Scatter', constant=True)


Expand Down
12 changes: 0 additions & 12 deletions holoviews/plotting/bokeh/bokehwidgets.css

This file was deleted.

68 changes: 0 additions & 68 deletions holoviews/plotting/bokeh/bokehwidgets.js

This file was deleted.

30 changes: 11 additions & 19 deletions holoviews/plotting/bokeh/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
from bokeh.models import Renderer, Title, Legend, ColorBar, tools
from bokeh.models.axes import CategoricalAxis, DatetimeAxis
from bokeh.models.formatters import (
FuncTickFormatter, TickFormatter, PrintfTickFormatter,
MercatorTickFormatter)
FuncTickFormatter, TickFormatter, MercatorTickFormatter)
from bokeh.models.mappers import (
LinearColorMapper, LogColorMapper, CategoricalColorMapper)
from bokeh.models.ranges import Range1d, DataRange1d, FactorRange
Expand All @@ -40,7 +39,7 @@
TOOL_TYPES, date_to_integer, decode_bytes, get_tab_title,
glyph_order, py2js_tickformatter, recursive_model_update,
theme_attr_json, cds_column_replace, hold_policy, match_dim_specs,
compute_layout_properties)
compute_layout_properties, wrap_formatter)



Expand Down Expand Up @@ -631,18 +630,7 @@ def _axis_properties(self, axis, key, plot, dimension=None,

formatter = self.xformatter if axis == 'x' else self.yformatter
if formatter:
if isinstance(formatter, TickFormatter):
pass
elif isinstance(formatter, FunctionType):
msg = ('%sformatter could not be '
'converted to tick formatter. ' % axis)
jsfunc = py2js_tickformatter(formatter, msg)
if jsfunc:
formatter = FuncTickFormatter(code=jsfunc)
else:
formatter = None
else:
formatter = PrintfTickFormatter(format=formatter)
formatter = wrap_formatter(formatter, axis)
if formatter is not None:
axis_props['formatter'] = formatter
elif FuncTickFormatter is not None and ax_mapping and isinstance(dimension, Dimension):
Expand Down Expand Up @@ -1556,6 +1544,10 @@ class ColorbarPlot(ElementPlot):
User-specified colorbar axis range limits for the plot, as a tuple (low,high).
If specified, takes precedence over data and dimension ranges.""")

cformatter = param.ClassSelector(
default=None, class_=(util.basestring, TickFormatter, FunctionType), doc="""
Formatter for ticks along the colorbar axis.""")

colorbar = param.Boolean(default=False, doc="""
Whether to display a colorbar.""")

Expand Down Expand Up @@ -1608,6 +1600,10 @@ def _draw_colorbar(self, plot, color_mapper, prefix=''):

if self.clabel:
self.colorbar_opts.update({'title': self.clabel})

if self.cformatter is not None:
self.colorbar_opts.update({'formatter': wrap_formatter(self.cformatter, 'c')})

opts = dict(cbar_opts['opts'], color_mapper=color_mapper, ticker=ticker,
**self._colorbar_defaults)
color_bar = ColorBar(**dict(opts, **self.colorbar_opts))
Expand Down Expand Up @@ -1871,10 +1867,6 @@ class OverlayPlot(GenericOverlayPlot, LegendPlot):
'margin', 'aspect', 'data_aspect', 'frame_width',
'frame_height', 'responsive']

def __init__(self, overlay, **params):
super(OverlayPlot, self).__init__(overlay, **params)
self.set_root(params.pop('root', None))

def _process_legend(self):
plot = self.handles['plot']
subplots = self.traverse(lambda x: x, [lambda x: x is not self])
Expand Down
Loading

0 comments on commit 9295d20

Please sign in to comment.