def _make_tooltip(self): """If toolip is true, create it, either with default settings or using custom_tooltip object.""" if self._enable_tooltip: if self._custom_tooltip: tt = self._custom_tooltip else: tt = Tooltip(fields=['x', 'y'], formats=['', '.2f'], labels=['Hour', 'ED']) else: tt = None return tt
def process(self, inputs): """ Takes `datetime`, `open`, `close`, `high`, `volume` columns in the dataframe to plot the bqplot figure for this stock. Arguments ------- inputs: list list of input dataframes. Returns ------- bqplot.Figure """ stock = inputs[0] num_points = self.conf['points'] stride = max(len(stock) // num_points, 1) label = 'stock' if 'label' in self.conf: label = self.conf['label'] sc = LinearScale() sc2 = LinearScale() dt_scale = DateScale() ax_x = Axis(label='Date', scale=dt_scale) ax_y = Axis(label='Price', scale=sc, orientation='vertical', tick_format='0.0f') # Construct the marks ohlc = OHLC(x=stock['datetime'][::stride], y=stock[['open', 'high', 'low', 'close']] .as_gpu_matrix()[::stride, :], marker='candle', scales={'x': dt_scale, 'y': sc}, format='ohlc', stroke='blue', display_legend=True, labels=[label]) bar = Bars(x=stock['datetime'][::stride], y=stock['volume'][::stride], scales={'x': dt_scale, 'y': sc2}, padding=0.2) def_tt = Tooltip(fields=['x', 'y'], formats=['%Y-%m-%d', '.2f']) bar.tooltip = def_tt bar.interactions = { 'legend_hover': 'highlight_axes', 'hover': 'tooltip', 'click': 'select', } sc.min = stock['close'].min() - 0.3 * \ (stock['close'].max() - stock['close'].min()) sc.max = stock['close'].max() sc2.max = stock['volume'].max()*4.0 f = Figure(axes=[ax_x, ax_y], marks=[ohlc, bar], fig_margin={"top": 0, "bottom": 60, "left": 60, "right": 60}) return f
def __init__(self): # Initialize the chart with a default ticker self.data_chart = pd.DataFrame() self.chart_dropdown_x = Dropdown(description='X-Axis', layout=Layout(width='380px')) self.chart_dropdown_y = Dropdown(description='Y-Axis', layout=Layout(width='380px')) self.x_sc = LinearScale() self.y_sc = LinearScale() self.tt = Tooltip(fields=['name', 'x', 'y'], formats=['', '.2f', '.2f']) self.scatter = Scatter(scales={ 'x': self.x_sc, 'y': self.y_sc }, colors=['dodgerblue'], tooltip=self.tt, unhovered_style={'opacity': 0.5}) self.ax_x = Axis(scale=self.x_sc) self.ax_y = Axis(scale=self.y_sc, orientation='vertical', tick_format='0.2f') self.fig = Figure(marks=[], axes=[self.ax_x, self.ax_y], animation_duration=1000, padding_x=0, layout={ 'width': "100%", 'height': "500px" }) self.data_grid = DataGrid(layout={'width': "720px", 'height': "200px"}) self.box = VBox([ HBox([self.fig]), HBox([self.chart_dropdown_x, self.chart_dropdown_y]) ]) display(self.box)
def create_map(self, map_file): first_year = self.years[0] first_provincial_income_proportion = self.get_provincial_income_proportion( first_year) sc_geo = Mercator(scale_factor=7000, center=(5.5, 53.0)) tooltip = Tooltip(fields=["name"], labels=["province"]) return Map( map_data=topo_load(map_file), scales={ "projection": sc_geo, "color": self.col_scale }, colors={"default_color": "Grey"}, color=first_provincial_income_proportion, tooltip=tooltip, interactions={ "click": "select", "hover": "tooltip" }, visible=True, )
def __init__(self): self.niveau = Dropdown(options=[('Niveau {}'.format(i), i) for i in RushHour.niveaux()]) self.niveau.observe(lambda widget: self.change_niveau(widget.owner.value)) self.voiture = Dropdown(options=[]) self.modele = Plateau(1) self.vue = Figure(scale_x = LinearScale(min=0, max=self.modele.dimension), scale_y = LinearScale(min=self.modele.dimension, max=0)) self.vue.layout.width="75ex" self.vue.layout.width="75ex" self.vue.layout.height=self.vue.layout.width; self.vue.vue_voitures = {} for lettre, couleur in couleurs.items(): vue_voiture = Lines(x=[], y=[], scales={'x':self.vue.scale_x, 'y':self.vue.scale_y}, fill='inside', colors=[couleurs[lettre]], visible=False, tooltip=Tooltip(fields=["lettre"],show_labels=False), ) vue_voiture.lettre = "coucou" # lettre vue_voiture.on_click(lambda vue_voiture, _: self.choix_voiture(vue_voiture.lettre)) self.vue.vue_voitures[lettre] = vue_voiture self.vue.marks = list(self.vue.vue_voitures.values()) boutton_solution = Button(description="Solution") boutton_solution.on_click(self.montre_solution) VBox.__init__(self, [HBox([self.niveau, boutton_solution]), self.vue, self.boutton_direction('U'), HBox([self.boutton_direction('L'), self.voiture, self.boutton_direction('R')]), self.boutton_direction('D') ]) self.layout.align_items = 'center' self.change_niveau(1)
def feature_byProperty(features, xProperties, seriesProperty, **kwargs): """Generates a Chart from a set of features. Plots property values of one or more features. Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturebyproperty Args: features (ee.FeatureCollection): The features to include in the chart. xProperties (list | dict): One of (1) a list of properties to be plotted on the x-axis; or (2) a (property, label) dictionary specifying labels for properties to be used as values on the x-axis. seriesProperty (str): The name of the property used to label each feature in the legend. Raises: Exception: If the provided xProperties is not a list or dict. Exception: If the chart fails to create. """ try: df = ee_to_df(features) if isinstance(xProperties, list): x_data = xProperties y_data = df[xProperties].values elif isinstance(xProperties, dict): x_data = list(xProperties.values()) y_data = df[list(xProperties.keys())].values else: raise Exception("xProperties must be a list or dictionary.") labels = list(df[seriesProperty]) if "ylim" in kwargs: min_value = kwargs["ylim"][0] max_value = kwargs["ylim"][1] else: min_value = y_data.min() max_value = y_data.max() max_value = max_value + 0.2 * (max_value - min_value) if "title" not in kwargs: title = "" else: title = kwargs["title"] if "legend_location" not in kwargs: legend_location = "top-left" else: legend_location = kwargs["legend_location"] if "display_legend" not in kwargs: display_legend = True else: display_legend = kwargs["display_legend"] fig = plt.figure( title=title, legend_location=legend_location, ) if "width" in kwargs: fig.layout.width = kwargs["width"] if "height" in kwargs: fig.layout.height = kwargs["height"] bar_chart = plt.bar(x=x_data, y=y_data, labels=labels, display_legend=display_legend) bar_chart.type = "grouped" if "colors" in kwargs: bar_chart.colors = kwargs["colors"] if "xlabel" in kwargs: plt.xlabel(kwargs["xlabel"]) if "ylabel" in kwargs: plt.ylabel(kwargs["ylabel"]) plt.ylim(min_value, max_value) if "xlabel" in kwargs and ("ylabel" in kwargs): bar_chart.tooltip = Tooltip( fields=["x", "y"], labels=[kwargs["xlabel"], kwargs["ylabel"]]) else: bar_chart.tooltip = Tooltip(fields=["x", "y"]) plt.show() except Exception as e: raise Exception(e)
def feature_histogram(features, property, maxBuckets=None, minBucketWidth=None, **kwargs): """ Generates a Chart from a set of features. Computes and plots a histogram of the given property. - X-axis = Histogram buckets (of property value). - Y-axis = Frequency Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturehistogram Args: features (ee.FeatureCollection): The features to include in the chart. property (str): The name of the property to generate the histogram for. maxBuckets (int, optional): The maximum number of buckets (bins) to use when building a histogram; will be rounded up to a power of 2. minBucketWidth (float, optional): The minimum histogram bucket width, or null to allow any power of 2. Raises: Exception: If the provided xProperties is not a list or dict. Exception: If the chart fails to create. """ import math if not isinstance(features, ee.FeatureCollection): raise Exception("features must be an ee.FeatureCollection") first = features.first() props = first.propertyNames().getInfo() if property not in props: raise Exception( f"property {property} not found. Available properties: {', '.join(props)}" ) def nextPowerOf2(n): return pow(2, math.ceil(math.log2(n))) def grow_bin(bin_size, ref): while bin_size < ref: bin_size *= 2 return bin_size try: raw_data = pd.to_numeric( pd.Series(features.aggregate_array(property).getInfo())) y_data = raw_data.tolist() if "ylim" in kwargs: min_value = kwargs["ylim"][0] max_value = kwargs["ylim"][1] else: min_value = raw_data.min() max_value = raw_data.max() data_range = max_value - min_value if not maxBuckets: initial_bin_size = nextPowerOf2(data_range / pow(2, 8)) if minBucketWidth: if minBucketWidth < initial_bin_size: bin_size = grow_bin(minBucketWidth, initial_bin_size) else: bin_size = minBucketWidth else: bin_size = initial_bin_size else: initial_bin_size = math.ceil(data_range / nextPowerOf2(maxBuckets)) if minBucketWidth: if minBucketWidth < initial_bin_size: bin_size = grow_bin(minBucketWidth, initial_bin_size) else: bin_size = minBucketWidth else: bin_size = initial_bin_size start_bins = (math.floor(min_value / bin_size) * bin_size) - (bin_size / 2) end_bins = (math.ceil(max_value / bin_size) * bin_size) + (bin_size / 2) if start_bins < min_value: y_data.append(start_bins) else: y_data[y_data.index(min_value)] = start_bins if end_bins > max_value: y_data.append(end_bins) else: y_data[y_data.index(max_value)] = end_bins num_bins = math.floor((end_bins - start_bins) / bin_size) if "title" not in kwargs: title = "" else: title = kwargs["title"] fig = plt.figure(title=title) if "width" in kwargs: fig.layout.width = kwargs["width"] if "height" in kwargs: fig.layout.height = kwargs["height"] if "xlabel" not in kwargs: xlabel = "" else: xlabel = kwargs["xlabel"] if "ylabel" not in kwargs: ylabel = "" else: ylabel = kwargs["ylabel"] histogram = plt.hist( sample=y_data, bins=num_bins, axes_options={ "count": { "label": ylabel }, "sample": { "label": xlabel } }, ) if "colors" in kwargs: histogram.colors = kwargs["colors"] if "stroke" in kwargs: histogram.stroke = kwargs["stroke"] else: histogram.stroke = "#ffffff00" if "stroke_width" in kwargs: histogram.stroke_width = kwargs["stroke_width"] else: histogram.stroke_width = 0 if ("xlabel" in kwargs) and ("ylabel" in kwargs): histogram.tooltip = Tooltip( fields=["midpoint", "count"], labels=[kwargs["xlabel"], kwargs["ylabel"]], ) else: histogram.tooltip = Tooltip(fields=["midpoint", "count"]) plt.show() except Exception as e: raise Exception(e)
def feature_groups(features, xProperty, yProperty, seriesProperty, **kwargs): """Generates a Chart from a set of features. Plots the value of one property for each feature. Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturegroups Args: features (ee.FeatureCollection): The feature collection to make a chart from. xProperty (str): Features labeled by xProperty. yProperty (str): Features labeled by yProperty. seriesProperty (str): The property used to label each feature in the legend. Raises: Exception: Errors when creating the chart. """ try: df = ee_to_df(features) df[yProperty] = pd.to_numeric(df[yProperty]) unique_series_values = df[seriesProperty].unique().tolist() new_column_names = [] for value in unique_series_values: sample_filter = (df[seriesProperty] == value).map({ True: 1, False: 0 }) column_name = str(yProperty) + "_" + str(value) df[column_name] = df[yProperty] * sample_filter new_column_names.append(column_name) if "labels" in kwargs: labels = kwargs["labels"] else: labels = [str(x) for x in unique_series_values] if "ylim" in kwargs: min_value = kwargs["ylim"][0] max_value = kwargs["ylim"][1] else: min_value = df[yProperty].to_numpy().min() max_value = df[yProperty].to_numpy().max() max_value = max_value + 0.2 * (max_value - min_value) if "title" not in kwargs: title = "" else: title = kwargs["title"] if "legend_location" not in kwargs: legend_location = "top-left" else: legend_location = kwargs["legend_location"] x_data = list(df[xProperty]) y_data = [df[x] for x in new_column_names] plt.bar(x_data, y_data) fig = plt.figure( title=title, legend_location=legend_location, ) if "width" in kwargs: fig.layout.width = kwargs["width"] if "height" in kwargs: fig.layout.height = kwargs["height"] if "display_legend" not in kwargs: display_legend = True else: display_legend = kwargs["display_legend"] bar_chart = plt.bar(x_data, y_data, labels=labels, display_legend=display_legend) if "colors" in kwargs: bar_chart.colors = kwargs["colors"] if "xlabel" in kwargs: plt.xlabel(kwargs["xlabel"]) if "ylabel" in kwargs: plt.ylabel(kwargs["ylabel"]) plt.ylim(min_value, max_value) if "xlabel" in kwargs and ("ylabel" in kwargs): bar_chart.tooltip = Tooltip( fields=["x", "y"], labels=[kwargs["xlabel"], kwargs["ylabel"]]) else: bar_chart.tooltip = Tooltip(fields=["x", "y"]) plt.show() except Exception as e: raise Exception(e)
def feature_byFeature(features, xProperty, yProperties, **kwargs): """Generates a Chart from a set of features. Plots the value of one or more properties for each feature. Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturebyfeature Args: features (ee.FeatureCollection): The feature collection to generate a chart from. xProperty (str): Features labeled by xProperty. yProperties (list): Values of yProperties. Raises: Exception: Errors when creating the chart. """ try: df = ee_to_df(features) if "ylim" in kwargs: min_value = kwargs["ylim"][0] max_value = kwargs["ylim"][1] else: min_value = df[yProperties].to_numpy().min() max_value = df[yProperties].to_numpy().max() max_value = max_value + 0.2 * (max_value - min_value) if "title" not in kwargs: title = "" else: title = kwargs["title"] if "legend_location" not in kwargs: legend_location = "top-left" else: legend_location = kwargs["legend_location"] x_data = list(df[xProperty]) y_data = df[yProperties].values.T.tolist() plt.bar(x_data, y_data) fig = plt.figure( title=title, legend_location=legend_location, ) if "width" in kwargs: fig.layout.width = kwargs["width"] if "height" in kwargs: fig.layout.height = kwargs["height"] if "labels" in kwargs: labels = kwargs["labels"] else: labels = yProperties if "display_legend" not in kwargs: display_legend = True else: display_legend = kwargs["display_legend"] bar_chart = plt.bar(x_data, y_data, labels=labels, display_legend=display_legend) bar_chart.type = "grouped" if "colors" in kwargs: bar_chart.colors = kwargs["colors"] if "xlabel" in kwargs: plt.xlabel(kwargs["xlabel"]) if "ylabel" in kwargs: plt.ylabel(kwargs["ylabel"]) plt.ylim(min_value, max_value) if "xlabel" in kwargs and ("ylabel" in kwargs): bar_chart.tooltip = Tooltip( fields=["x", "y"], labels=[kwargs["xlabel"], kwargs["ylabel"]]) else: bar_chart.tooltip = Tooltip(fields=["x", "y"]) plt.show() except Exception as e: raise Exception(e)
# %% {"collapsed": true} def get_data(year): year_index = year - 1800 income = data['income'].apply(lambda x: x[year_index]) life_exp = data['lifeExpectancy'].apply(lambda x: x[year_index]) pop = data['population'].apply(lambda x: x[year_index]) return income, life_exp, pop # %% [markdown] # #### Creating the Tooltip to display the required fields # # `bqplot`'s native `Tooltip` allows us to simply display the data fields we require on a mouse-interaction. # %% {"collapsed": true} tt = Tooltip(fields=['name', 'x', 'y'], labels=['Country Name', 'Income per Capita', 'Life Expectancy']) # %% [markdown] # #### Creating the Label to display the year # # Staying true to the `d3` recreation of the talk, we place a `Label` widget in the bottom-right of the `Figure` (it inherits the `Figure` co-ordinates when no scale is passed to it). With `enable_move` set to `True`, the `Label` can be dragged around. # %% {"collapsed": true} year_label = Label(x=[0.75], y=[0.10], default_size=46, font_weight='bolder', colors=['orange'], text=[str(initial_year)], enable_move=True)
def init_dashboard(self): from bqplot import DateScale, LinearScale, DateScale, Axis, Lines, Figure, Tooltip from bqplot.colorschemes import CATEGORY10, CATEGORY20 from bqplot.toolbar import Toolbar from ipywidgets import VBox, Tab from IPython.display import display from tornado import gen cpu_sx = LinearScale() cpu_sy = LinearScale() cpu_x = Axis(label='Time (s)', scale=cpu_sx) cpu_y = Axis(label='CPU Usage (%)', scale=cpu_sy, orientation='vertical') mem_sx = LinearScale() mem_sy = LinearScale() mem_x = Axis(label='Time (s)', scale=mem_sx) mem_y = Axis(label='Memory Usage (MB)', scale=mem_sy, orientation='vertical') thru_sx = LinearScale() thru_sy = LinearScale() thru_x = Axis(label='Time (s)', scale=thru_sx) thru_y = Axis(label='Data Processed (MB)', scale=thru_sy, orientation='vertical') colors = CATEGORY20 tt = Tooltip(fields=['name'], labels=['Filter Name']) self.cpu_lines = { str(n): Lines(labels=[str(n)], x=[0.0], y=[0.0], colors=[colors[i]], tooltip=tt, scales={ 'x': cpu_sx, 'y': cpu_sy }) for i, n in enumerate(self.other_nodes) } self.mem_lines = { str(n): Lines(labels=[str(n)], x=[0.0], y=[0.0], colors=[colors[i]], tooltip=tt, scales={ 'x': mem_sx, 'y': mem_sy }) for i, n in enumerate(self.other_nodes) } self.thru_lines = { str(n): Lines(labels=[str(n)], x=[0.0], y=[0.0], colors=[colors[i]], tooltip=tt, scales={ 'x': thru_sx, 'y': thru_sy }) for i, n in enumerate(self.other_nodes) } self.cpu_fig = Figure(marks=list(self.cpu_lines.values()), axes=[cpu_x, cpu_y], title='CPU Usage', animation_duration=50) self.mem_fig = Figure(marks=list(self.mem_lines.values()), axes=[mem_x, mem_y], title='Memory Usage', animation_duration=50) self.thru_fig = Figure(marks=list(self.thru_lines.values()), axes=[thru_x, thru_y], title='Data Processed', animation_duration=50) tab = Tab() tab.children = [self.cpu_fig, self.mem_fig, self.thru_fig] tab.set_title(0, 'CPU') tab.set_title(1, 'Memory') tab.set_title(2, 'Throughput') display(tab) perf_queue = Queue() self.exit_perf = Event() def wait_for_perf_updates(q, exit, cpu_lines, mem_lines, thru_lines): while not exit.is_set(): messages = [] while not exit.is_set(): try: messages.append(q.get(False)) except queue.Empty as e: time.sleep(0.05) break for message in messages: filter_name, time_val, cpu, mem_info, processed = message mem = mem_info[0] / 2.**20 vmem = mem_info[1] / 2.**20 proc = processed / 2.**20 cpu_lines[filter_name].x = np.append( cpu_lines[filter_name].x, [time_val.total_seconds()]) cpu_lines[filter_name].y = np.append( cpu_lines[filter_name].y, [cpu]) mem_lines[filter_name].x = np.append( mem_lines[filter_name].x, [time_val.total_seconds()]) mem_lines[filter_name].y = np.append( mem_lines[filter_name].y, [mem]) thru_lines[filter_name].x = np.append( thru_lines[filter_name].x, [time_val.total_seconds()]) thru_lines[filter_name].y = np.append( thru_lines[filter_name].y, [proc]) for n in self.other_nodes: n.perf_queue = perf_queue self.perf_thread = Thread(target=wait_for_perf_updates, args=(perf_queue, self.exit_perf, self.cpu_lines, self.mem_lines, self.thru_lines)) self.perf_thread.start()
def plot_pulse_files(metafile, time=True, backend='bqplot'): ''' plot_pulse_files(metafile) Helper function to plot a list of AWG files. A jupyter slider widget allows choice of sequence number. ''' #If we only go one filename turn it into a list with open(metafile, 'r') as FID: meta_info = json.load(FID) fileNames = [] for el in meta_info["instruments"].values(): # Accomodate seq_file per instrument and per channel if isinstance(el, str): fileNames.append(el) elif isinstance(el, dict): for file in el.values(): fileNames.append(file) line_names, num_seqs, data_dicts = extract_waveforms(fileNames, time=time) localname = os.path.split(fileNames[0])[1] sequencename = localname.split('-')[0] if backend == 'matplotlib': import matplotlib.pyplot as plt from ipywidgets import interact, IntSlider def update_plot(seq_ind): for line_name in line_names: dat = data_dicts[f"{line_name}_{seq_ind}"] plt.plot(dat['x'], dat['y'], label=line_name, linewidth=1.0) interact(update_plot, seq_ind=IntSlider(min=1, max=num_seqs, step=1, value=1, description="Sequence Number")) elif backend == 'bqplot': from bqplot import DateScale, LinearScale, Axis, Lines, Figure, Tooltip from bqplot.colorschemes import CATEGORY10, CATEGORY20 from ipywidgets import interact, IntSlider, VBox sx = LinearScale() sy = LinearScale(min=-1.0, max=2 * len(line_names) - 1.0) if time: ax = Axis(label='Time (ns)', scale=sx) else: ax = Axis(label="Samples", scale=sx) ay = Axis(label='Amplitude', scale=sy, orientation='vertical') colors = CATEGORY10 if len(line_names) < 10 else CATEGORY20 lines = [] tt = Tooltip(fields=['name'], labels=['Channel']) x_mult = 1.0e9 if time else 1 for i, line_name in enumerate(line_names): dat = data_dicts[f"{line_name}_1"] lines.append( Lines(labels=[line_name], x=x_mult * dat['x'], y=dat['y'], scales={ 'x': sx, 'y': sy }, tooltip=tt, animate=False, colors=[colors[i]])) slider = IntSlider(min=1, max=num_seqs, step=1, description='Segment', value=1) def segment_changed(change): for line, line_name in zip(lines, line_names): dat = data_dicts[f"{line_name}_{slider.value}"] line.x = x_mult * dat['x'] line.y = dat['y'] slider.observe(segment_changed, 'value') fig = Figure(marks=lines, axes=[ax, ay], title='Waveform Plotter', animation_duration=50) return VBox([slider, fig])
def feature_vector_distribution(features, label_column, bins=25, group_columns=None, f_lim=None, colors=None): """ features (dataframe): a data frame of feature vectors along with a label column and other metadata label_column (str): the name of the column in the features dataframe that refers to the label infomration bins (int): the number of bins in the histograms group_columns (list): if you want other metadata in the tooltip, these columns will be added f_lim (dict): this sets the limits for max and min of the plots to a constant {'max':10, 'min':10}. otherwise defaults to the values of the current features which can be missleading. colors (list): list of colors to use. Internally has a list of 10. If the labels are longer you will need to pass your own """ dist = unicode("640px") third_dist = unicode("213px") if f_lim: sc_x = LinearScale(min=f_lim['min'], max=f_lim['max']) sc_y = LinearScale(min=f_lim['min'], max=f_lim['max']) else: sc_x = LinearScale() sc_y = LinearScale() scale_y = LinearScale(min=0) x_ord_legend = OrdinalScale() y_lin_legend = LinearScale() if group_columns is None: count_column = features.columns[1] group_columns = [] else: count_column = group_columns[0] if colors is None: colors = [ "#E6B0AA", "#C39BD3", "#73C6B6", "#F7DC6F", "#F0B27A", "#D0D3D4", "#85929E", "#6E2C00", "#1A5276", "#17202A" ] box_color = 'black' feature_x = Dropdown(description='Feature 1') feature_y = Dropdown(description='Feature 2') feature_x.options = [ x for x in features.columns if x not in [label_column] + group_columns ] feature_y.options = [ x for x in features.columns if x not in [label_column] + group_columns ] feature1 = feature_x.options[0] feature2 = feature_y.options[1] tt = Tooltip(fields=['name'], labels=[', '.join(['index', label_column] + group_columns)]) scatters = [] hists_y = [] hists_x = [] h_bins_x = get_h_bins(features[[feature1]], bins, f_lim) h_bins_y = get_h_bins(features[[feature2]], bins, f_lim) for index, group in enumerate(features.groupby([label_column])): # put the label column and any group column data in the tooltip names = [] for row in range(group[1].shape[0]): names.append('{},'.format(row) + ','.join([ str(x) for x in group[1][[label_column] + group_columns].iloc[row].values ])) # create a scatter plot for each group scatters.append( Scatter( x=group[1][feature1].values, y=group[1][feature2].values, names=names, display_names=False, default_opacities=[0.5], default_size=30, scales={ 'x': sc_x, 'y': sc_y }, colors=[colors[index]], tooltip=tt, )) # create a histograms using a bar chart for each group # histogram plot for bqplot does not have enough options (no setting range, no setting orientation) h_y, h_x = np.histogram(group[1][feature1].values, bins=h_bins_x) hists_x.append( Bars(x=h_x, y=h_y, opacities=[0.3] * bins, scales={ 'x': sc_x, 'y': scale_y }, colors=[colors[index]], orientation='vertical')) h_y, h_x = np.histogram(group[1][feature2].values, bins=h_bins_y) hists_y.append( Bars(x=h_x, y=h_y, opacities=[0.3] * bins, scales={ 'x': sc_x, 'y': scale_y }, colors=[colors[index]], orientation='horizontal')) # legend will show the names of the labels as well as a total count of each legend_bar = Bars( x=features.groupby(label_column).count()[count_column].index, y=features.groupby(label_column).count()[count_column].values, colors=colors, opacities=[0.3] * 6, scales={ 'x': x_ord_legend, 'y': y_lin_legend }, orientation='horizontal') ax_x_legend = Axis( scale=x_ord_legend, tick_style={'font-size': 24}, label='', orientation='vertical', tick_values=features.groupby(label_column).count()[count_column].index) ax_y_legend = Axis(scale=y_lin_legend, orientation='horizontal', label='Total', color=box_color, num_ticks=4) #these are blank blank axis that are used to fill in the boarder for the top and right of the figures ax_top = Axis(scale=sc_x, color=box_color, side='top', tick_style={'font-size': 0}) ax_right = Axis(scale=sc_x, color=box_color, side='right', tick_style={'font-size': 0}) ax_left = Axis(scale=sc_x, color=box_color, side='left', tick_style={'font-size': 0}) ax_bottom = Axis(scale=sc_x, color=box_color, side='bottom', tick_style={'font-size': 0}) #scatter plot axis ax_x = Axis(label=feature1, scale=sc_x, color=box_color) ax_y = Axis(label=feature2, scale=sc_y, orientation='vertical', color=box_color) #count column of histogram ax_count_vert = Axis(label='', scale=scale_y, orientation='vertical', color=box_color, num_ticks=5) ax_count_horiz = Axis(label='', scale=scale_y, orientation='horizontal', color=box_color, num_ticks=5) #histogram bin axis ax_hist_x = Axis(label='', scale=sc_x, orientation='vertical', color=box_color) ax_hist_y = Axis(label='', scale=sc_x, orientation='horizontal', color=box_color) #create figures for each plot f_scatter = Figure( axes=[ax_x, ax_y, ax_top, ax_right], background_style={'fill': 'white'}, #css is inserted directly marks=scatters, min_aspect_ratio=1, max_aspect_ratio=1, fig_margin={ "top": 0, "bottom": 60, "left": 60, "right": 0 }, ) f_hists_y = Figure( axes=[ax_left, ax_count_horiz, ax_top, ax_right], background_style={'fill': 'white'}, marks=hists_y, min_aspect_ratio=.33, max_aspect_ratio=.33, fig_margin={ "top": 0, "bottom": 60, "left": 10, "right": 0 }, ) f_hists_x = Figure( axes=[ax_count_vert, ax_bottom, ax_top, ax_right], background_style={'fill': 'white'}, marks=hists_x, min_aspect_ratio=3, max_aspect_ratio=3, fig_margin={ "top": 20, "bottom": 10, "left": 60, "right": 0 }, ) f_legend = Figure(marks=[legend_bar], axes=[ax_x_legend, ax_y_legend], title='', legend_location='bottom-right', background_style={'fill': 'white'}, min_aspect_ratio=1, max_aspect_ratio=1, fig_margin={ "top": 10, "bottom": 30, "left": 20, "right": 20 }) # we already set the ratios, but it is necessary to set the size explicitly anyway # this is kind of cool, inserts this into the style in html f_legend.layout.height = third_dist f_legend.layout.width = third_dist f_hists_x.layout.height = third_dist f_hists_x.layout.width = dist f_hists_y.layout.height = dist f_hists_y.layout.width = third_dist f_scatter.layout.height = dist f_scatter.layout.width = dist # we create some functions that allow changes when the widgets notice an event def change_x_feature(b): h_bins_x = get_h_bins(features[[feature_x.value]], bins, f_lim) for index, group in enumerate(features.groupby([label_column])): scatters[index].x = group[1][feature_x.value] h_y, h_x = np.histogram(group[1][feature_x.value].values, bins=h_bins_x) hists_x[index].y = h_y ax_x.label = feature_x.value def change_y_feature(b): h_bins_y = get_h_bins(features[[feature_y.value]], bins, f_lim) for index, group in enumerate(features.groupby([label_column])): scatters[index].y = group[1][feature_y.value] h_y, h_x = np.histogram(group[1][feature_y.value].values, bins=h_bins_y) hists_y[index].y = h_y ax_y.label = feature_y.value # when the user selects a different feature, switch the data plotted feature_x.observe(change_x_feature, 'value') feature_y.observe(change_y_feature, 'value') #return the stacked figures to be plotted return VBox([ HBox([feature_x, feature_y]), HBox([f_hists_x, f_legend]), HBox([f_scatter, f_hists_y]) ])