def _get_points(self): embeddings = self.embeddings classes = self.classes if (self.label_flag) and (classes is not None): data = pd.DataFrame(embeddings) data.columns = ['ivis 1', 'ivis 2'] data['label'] = classes num_ks = len(np.unique(classes)) color_key = list(enumerate(Sets1to3[0:num_ks])) embed = { k: hv.Points(data.values[classes == k, :], ['ivis 1', 'ivis 2'], 'k', label=str(k)).opts(color=v, size=0) for k, v in color_key } dse = dynspread( datashade(hv.NdOverlay(embed, kdims=['k']), aggregator=ds.by('k', ds.count()))) color_points = hv.NdOverlay({ k: hv.Points([0, 0]).opts(color=v, size=0) for k, v in color_key }) points = color_points * dse else: points = datashade(hv.Points(embeddings)) points.opts(height=400, width=500, xaxis=None, yaxis=None) return points
def plot_arms_error(self, start_time=None, interval=None, lookahead=0, lookbehind=0): error_plots = {} real_pos_plots = {} joint_pos_plots = {} for arm, arm_table in self.arm_error_tables.items(): if not (start_time is None or interval is None): #arm_table = arm_table[start_time: start_time+interval] pass if len(arm_table) == 0: # dummy table arm_table = hv.Table([[start_time,self.max_pos,0,0]], kdims=['time'], vdims=['real_pos','plt_error_down','plt_error_up']) error_plots[arm] = hv.ErrorBars(arm_table, kdims='time', vdims=['real_pos', 'plt_error_down', 'plt_error_up'], extents=(start_time, self.min_pos, start_time+interval, self.max_pos)) error_plots[arm].redim(time={'range': (start_time, start_time+interval)}) real_pos_plots[arm] = arm_table.to.points(kdims=['time', 'real_pos'], vdims=['real_pos'], extents=(start_time, self.min_pos, start_time+interval, self.max_pos)) #joint_pos_plots[arm] = real_pos_plots[arm] * error_plots[arm] errorbar_overlay = hv.NdOverlay(error_plots, kdims='arm') errorbars = hv.NdOverlay(error_plots, kdims='arm') points = hv.NdOverlay(real_pos_plots, kdims='arm') return errorbars * points
def _create_plot_object( self, components=None, group_title="fit", invert_dq=False, invert_res=False, width=500, height=500, size=8, ): if self.invert_dq: y = -self.y else: y = self.y i = 1 if self.invert_res: i = -1 logging.info("-> creating plot object") raw = hv.Points((self.x, y), label="raw", group=group_title).opts( width=width, height=height, size=size, alpha=0.3, xlabel="Voltage", ylabel="dQ/dv", ) if components is not None: logging.info("-> components are not None") prt = {} for key in components: if not key.endswith("Scale"): prt[key] = hv.Curve((self.x, i * components[key]), group=group_title) return raw * hv.NdOverlay(prt) prt = { "init": hv.Curve((self.x, i * self.result.init_fit), group=group_title).opts(alpha=0.5, tools=["hover"]), "best": hv.Curve((self.x, i * self.result.best_fit), group=group_title).opts(tools=["hover"]), } parts = self.result.eval_components(x=self.x) if not parts: logging.info("-> no parts extracted") s = ( self.peaks_object.scale ) # set this to 1 if you dont want to use the scale factor when plotting for key in parts: if not key.endswith("Scale"): logging.info(f"-> adding {key} to the plot (scaled)") prt[key] = hv.Curve((self.x, i * s * parts[key]), group=group_title).opts(tools=["hover"]) return raw * hv.NdOverlay(prt)
def scatter_dist_by_mappings(dataset, x_kdims, y_kdims, mappings, selection_dim="Gene", datashade_=False, dynspread_=False, ): data_groups = {name: dataset.sel({selection_dim: genes}) for name, genes in mappings.items()} data_group_dfs = {k: v[[x_kdims, y_kdims]].to_dataframe() for k, v in data_groups.items()} points = {k: hv.Points(val, kdims=[x_kdims, y_kdims]) for k, val in data_group_dfs.items()} dist_x = {k: univariate_kde(hv.Distribution(p, kdims=[y_kdims], group="dist_x"), n_samples=1000) for k, p in points.items()} dist_y = {k: univariate_kde(hv.Distribution(p, kdims=[x_kdims], group="dist_y"), n_samples=1000) for k, p in points.items()} if datashade_: points_overlay = datashade(hv.NdOverlay(points)) if dynspread_: points_overlay = dynspread(points_overlay) else: points_overlay = hv.NdOverlay(points) return points_overlay << hv.NdOverlay(dist_x) << hv.NdOverlay(dist_y)
def roi_curves(data): if not data or not any(len(d) for d in data.values()): return hv.NdOverlay({0: hv.Curve([], 'Time', 'Fluorescence')}) curves = {} data = zip(data['x0'], data['x1'], data['y0'], data['y1']) for i, (x0, x1, y0, y1) in enumerate(data): selection = ds.select(x=(x0, x1), y=(y0, y1)) curves[i] = hv.Curve(selection.aggregate('Time', np.mean)) return hv.NdOverlay(curves)
def roi_curves(data): if not data or not any(len(d) for d in data.values()): return hv.NdOverlay({0: hv.Curve([], 'ens', 'coefs')}) curves = {} data = zip(data['x0'], data['x1'], data['y0'], data['y1']) for i, (x0, x1, y0, y1) in enumerate(data): selection = ds.select(x=(x0, x1), y=(y1, y0)) # swap y0 and y1 when inverted y-axis curves[i] = hv.Spread(selection.aggregate('ens', np.mean, np.std)) return hv.NdOverlay(curves)
def _holoviews_get_metrics(self, color_map: dict = {}, label_map: dict = {}): """ Creates holoviews plot for every not node specific metric over time Returns: holoviews.HoloMap: holoviews object representing plot """ metric_names = [ name for name in next(iter( self._calculated_networks.values())).graph.keys() ] curve_dict = {} for metric_name in metric_names: name = label_map.get(metric_name, metric_name) curve_dict[name] = hv.Curve( (list(self._calculated_networks.keys()), list( map(lambda x: x.graph[metric_name], self._calculated_networks.values()))), kdims='Time', vdims='Value') if metric_name in color_map: curve_dict[name].opts(color=color_map[metric_name]) ndoverlay = hv.NdOverlay(curve_dict) distribution = hv.HoloMap({i: (ndoverlay * hv.VLine(i)).relabel(group='Metrics') for i in self._calculated_networks.keys()}, kdims='Time')\ .opts(width=400, height=400, padding=0.1) return distribution
def to_ndoverlay(self, colors=None, labels=None): """ Fetches the vector tile (from python cache or from the local disk or from the web service <- search order) and returns a NdOverlay of Shape Elements with a numeric index kwargs: - colors (iterable of color values, or str indicating a colormap definted in holoviews) : Used to generate a itertools.cycle to cycle through color values. : Default is 'Category20' eg: color=bokeh.palettes.Category20_10 For hv.Cycle('colomap_name')'s usage, refer to: http://holoviews.org/user_guide/Style_Mapping.html """ gdf = self.to_gdf() colors = colors or 'Category20' # return ndoverlay of each shape ndoverlay = hv.NdOverlay({ i: gv.Shape(geom).opts(fill_color=hv.Cycle(colors)) for (i, geom) in enumerate(gdf.geometry) }) # if labels is not None: # ndoverlay = relabel_children(ndoverlay, labels) return ndoverlay
def select_series(boundsy): test = df.columns.values[0] == boundsy[0] def plot_series(df, i): data = {"x": df.index.values, "y": df.iloc[:, i].values} return hv.Area(data) #* hv.Curve(data).opts(tools=["hover"]) low, high = boundsy low, high = int(np.floor(low)), int(np.ceil(high)) overlay = hv.NdOverlay({ df.columns[i]: hv.Area(df.iloc[:, i].values) #* hv.Curve(df.iloc[:, i].values).opts(tools=["hover"])) for i in range(low, high) }).opts(legend_position='left', fontsize={'legend': '6pt'}) ticks = list( zip(range(len(df.index)), [pd.to_datetime(x).strftime("%Y-%m-%d") for x in df.index])) return hv.Area.stack( overlay.opts( xticks=ticks[::len(df.index) // 15], ylim=( 0, None ), #df.iloc[:, list(range(low,high))].values.max()*1.05), xlabel="Date", ylabel="Commits", xrotation=45))
def plot_decision_boundaries(cls, model, X, class_names: str = None, dim_x: int = 0, dim_y: int = 1): """Plot the decission boundaries of a classification model.""" min_x = cls.safe_bound(X[:, dim_x].min(), low=True, pct=0.075) max_x = cls.safe_bound(X[:, dim_y].max(), low=False, pct=0.075) plots = {} for i, (intercept, coef) in enumerate( zip(model.intercept_.tolist(), model.coef_.tolist())): # getting the x co-ordinates of the decision boundary theta = np.concatenate([[intercept], coef]) plot_x = np.array([min_x, max_x]) # getting corresponding y co-ordinates of the decision boundary plot_y = (-1 / coef[dim_y]) * ( theta[dim_x] * plot_x + intercept ) # Plotting the Single Line Decision # Boundary data = pd.DataFrame({"x": plot_x, "y": plot_y}) boundary = hv.Curve(data).opts(line_width=5) name = "class_{}".format( i) if class_names is None else class_names[i] plots[name] = boundary return hv.NdOverlay(plots, kdims="Decision boundaries")
def dist_compare_plot(seq1, seq2, bins=None, max_bar_categories: int = 40): plot_object = None if (is_numeric_dtype(seq1) and is_numeric_dtype(seq2) and len(np.unique(np.concatenate((seq1, seq2)))) > 4): if bins is None: bins = bin_it(np.concatenate((seq1.values, seq2.values), axis=0)) frequencies1, edges1 = np.histogram(seq1.dropna(), bins, density=True) frequencies2, edges2 = np.histogram(seq2.dropna(), bins, density=True) plot_object = hv.NdOverlay({ "df1": hv.Histogram((edges1, frequencies1)), "df2": hv.Histogram((edges2, frequencies2)), }) plot_object.opts("Histogram", fill_alpha=0.5).redim.label(x="Value") else: plot_df_1 = pd.DataFrame(seq1.value_counts() / len(seq1)) plot_df_1["Data frame"] = "df1" plot_df_2 = pd.DataFrame(seq2.value_counts() / len(seq2)) plot_df_2["Data frame"] = "df2" plot_df = pd.concat([plot_df_1, plot_df_2]).reset_index() plot_df.columns = ["Category", "Count", "Source"] if len(plot_df.Category.unique()) < max_bar_categories: plot_object = hv.Bars(plot_df, ["Category", "Source"], "Count") else: train_levels = plot_df_1.index.unique() test_levels = plot_df_2.index.unique() plot_object = hv.Bars(( ["Only df1", "In both", "Only df2"], [ len([t for t in train_levels if t not in test_levels]), len([t for t in train_levels if t in test_levels]), len([t for t in test_levels if t not in train_levels]), ], )).opts(invert_axes=True) return plot_object
def roi_curves(self, data): """ :param data: :return: """ if not data or not any(len(d) for d in data.values()): return hv.NdOverlay( {0: hv.Curve([], self.spectral_axis_name, 'Reflectance')}) curves = {} data = zip(data['x0'], data['x1'], data['y0'], data['y1']) for i, (x0, x1, y0, y1) in enumerate(data): selection = self.ds.select(x=(x0, x1), y=(y0, y1)) curves[i] = hv.Curve( selection.aggregate(self.spectral_axis_name, np.mean)) return hv.NdOverlay(curves)
def plot_response(obj, **args): spatial = args.get('spatial', False) if not spatial: idx = [i for i in range(len(obj.spikes)) if len(obj.spikes[i] > 0)] spikes = dict() for i in range(len(idx)): s = hv.Spikes(obj.spikes[idx[i]], kdims=['Time']) spikes[i] = s.opts(plot=dict(position=0.1 * i, spike_length=0.1), style=dict(color=tuple(Afferent.affcol[ obj.aff.afferents[idx[i]].affclass]))) hvobj = hv.NdOverlay(spikes).opts(plot=dict(yaxis=None)) else: scale = args.get('scale', True) scaling_factor = args.get('scaling_factor', 2) bin = args.get('bin', float('Inf')) if np.isinf(bin): r = obj.rate() else: r = np.float64(obj.psth(bin)) hm = dict() for t in range(r.shape[1]): points = dict() for a in Afferent.affclasses: idx = np.logical_and(obj.aff.find(a), np.logical_not(r[:, t] == 0)) if np.sum(np.nonzero(idx)) == 0: idx[0] = True p = hv.Points(np.concatenate([ obj.aff.surface.hand2pixel(obj.aff.location[idx, :]), r[idx, t:t + 1] ], axis=1), vdims=['Firing rate']) if scale: points[a] = p.opts( style=dict(color=tuple(Afferent.affcol[a])), plot=dict(size_index=2, scaling_factor=scaling_factor, aspect='equal')) else: points[a] = p.opts( plot=dict(color_index=2, aspect='equal')).options( cmap='fire_r') hm[t] = hv.NdOverlay(points) hvobj = hv.HoloMap(hm, kdims='Time bin [' + str(bin) + ' ms]') return hvobj
def plot_afferent_population(obj, **args): size = args.get('size', 1) points = dict() for a in Afferent.affclasses: p = hv.Points(obj.surface.hand2pixel(obj.location[obj.find(a), :])) points[a] = p.opts(plot=dict(aspect='equal'), style=dict(color=tuple(Afferent.affcol[a]), s=size)) return hv.NdOverlay(points)
def plot_Cp(index): if not index: index = list(range(len(aoa))) out = dict() for ind in index: out[aoa[ind]] = self[ind].plot_surface() return hv.NdOverlay(out, kdims='AoA').relabel('x vs Cp (AoA-legend)')
def _sample(self, obj, data): """ Rasterizes the supplied object in the current region and samples it with the drawn paths returning an NdOverlay of Curves. Note: Because the function returns an NdOverlay containing a variable number of elements batching must be enabled and the legend_limit must be set to 0. """ if self.poly_stream.data is None: path = self.polys else: path = self.poly_stream.element if isinstance(obj, TriMesh): vdim = obj.nodes.vdims[0] else: vdim = obj.vdims[0] if len(path) > 2: x_range = path.range(0) y_range = path.range(1) else: return hv.NdOverlay({0: hv.Curve([], 'Distance', vdim)}) (x0, x1), (y0, y1) = x_range, y_range width, height = (max([min([(x1 - x0) / self.resolution, 500]), 10]), max([min([(y1 - y0) / self.resolution, 500]), 10])) raster = rasterize(obj, x_range=x_range, y_range=y_range, aggregator=self.aggregator, width=int(width), height=int(height), dynamic=False) x, y = raster.kdims sections = [] for g in path.geom(): xs, ys, distance = self._gen_samples(g) indexes = {x.name: xs, y.name: ys} points = raster.data.sel_points(method='nearest', **indexes).to_dataframe() points['Distance'] = distance sections.append(hv.Curve(points, 'Distance', vdims=[vdim, x, y])) return hv.NdOverlay(dict(enumerate(sections)))
def plot_stimulus(obj, **args): spatial = args.get('spatial', False) bin = args.get('bin', float('Inf')) if np.isinf(bin): bins = np.array([0, obj.duration]) num = 1 else: bins = np.r_[0:obj.duration + bin / 1000.:bin / 1000.] num = bins.size - 1 if not spatial: grid = args.get('grid', False) hm = dict() tmin = np.min(obj.trace) tmax = np.max(obj.trace) for t in range(num): if num == 1: d = {i:hv.Curve((obj.time,obj.trace[i]))\ for i in range(obj.trace.shape[0])} else: d = {i:hv.Curve((obj.time,obj.trace[i]))*\ hv.Curve([(bins[t+1],tmin),(bins[t+1],tmax)]) for i in range(obj.trace.shape[0])} if grid: hvobj = hv.NdLayout(d) else: hvobj = hv.NdOverlay(d) hm[t] = hvobj hvobj = hv.HoloMap(hm, kdims='Time bin [' + str(bin) + ' ms]').collate() else: sur = args.get('surface', hand_surface) mid = (bins[1:] + bins[:-1]) / 2. d = np.array([np.interp(mid,obj.time,obj.trace[i])\ for i in range(obj.trace.shape[0])]) d = (d - np.min(d)) d = 1 - d / np.max(d) hm = dict() locs = sur.hand2pixel(obj.location) rad = obj.pin_radius * sur.pxl_per_mm for t in range(num): p = hv.Polygons( [{ ('x', 'y'): hv.Ellipse(locs[l, 0], locs[l, 1], 2 * rad).array(), 'z': d[l, t] } for l in range(obj.location.shape[0])], vdims='z').opts( plot=dict(color_index='z', aspect='equal'), style=dict(linewidth=0., line_width=0.01)).options(cmap='fire') hm[t] = p hvobj = hv.HoloMap(hm, kdims='Time bin [' + str(bin) + ' ms]') return hvobj
def lines(self, **kwargs): return hv.NdOverlay( { _y: hv.Curve( self.source_df[[self.x, _y]].rename(columns={_y: "y"}) ) for i, _y in enumerate(self.line_dims) }, kdims="k", )
def _pos_indicator(self, obj, x): """ Returns an NdOverlay of Points indicating the current mouse position along the cross-sections. Note: Because the function returns an NdOverlay containing a variable number of elements batching must be enabled and the legend_limit must be set to 0. """ points = [] elements = obj or [] for el in elements: if len(el) < 1: continue p = Points(el[x], ['x', 'y'], crs=ccrs.GOOGLE_MERCATOR) points.append(p) if not points: return hv.NdOverlay({0: Points([], ['x', 'y'])}) return hv.NdOverlay(enumerate(points))
def plot_batch_x(ds, i): """Plot input features""" x_past, y_past, x_future, y_future = ds.get_rows(i) x = pd.concat([x_past, x_future]) p = hv.NdOverlay({ col: hv.Curve(x[col]) for col in x.columns }, kdims='column') now = y_past.index[-1] p *= hv.VLine(now).relabel('now').opts(color='red') return p
def concatenated_summary_curve_factory( cdf, kdims="Cycle_Index", vdims="Charge_Capacity(mAh/g)", title="Summary Curves", fill_alpha=0.8, size=12, width=800, legend_position="right", colors=None, markers=None, ): # TODO: missing doc-string if not hv_available: print("This function uses holoviews. But could not import it." "So I am aborting...") return if colors is None: colors = hv.Cycle("Category10") if markers is None: markers = hv.Cycle(["circle", "square", "triangle", "diamond"]) groups = [] curves_opts = [] curves = {} for indx, new_df in cdf.groupby(level=0, axis=1): g = indx.split("_")[1] groups.append(g) n = hv.Scatter(data=new_df[indx], kdims=kdims, vdims=vdims, group=g, label=indx).opts(fill_alpha=fill_alpha, size=size) curves[indx] = n ugroups = set(groups) max_sub_group = max([groups.count(x) for x in ugroups]) markers = markers[max_sub_group] colors = colors[len(ugroups)] for g, c in zip(ugroups, colors.values): curves_opts.append(opts.Scatter(g, color=c, marker=markers)) curves_overlay = hv.NdOverlay(curves, kdims="cell id").opts( opts.NdOverlay(width=800, legend_position=legend_position, title=title), *curves_opts, ) return curves_overlay
def hist(self, x, y): hists = {} ds = hv.Dataset(self.data) plot_opts = dict(self._plot_opts) plot_opts['invert_axes'] = self.kwds.get('orientation', False) == 'horizontal' opts = dict(plot=plot_opts, style=dict(alpha=self.kwds.get('alpha', 1))) for col in self.data.columns: hists[col] = hv.operation.histogram(ds, dimension=col).opts(**opts) return hv.NdOverlay(hists)
def select_widget(Select_numeric_variable): """ This program must take in a variable passed from the widget and turn it into a chart. The input is known as num_var and it is the variable you must use to get the data and build a chart. The output must return a HoloViews Chart. """ color = next(colors) overlay = hv.NdOverlay({group: hv.Distribution(np.histogram(dft[dft[dep]==group][Select_numeric_variable].values)) for i,group in enumerate(target_vars)}) hv_look = overlay.opts(opts.Distribution(alpha=0.5, height=height_size, width=width_size)).opts( title='KDE (Distribution) Plots of all Numeric Variables by Classes').opts( xlabel='%s' %dep).opts(ylabel='%s' %Select_numeric_variable) return hv_look
def plot_and_save_progress_graph(holoviews_table, filename, process_list): '''plot and save process graph from output file made by loop.py ''' progress_dict = { process: hv.Scatter(holoviews_table.select(process=process), 'ms', 'progress') for process in process_list } ndoverlay = hv.NdOverlay(progress_dict, kdims='process') renderer = hv.renderer('matplotlib') renderer.save(ndoverlay, 'progress_' + filename)
def line(self, x, y): if x and y: return hv.Curve(self.data, kdims=[x], vdims=[y]) else: df = self.data x = df.index.name or 'index' cols = df.columns df = df.reset_index() curves = {} for c in cols: curves[c] = hv.Curve(df, kdims=[x], vdims=[c]).opts(plot=self._plot_opts) return hv.NdOverlay(curves)
def plot_df(df_list): renderer = hv.renderer('bokeh').instance(mode='server') print(df_list) lines = { i: hv.Curve(df_list[i], kdims=['x'], vdims=['y']) for i in range(len(df_list)) } linespread = dynspread(datashade(hv.NdOverlay(lines, kdims='k')), aggregator=ds.count_cat('k')).opts( **{'plot': { 'height': 400, 'width': 1400 }}) return renderer.get_plot(linespread).state
def LayoutOfCycles(self, start, stop, fits_pulls=None, fits_releases=None): '''Return a holoviews layout of force-extension cycles. Args: start (int): number of first cycle in layout. stop (int): number of last cycle in layout. fits_pulls (list of lists of xr.DataArray): outer list: cycle, inner list: segment within cycle. fits_releases (list of lists of xr.DataArray): outer list: cycle, inner list: segment within cycle. Returns: layout (holoviews/matplotlib): layout of cycles. ''' hv.extension('matplotlib') #all_cycles = hv.Layout() all_cycles_dict = OrderedDict() for cyclenum in np.arange(start, stop): pull = self.pullsXr[cyclenum] release = self.releasesXr[cyclenum] currentcycle = OrderedDict() currentcycle['extension'] = hv.Curve(pull).opts(color='red') currentcycle['relaxation'] = hv.Curve(release).opts(color='blue') if (fits_pulls is not None and fits_releases is not None): for i, pullfit in enumerate(fits_pulls[cyclenum]): currentcycle['fit (pull) ' + str(i)] = hv.Curve(pullfit).opts( color='black', linestyle='dashed') for i, releasefit in enumerate(fits_releases[cyclenum]): currentcycle['fit (relaxation) ' + str(i)] = hv.Curve(releasefit).opts( color='black', linestyle='dashed') layout = hv.NdOverlay(currentcycle, kdims='ramp', sort=False).options({ 'Curve': { 'xlim': (0, 200e-9), 'ylim': (0, 70e-12) } }) layout.opts(show_legend=False) #cyclelayout = hd.dynspread(hd.datashade(layout, aggregator=ds.count_cat('k'))).opts(xrotation=0, xlim=(0, 200e-9), ylim=(0, 70e-12), xformatter='%.1e',yformatter='%.1e',xlabel='extension (m)', ylabel='force (N)') cyclelayout = layout.opts(xlabel='extension (m)', ylabel='force (N)') all_cycles_dict[str(cyclenum)] = cyclelayout all_cycles = hv.Layout(all_cycles_dict, kdims='cycle') hd.shade.color_key = None #reset return all_cycles.cols(4)
def compute_legend(self, colors=None): colors = colors or self.colors if self.legend: res = [] for i, val in enumerate(colors): res.append((self.y[i], val)) return hv.NdOverlay({ k: hv.Curve( self.source.head(1), label=str(k), kdims=[self.x], vdims=[self.y[0]], ).opts(color=v) for k, v in res }).opts(legend_position=self.legend_position) return None
def relabel_elements(ndoverlay, labels): """ ndOverlay is indexed by integer labels (str or iterable of strs) length of hv elements in the overlay must equal to the length of labels """ import holoviews as hv from itertools import cycle if isinstance(labels, str): labels = [labels] if isinstance(labels, list) and len(labels) != len(ndoverlay): raise ValueError('Length of the labels and ndoverlay must be the same') it = cycle(labels) relabeled = hv.NdOverlay({i: ndoverlay[i].relabel(next(it)) for i in range(len(ndoverlay))}) return relabeled
def peak_plot(self, index): print(index) if not index: index = [0] events = self.event_table.iloc[index].data all_peaks = np.load("181028_0045_peaks.npy") peaks = strax.split_by_containment(all_peaks, events.to_records()) # print(peaks[0]) plots = [] for i, peak in enumerate(peaks): if len(peak["data"]) and len(peak["data"]) < 30: ymax = np.max(peak["data"]) overlay = hv.NdOverlay({ j: hv.Curve(peak["data"][k], kdims='digtizer index', vdims="counts").opts(width=500, interpolation="steps-mid", xlim=(0, 200), ylim=(0, ymax)) for j, k in enumerate(reversed(np.argsort(peak["area"]))) }) plots.append(overlay) return hv.Layout(plots)