def plot(self, nsamples=200): hv = ensure_holoviews() if self.space.n_dims > 1: raise ValueError("Can only plot 1D functions") bounds = self.space.bounds[0] if not self.Xi: p = hv.Scatter([]) * hv.Curve([]) * hv.Area([]) else: scatter = hv.Scatter(([p[0] for p in self.Xi], self.yi)) if self.models: model = self.models[-1] xs = np.linspace(*bounds, nsamples) xsp = self.space.transform(xs.reshape(-1, 1).tolist()) y_pred, sigma = model.predict(xsp, return_std=True) # Plot model prediction for function curve = hv.Curve((xs, y_pred)).opts(style=dict(line_dash="dashed")) # Plot 95% confidence interval as colored area around points area = hv.Area( (xs, y_pred - 1.96 * sigma, y_pred + 1.96 * sigma), vdims=["y", "y2"], ).opts(style=dict(alpha=0.5, line_alpha=0)) else: area = hv.Area([]) curve = hv.Curve([]) p = scatter * curve * area # Plot with 5% empty margins such that the boundary points are visible margin = 0.05 * (bounds[1] - bounds[0]) plot_bounds = (bounds[0] - margin, bounds[1] + margin) return p.redim(x=dict(range=plot_bounds))
def plot(self, n=None, tri_alpha=0): """Plot the function we want to learn, only works in 2D. Parameters ---------- n : int the number of boxes in the interpolation grid along each axis tri_alpha : float (0 to 1) Opacity of triangulation lines """ hv = ensure_holoviews() if self.vdim > 1: raise NotImplementedError( "holoviews currently does not support", "3D surface plots in bokeh." ) if self.ndim != 2: raise NotImplementedError( "Only 2D plots are implemented: You can " "plot a 2D slice with 'plot_slice'." ) x, y = self._bbox lbrt = x[0], y[0], x[1], y[1] if len(self.data) >= 4: if n is None: # Calculate how many grid points are needed. # factor from A=√3/4 * a² (equilateral triangle) scale_factor = np.product(np.diag(self._transform)) a_sq = np.sqrt(np.min(self.tri.volumes()) * scale_factor) n = max(10, int(0.658 / a_sq)) xs = ys = np.linspace(0, 1, n) xs = xs * (x[1] - x[0]) + x[0] ys = ys * (y[1] - y[0]) + y[0] z = self._ip()(xs[:, None], ys[None, :]).squeeze() im = hv.Image(np.rot90(z), bounds=lbrt) if tri_alpha: points = np.array( [self.tri.get_vertices(s) for s in self.tri.simplices] ) points = np.pad( points[:, [0, 1, 2, 0], :], pad_width=((0, 0), (0, 1), (0, 0)), mode="constant", constant_values=np.nan, ).reshape(-1, 2) tris = hv.EdgePaths([points]) else: tris = hv.EdgePaths([]) else: im = hv.Image([], bounds=lbrt) tris = hv.EdgePaths([]) im_opts = dict(cmap="viridis") tri_opts = dict(line_width=0.5, alpha=tri_alpha) no_hover = dict(plot=dict(inspection_policy=None, tools=[])) return im.opts(style=im_opts) * tris.opts(style=tri_opts, **no_hover)
def plot(self, *, scatter_or_line="scatter"): """Returns a plot of the evaluated data. Parameters ---------- scatter_or_line : str, default: "scatter" Plot as a scatter plot ("scatter") or a line plot ("line"). Returns ------- plot : `holoviews.Overlay` Plot of the evaluated data. """ if scatter_or_line not in ("scatter", "line"): raise ValueError("scatter_or_line must be 'scatter' or 'line'") hv = ensure_holoviews() xs, ys = zip(*sorted(self.data.items())) if self.data else ([], []) if scatter_or_line == "scatter": if self.vdim == 1: plots = [hv.Scatter((xs, ys))] else: plots = [hv.Scatter((xs, _ys)) for _ys in np.transpose(ys)] else: plots = [hv.Path((xs, ys))] # Put all plots in an Overlay because a DynamicMap can't handle changing # datatypes, e.g. when `vdim` isn't yet known and the live_plot is running. p = hv.Overlay(plots) # Plot with 5% empty margins such that the boundary points are visible margin = 0.05 * (self.bounds[1] - self.bounds[0]) plot_bounds = (self.bounds[0] - margin, self.bounds[1] + margin) return p.redim(x=dict(range=plot_bounds))
def plot(self): """Returns a plot of the evaluated data with error bars (not implemented for vector functions, i.e., it requires vdim=1). Returns ------- plot : `holoviews.element.Scatter * holoviews.element.ErrorBars * holoviews.element.Path` Plot of the evaluated data. """ hv = ensure_holoviews() if not self.data: p = hv.Scatter([]) * hv.ErrorBars([]) * hv.Path([]) elif not self.vdim > 1: xs, ys = zip(*sorted(self.data.items())) scatter = hv.Scatter(self.data) error = hv.ErrorBars([(x, self.data[x], self.error[x]) for x in self.data]) line = hv.Path((xs, ys)) p = scatter * error * line else: raise Exception("plot() not implemented for vector functions.") # Plot with 5% empty margins such that the boundary points are visible margin = 0.05 * (self.bounds[1] - self.bounds[0]) plot_bounds = (self.bounds[0] - margin, self.bounds[1] + margin) return p.redim(x=dict(range=plot_bounds))
def plot(self): hv = ensure_holoviews() ivals = sorted(self.ivals, key=attrgetter("a")) if not self.data: return hv.Path([]) xs, ys = zip(*[(x, y) for ival in ivals for x, y in sorted(ival.data.items())]) return hv.Path((xs, ys))
def plot(self): """Returns a histogram of the evaluated data. Returns ------- holoviews.element.Histogram A histogram of the evaluated data.""" hv = ensure_holoviews() vals = [v for v in self.data.values() if v is not None] if not vals: return hv.Histogram([[], []]) num_bins = int(max(5, sqrt(self.npoints))) vals = hv.Points(vals) return hv.operation.histogram(vals, num_bins=num_bins, dimension=1)
def plot_isoline(self, level=0.0, n=None, tri_alpha=0): """Plot the isoline at a specific level, only works in 2D. Parameters ---------- level : float, default: 0 The value of the function at which you would like to see the isoline. n : int The number of boxes in the interpolation grid along each axis. This is passed to `plot`. tri_alpha : float The opacity of the overlaying triangulation. This is passed to `plot`. Returns ------- `holoviews.core.Overlay` The plot of the isoline(s). This overlays a `plot` with a `holoviews.element.Path`. """ hv = ensure_holoviews() if n == -1: plot = hv.Path([]) else: plot = self.plot(n=n, tri_alpha=tri_alpha) if isinstance(level, Iterable): for l in level: plot = plot * self.plot_isoline(level=l, n=-1) return plot vertices, lines = self._get_iso(level, which="line") paths = [[vertices[i], vertices[j]] for i, j in lines] contour = hv.Path(paths) contour_opts = dict(color="black") contour = contour.opts(style=contour_opts) return plot * contour
def plot(self): """Returns a plot of the evaluated data. Returns ------- plot : `holoviews.element.Scatter` (if vdim=1)\ else `holoviews.element.Path` Plot of the evaluated data. """ hv = ensure_holoviews() xs, ys = zip(*sorted(self.data.items())) if self.data else ([], []) if self.vdim == 1: p = hv.Path([]) * hv.Scatter((xs, ys)) else: p = hv.Path((xs, ys)) * hv.Scatter([]) # Plot with 5% empty margins such that the boundary points are visible margin = 0.05 * (self.bounds[1] - self.bounds[0]) plot_bounds = (self.bounds[0] - margin, self.bounds[1] + margin) return p.redim(x=dict(range=plot_bounds))
def plot(self, cdims=None, plotter=None, dynamic=True): """Returns a DynamicMap with sliders. Parameters ---------- cdims : sequence of dicts, or (keys, iterable of values), optional Constant dimensions; the parameters that label the learners. Example inputs that all give identical results: - sequence of dicts: >>> cdims = [{'A': True, 'B': 0}, ... {'A': True, 'B': 1}, ... {'A': False, 'B': 0}, ... {'A': False, 'B': 1}]` - tuple with (keys, iterable of values): >>> cdims = (['A', 'B'], itertools.product([True, False], [0, 1])) >>> cdims = (['A', 'B'], [(True, 0), (True, 1), ... (False, 0), (False, 1)]) plotter : callable, optional A function that takes the learner as a argument and returns a holoviews object. By default ``learner.plot()`` will be called. dynamic : bool, default True Return a `holoviews.core.DynamicMap` if True, else a `holoviews.core.HoloMap`. The `~holoviews.core.DynamicMap` is rendered as the sliders change and can therefore not be exported to html. The `~holoviews.core.HoloMap` does not have this problem. Returns ------- dm : `holoviews.core.DynamicMap` (default) or `holoviews.core.HoloMap` A `DynamicMap` ``(dynamic=True)`` or `HoloMap` ``(dynamic=False)`` with sliders that are defined by `cdims`. """ hv = ensure_holoviews() cdims = cdims or self._cdims_default if cdims is None: cdims = [{'i': i} for i in range(len(self.learners))] elif not isinstance(cdims[0], dict): # Normalize the format keys, values_list = cdims cdims = [dict(zip(keys, values)) for values in values_list] mapping = { tuple(_cdims.values()): l for l, _cdims in zip(self.learners, cdims) } d = defaultdict(list) for _cdims in cdims: for k, v in _cdims.items(): d[k].append(v) def plot_function(*args): with suppress(KeyError): learner = mapping[tuple(args)] return learner.plot() if plotter is None else plotter(learner) dm = hv.DynamicMap(plot_function, kdims=list(d.keys())) dm = dm.redim.values(**d) if dynamic: return dm else: # XXX: change when https://github.com/ioam/holoviews/issues/3085 # is fixed. vals = {d.name: d.values for d in dm.dimensions() if d.values} return hv.HoloMap(dm.select(**vals))
def plot(self, n=None, tri_alpha=0): r"""Plot the Learner2D's current state. This plot function interpolates the data on a regular grid. The gridspacing is evaluated by checking the size of the smallest triangle. Parameters ---------- n : int Number of points in x and y. If None (default) this number is evaluated by looking at the size of the smallest triangle. tri_alpha : float The opacity ``(0 <= tri_alpha <= 1)`` of the triangles overlayed on top of the image. By default the triangulation is not visible. Returns ------- plot : `holoviews.core.Overlay` or `holoviews.core.HoloMap` A `holoviews.core.Overlay` of ``holoviews.Image * holoviews.EdgePaths``. If the `learner.function` returns a vector output, a `holoviews.core.HoloMap` of the `holoviews.core.Overlay`\s wil be returned. Notes ----- The plot object that is returned if ``learner.function`` returns a vector *cannot* be used with the live_plotting functionality. """ hv = ensure_holoviews() x, y = self.bounds lbrt = x[0], y[0], x[1], y[1] if len(self.data) >= 4: ip = self.interpolator(scaled=True) x, y, z = self.interpolated_on_grid(n) if self.vdim > 1: ims = { i: hv.Image(np.rot90(z[:, :, i]), bounds=lbrt) for i in range(z.shape[-1]) } im = hv.HoloMap(ims) else: im = hv.Image(np.rot90(z), bounds=lbrt) if tri_alpha: points = self._unscale(ip.tri.points[ip.tri.vertices]) points = np.pad( points[:, [0, 1, 2, 0], :], pad_width=((0, 0), (0, 1), (0, 0)), mode="constant", constant_values=np.nan, ).reshape(-1, 2) tris = hv.EdgePaths([points]) else: tris = hv.EdgePaths([]) else: im = hv.Image([], bounds=lbrt) tris = hv.EdgePaths([]) im_opts = dict(cmap="viridis") tri_opts = dict(line_width=0.5, alpha=tri_alpha) no_hover = dict(plot=dict(inspection_policy=None, tools=[])) return im.opts(style=im_opts) * tris.opts(style=tri_opts, **no_hover)
def plot(self, n=None, tri_alpha=0): r"""Plot the Learner2D's current state. This plot function interpolates the data on a regular grid. The gridspacing is evaluated by checking the size of the smallest triangle. Parameters ---------- n : int Number of points in x and y. If None (default) this number is evaluated by looking at the size of the smallest triangle. tri_alpha : float The opacity ``(0 <= tri_alpha <= 1)`` of the triangles overlayed on top of the image. By default the triangulation is not visible. Returns ------- plot : `holoviews.core.Overlay` or `holoviews.core.HoloMap` A `holoviews.core.Overlay` of ``holoviews.Image * holoviews.EdgePaths``. If the `learner.function` returns a vector output, a `holoviews.core.HoloMap` of the `holoviews.core.Overlay`\s wil be returned. Notes ----- The plot object that is returned if ``learner.function`` returns a vector *cannot* be used with the live_plotting functionality. """ hv = ensure_holoviews() x, y = self.bounds lbrt = x[0], y[0], x[1], y[1] if len(self.data) >= 4: ip = self.ip() if n is None: # Calculate how many grid points are needed. # factor from A=√3/4 * a² (equilateral triangle) n = int(0.658 / sqrt(areas(ip).min())) n = max(n, 10) # The bounds of the linspace should be (-0.5, 0.5) but because of # numerical precision problems it could (for example) be # (-0.5000000000000001, 0.49999999999999983), then any point at exact # boundary would be outside of the domain. See #181. eps = 1e-13 x = y = np.linspace(-0.5 + eps, 0.5 - eps, n) z = ip(x[:, None], y[None, :] * self.aspect_ratio).squeeze() if self.vdim > 1: ims = { i: hv.Image(np.rot90(z[:, :, i]), bounds=lbrt) for i in range(z.shape[-1]) } im = hv.HoloMap(ims) else: im = hv.Image(np.rot90(z), bounds=lbrt) if tri_alpha: points = self._unscale(ip.tri.points[ip.tri.vertices]) points = np.pad(points[:, [0, 1, 2, 0], :], pad_width=((0, 0), (0, 1), (0, 0)), mode='constant', constant_values=np.nan).reshape(-1, 2) tris = hv.EdgePaths([points]) else: tris = hv.EdgePaths([]) else: im = hv.Image([], bounds=lbrt) tris = hv.EdgePaths([]) im_opts = dict(cmap='viridis') tri_opts = dict(line_width=0.5, alpha=tri_alpha) no_hover = dict(plot=dict(inspection_policy=None, tools=[])) return im.opts(style=im_opts) * tris.opts(style=tri_opts, **no_hover)
def plot_slice(self, cut_mapping, n=None): """Plot a 1D or 2D interpolated slice of a N-dimensional function. Parameters ---------- cut_mapping : dict (int → float) for each fixed dimension the value, the other dimensions are interpolated. e.g. ``cut_mapping = {0: 1}``, so from dimension 0 ('x') to value 1. n : int the number of boxes in the interpolation grid along each axis """ hv = ensure_holoviews() plot_dim = self.ndim - len(cut_mapping) if plot_dim == 1: if not self.data: return hv.Scatter([]) * hv.Path([]) elif self.vdim > 1: raise NotImplementedError( "multidimensional output not yet supported by `plot_slice`" ) n = n or 201 values = [ cut_mapping.get(i, np.linspace(*self._bbox[i], n)) for i in range(self.ndim) ] ind = next(i for i in range(self.ndim) if i not in cut_mapping) x = values[ind] y = self._ip()(*values) p = hv.Path((x, y)) # Plot with 5% margins such that the boundary points are visible margin = 0.05 / self._transform[ind, ind] plot_bounds = (x.min() - margin, x.max() + margin) return p.redim(x=dict(range=plot_bounds)) elif plot_dim == 2: if self.vdim > 1: raise NotImplementedError( "holoviews currently does not support 3D surface plots in bokeh." ) if n is None: # Calculate how many grid points are needed. # factor from A=√3/4 * a² (equilateral triangle) scale_factor = np.product(np.diag(self._transform)) a_sq = np.sqrt(np.min(self.tri.volumes()) * scale_factor) n = max(10, int(0.658 / a_sq)) xs = ys = np.linspace(0, 1, n) xys = [xs[:, None], ys[None, :]] values = [ cut_mapping[i] if i in cut_mapping else xys.pop(0) * (b[1] - b[0]) + b[0] for i, b in enumerate(self._bbox) ] lbrt = [b for i, b in enumerate(self._bbox) if i not in cut_mapping] lbrt = np.reshape(lbrt, (2, 2)).T.flatten().tolist() if len(self.data) >= 4: z = self._ip()(*values).squeeze() im = hv.Image(np.rot90(z), bounds=lbrt) else: im = hv.Image([], bounds=lbrt) return im.opts(style=dict(cmap="viridis")) else: raise ValueError("Only 1 or 2-dimensional plots can be generated.")