def monsoon_data_plot_power(self, samples, voltage, dest_path, plot_title): """Plot the monsoon power data using bokeh interactive plotting tool. Args: samples: a list of tuples in which the first element is a timestamp and the second element is the sampled current at that time voltage: the voltage that was used during the measurement dest_path: destination path plot_title: a filename and title for the plot. """ logging.info('Plotting the power measurement data.') time_relative = [sample[0] for sample in samples] duration = time_relative[-1] - time_relative[0] current_data = [sample[1] * 1000 for sample in samples] avg_current = sum(current_data) / len(current_data) power_data = [current * voltage for current in current_data] color = ['navy'] * len(samples) # Preparing the data and source link for bokehn java callback source = ColumnDataSource( data=dict(x0=time_relative, y0=power_data, color=color)) s2 = ColumnDataSource( data=dict(z0=[duration], y0=[round(avg_current, 2)], x0=[round(avg_current * voltage, 2)], z1=[round(avg_current * voltage * duration, 2)], z2=[round(avg_current * duration, 2)])) # Setting up data table for the output columns = [ TableColumn(field='z0', title='Total Duration (s)'), TableColumn(field='y0', title='Average Current (mA)'), TableColumn(field='x0', title='Average Power (4.2v) (mW)'), TableColumn(field='z1', title='Average Energy (mW*s)'), TableColumn(field='z2', title='Normalized Average Energy (mA*s)') ] dt = DataTable(source=s2, columns=columns, width=1300, height=60, editable=True) output_file(os.path.join(dest_path, plot_title + '.html')) tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save' # Create a new plot with the datatable above plot = figure(plot_width=1300, plot_height=700, title=plot_title, tools=tools, output_backend='webgl') plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='width')) plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='height')) plot.line('x0', 'y0', source=source, line_width=2) plot.circle('x0', 'y0', source=source, size=0.5, fill_color='color') plot.xaxis.axis_label = 'Time (s)' plot.yaxis.axis_label = 'Power (mW)' plot.title.text_font_size = {'value': '15pt'} jsscript = open(self.customjsfile, 'r') customjsscript = jsscript.read() # Callback Java scripting source.callback = CustomJS(args=dict(mytable=dt), code=customjsscript) # Layout the plot and the datatable bar save(layout([[dt], [plot]]))
def monsoon_data_plot(mon_info, monsoon_results, tag=''): """Plot the monsoon current data using bokeh interactive plotting tool. Plotting power measurement data with bokeh to generate interactive plots. You can do interactive data analysis on the plot after generating with the provided widgets, which make the debugging much easier. To realize that, bokeh callback java scripting is used. View a sample html output file: https://drive.google.com/open?id=0Bwp8Cq841VnpT2dGUUxLYWZvVjA Args: mon_info: obj with information of monsoon measurement, including monsoon device object, measurement frequency, duration, etc. monsoon_results: a MonsoonResult or list of MonsoonResult objects to to plot. tag: an extra tag to append to the resulting filename. Returns: plot: the plotting object of bokeh, optional, will be needed if multiple plots will be combined to one html file. dt: the datatable object of bokeh, optional, will be needed if multiple datatables will be combined to one html file. """ if not isinstance(monsoon_results, list): monsoon_results = [monsoon_results] logging.info('Plotting the power measurement data.') voltage = monsoon_results[0].voltage total_current = 0 total_samples = 0 for result in monsoon_results: total_current += result.average_current * result.num_samples total_samples += result.num_samples avg_current = total_current / total_samples time_relative = [ data_point.time for monsoon_result in monsoon_results for data_point in monsoon_result.get_data_points() ] current_data = [ data_point.current * 1000 for monsoon_result in monsoon_results for data_point in monsoon_result.get_data_points() ] total_data_points = sum(result.num_samples for result in monsoon_results) color = ['navy'] * total_data_points # Preparing the data and source link for bokehn java callback source = ColumnDataSource( data=dict(x=time_relative, y=current_data, color=color)) s2 = ColumnDataSource( data=dict(a=[mon_info.duration], b=[round(avg_current, 2)], c=[round(avg_current * voltage, 2)], d=[round(avg_current * voltage * mon_info.duration, 2)], e=[round(avg_current * mon_info.duration, 2)])) # Setting up data table for the output columns = [ TableColumn(field='a', title='Total Duration (s)'), TableColumn(field='b', title='Average Current (mA)'), TableColumn(field='c', title='Average Power (4.2v) (mW)'), TableColumn(field='d', title='Average Energy (mW*s)'), TableColumn(field='e', title='Normalized Average Energy (mA*s)') ] dt = DataTable(source=s2, columns=columns, width=1300, height=60, editable=True) plot_title = ( os.path.basename(os.path.splitext(monsoon_results[0].tag)[0]) + tag) output_file(os.path.join(mon_info.data_path, plot_title + '.html')) tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save' # Create a new plot with the datatable above plot = figure(plot_width=1300, plot_height=700, title=plot_title, tools=tools) plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='width')) plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='height')) plot.line('x', 'y', source=source, line_width=2) plot.circle('x', 'y', source=source, size=0.5, fill_color='color') plot.xaxis.axis_label = 'Time (s)' plot.yaxis.axis_label = 'Current (mA)' plot.title.text_font_size = {'value': '15pt'} # Callback JavaScript source.selected.js_on_change( "indices", CustomJS(args=dict(source=source, mytable=dt), code=""" const inds = source.selected.indices; const d1 = source.data; const d2 = mytable.source.data; var ym = 0 var ts = 0 var min=d1['x'][inds[0]] var max=d1['x'][inds[0]] d2['a'] = [] d2['b'] = [] d2['c'] = [] d2['d'] = [] d2['e'] = [] if (inds.length==0) {return;} for (var i = 0; i < inds.length; i++) { ym += d1['y'][inds[i]] d1['color'][inds[i]] = "red" if (d1['x'][inds[i]] < min) { min = d1['x'][inds[i]]} if (d1['x'][inds[i]] > max) { max = d1['x'][inds[i]]} } ym /= inds.length ts = max - min d2['a'].push(Math.round(ts*1000.0)/1000.0) d2['b'].push(Math.round(ym*100.0)/100.0) d2['c'].push(Math.round(ym*4.2*100.0)/100.0) d2['d'].push(Math.round(ym*4.2*ts*100.0)/100.0) d2['e'].push(Math.round(ym*ts*100.0)/100.0) source.change.emit(); mytable.change.emit(); """)) # Layout the plot and the datatable bar save(layout([[dt], [plot]])) return plot, dt
def monsoon_data_plot_power(self, mon_info, monsoon_results, tag=''): """Plot the monsoon power data using bokeh interactive plotting tool. Args: mon_info: Dictionary with the monsoon packet config. monsoon_results: a MonsoonResult or list of MonsoonResult objects to to plot. tag: an extra tag to append to the resulting filename. """ if not isinstance(monsoon_results, list): monsoon_results = [monsoon_results] logging.info('Plotting the power measurement data.') voltage = monsoon_results[0].voltage total_current = 0 total_samples = 0 for result in monsoon_results: total_current += result.average_current * result.num_samples total_samples += result.num_samples avg_current = total_current / total_samples time_relative = [ data_point.time for monsoon_result in monsoon_results for data_point in monsoon_result.get_data_points() ] power_data = [ data_point.current * voltage for monsoon_result in monsoon_results for data_point in monsoon_result.get_data_points() ] total_data_points = sum( result.num_samples for result in monsoon_results) color = ['navy'] * total_data_points # Preparing the data and source link for bokehn java callback source = ColumnDataSource( data=dict(x0=time_relative, y0=power_data, color=color)) s2 = ColumnDataSource( data=dict( z0=[mon_info.duration], y0=[round(avg_current, 2)], x0=[round(avg_current * voltage, 2)], z1=[round(avg_current * voltage * mon_info.duration, 2)], z2=[round(avg_current * mon_info.duration, 2)])) # Setting up data table for the output columns = [ TableColumn(field='z0', title='Total Duration (s)'), TableColumn(field='y0', title='Average Current (mA)'), TableColumn(field='x0', title='Average Power (4.2v) (mW)'), TableColumn(field='z1', title='Average Energy (mW*s)'), TableColumn(field='z2', title='Normalized Average Energy (mA*s)') ] dt = DataTable( source=s2, columns=columns, width=1300, height=60, editable=True) plot_title = (os.path.basename( os.path.splitext(monsoon_results[0].tag)[0]) + tag) output_file(os.path.join(mon_info.data_path, plot_title + '.html')) tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save' # Create a new plot with the datatable above plot = figure( plot_width=1300, plot_height=700, title=plot_title, tools=tools, output_backend='webgl') plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='width')) plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='height')) plot.line('x0', 'y0', source=source, line_width=2) plot.circle('x0', 'y0', source=source, size=0.5, fill_color='color') plot.xaxis.axis_label = 'Time (s)' plot.yaxis.axis_label = 'Power (mW)' plot.title.text_font_size = {'value': '15pt'} jsscript = open(self.customjsfile, 'r') customjsscript = jsscript.read() # Callback Java scripting source.callback = CustomJS( args=dict(mytable=dt), code=customjsscript) # Layout the plot and the datatable bar save(layout([[dt], [plot]]))
def bokeh_chart_plot(bt_attenuation_range, data_sets, legends, fig_property, shaded_region=None, output_file_path=None): """Plot bokeh figs. Args: bt_attenuation_range: range of BT attenuation. data_sets: data sets including lists of x_data and lists of y_data ex: [[[x_data1], [x_data2]], [[y_data1],[y_data2]]] legends: list of legend for each curve fig_property: dict containing the plot property, including title, labels, linewidth, circle size, etc. shaded_region: optional dict containing data for plot shading output_file_path: optional path at which to save figure Returns: plot: bokeh plot figure object """ TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save') colors = [ 'red', 'green', 'blue', 'olive', 'orange', 'salmon', 'black', 'navy', 'yellow', 'darkred', 'goldenrod' ] plot = [] data = [[], []] legend = [] for i in bt_attenuation_range: if "Packet drop" in legends[i][0]: plot_info = {0: "A2dp_packet_drop_plot", 1: "throughput_plot"} else: plot_info = {0: "throughput_plot"} for j in plot_info: if "Packet drops" in legends[i][j]: if data_sets[i]["a2dp_packet_drops"]: plot_i_j = figure( plot_width=1000, plot_height=500, title=fig_property['title'], tools=TOOLS) plot_i_j.add_tools( bokeh_tools.WheelZoomTool(dimensions="width")) plot_i_j.add_tools( bokeh_tools.WheelZoomTool(dimensions="height")) plot_i_j.xaxis.axis_label = fig_property['x_label'] plot_i_j.yaxis.axis_label = fig_property['y_label'][j] plot_i_j.legend.location = "top_right" plot_i_j.legend.click_policy = "hide" plot_i_j.title.text_font_size = {'value': '15pt'} plot_i_j.line( data_sets[i]["a2dp_attenuation"], data_sets[i]["a2dp_packet_drops"], legend=legends[i][j], line_width=3, color=colors[j]) plot_i_j.circle( data_sets[i]["a2dp_attenuation"], data_sets[i]["a2dp_packet_drops"], legend=str(legends[i][j]), fill_color=colors[j]) plot.append(plot_i_j) elif "Performance Results" in legends[i][j]: plot_i_j = figure( plot_width=1000, plot_height=500, title=fig_property['title'], tools=TOOLS) plot_i_j.add_tools( bokeh_tools.WheelZoomTool(dimensions="width")) plot_i_j.add_tools( bokeh_tools.WheelZoomTool(dimensions="height")) plot_i_j.xaxis.axis_label = fig_property['x_label'] plot_i_j.yaxis.axis_label = fig_property['y_label'][j] plot_i_j.legend.location = "top_right" plot_i_j.legend.click_policy = "hide" plot_i_j.title.text_font_size = {'value': '15pt'} data[0].insert(0, data_sets[i]["attenuation"]) data[1].insert(0, data_sets[i]["throughput_received"]) legend.insert(0, legends[i][j + 1]) plot_i_j.line( data_sets[i]["user_attenuation"], data_sets[i]["user_throughput"], legend=legends[i][j], line_width=3, color=colors[j]) plot_i_j.circle( data_sets[i]["user_attenuation"], data_sets[i]["user_throughput"], legend=str(legends[i][j]), fill_color=colors[j]) plot_i_j.line( data_sets[i]["attenuation"], data_sets[i]["throughput_received"], legend=legends[i][j + 1], line_width=3, color=colors[j]) plot_i_j.circle( data_sets[i]["attenuation"], data_sets[i]["throughput_received"], legend=str(legends[i][j + 1]), fill_color=colors[j]) if shaded_region: band_x = shaded_region[i]["x_vector"] band_x.extend(shaded_region[i]["x_vector"][::-1]) band_y = shaded_region[i]["lower_limit"] band_y.extend(shaded_region[i]["upper_limit"][::-1]) plot_i_j.patch( band_x, band_y, color='#7570B3', line_alpha=0.1, fill_alpha=0.1) plot.append(plot_i_j) else: plot_i_j = figure( plot_width=1000, plot_height=500, title=fig_property['title'], tools=TOOLS) plot_i_j.add_tools( bokeh_tools.WheelZoomTool(dimensions="width")) plot_i_j.add_tools( bokeh_tools.WheelZoomTool(dimensions="height")) plot_i_j.xaxis.axis_label = fig_property['x_label'] plot_i_j.yaxis.axis_label = fig_property['y_label'][j] plot_i_j.legend.location = "top_right" plot_i_j.legend.click_policy = "hide" plot_i_j.title.text_font_size = {'value': '15pt'} data[0].insert(0, data_sets[i]["attenuation"]) data[1].insert(0, data_sets[i]["throughput_received"]) legend.insert(0, legends[i][j]) plot_i_j.line( data_sets[i]["attenuation"], data_sets[i]["throughput_received"], legend=legends[i][j], line_width=3, color=colors[j]) plot_i_j.circle( data_sets[i]["attenuation"], data_sets[i]["throughput_received"], legend=str(legends[i][j]), fill_color=colors[j]) plot.append(plot_i_j) fig_property['y_label'] = "Throughput (Mbps)" all_plot = bokeh_plot(data, legend, fig_property, shaded_region=None, output_file_path=None) plot.insert(0, all_plot) if output_file_path is not None: output_file(output_file_path) save(column(plot)) return plot
def bokeh_plot(data_sets, legends, fig_property, shaded_region=None, output_file_path=None): """Plot bokeh figs. Args: data_sets: data sets including lists of x_data and lists of y_data ex: [[[x_data1], [x_data2]], [[y_data1],[y_data2]]] legends: list of legend for each curve fig_property: dict containing the plot property, including title, labels, linewidth, circle size, etc. shaded_region: optional dict containing data for plot shading output_file_path: optional path at which to save figure Returns: plot: bokeh plot figure object """ tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save' plot = figure(plot_width=1300, plot_height=700, title=fig_property['title'], tools=tools, output_backend="webgl") plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="width")) plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="height")) colors = [ 'red', 'green', 'blue', 'olive', 'orange', 'salmon', 'black', 'navy', 'yellow', 'darkred', 'goldenrod' ] if shaded_region: band_x = shaded_region["x_vector"] band_x.extend(shaded_region["x_vector"][::-1]) band_y = shaded_region["lower_limit"] band_y.extend(shaded_region["upper_limit"][::-1]) plot.patch(band_x, band_y, color='#7570B3', line_alpha=0.1, fill_alpha=0.1) for x_data, y_data, legend in zip(data_sets[0], data_sets[1], legends): index_now = legends.index(legend) color = colors[index_now % len(colors)] plot.line(x_data, y_data, legend=str(legend), line_width=fig_property['linewidth'], color=color) plot.circle(x_data, y_data, size=fig_property['markersize'], legend=str(legend), fill_color=color) # Plot properties plot.xaxis.axis_label = fig_property['x_label'] plot.yaxis.axis_label = fig_property['y_label'] plot.legend.location = "top_right" plot.legend.click_policy = "hide" plot.title.text_font_size = {'value': '15pt'} if output_file_path is not None: output_file(output_file_path) save(plot) return plot
def current_waveform_plot(samples, voltage, dest_path, plot_title): """Plot the current data using bokeh interactive plotting tool. Plotting power measurement data with bokeh to generate interactive plots. You can do interactive data analysis on the plot after generating with the provided widgets, which make the debugging much easier. To realize that, bokeh callback java scripting is used. View a sample html output file: https://drive.google.com/open?id=0Bwp8Cq841VnpT2dGUUxLYWZvVjA Args: samples: a list of tuples in which the first element is a timestamp and the second element is the sampled current in milli amps at that time. voltage: the voltage that was used during the measurement. dest_path: destination path. plot_title: a filename and title for the plot. Returns: plot: the plotting object of bokeh, optional, will be needed if multiple plots will be combined to one html file. dt: the datatable object of bokeh, optional, will be needed if multiple datatables will be combined to one html file. """ logging.info('Plotting the power measurement data.') time_relative = [sample[0] for sample in samples] duration = time_relative[-1] - time_relative[0] current_data = [sample[1] * 1000 for sample in samples] avg_current = sum(current_data) / len(current_data) color = ['navy'] * len(samples) # Preparing the data and source link for bokehn java callback source = ColumnDataSource( data=dict(x=time_relative, y=current_data, color=color)) s2 = ColumnDataSource( data=dict(a=[duration], b=[round(avg_current, 2)], c=[round(avg_current * voltage, 2)], d=[round(avg_current * voltage * duration, 2)], e=[round(avg_current * duration, 2)])) # Setting up data table for the output columns = [ TableColumn(field='a', title='Total Duration (s)'), TableColumn(field='b', title='Average Current (mA)'), TableColumn(field='c', title='Average Power (4.2v) (mW)'), TableColumn(field='d', title='Average Energy (mW*s)'), TableColumn(field='e', title='Normalized Average Energy (mA*s)') ] dt = DataTable(source=s2, columns=columns, width=1300, height=60, editable=True) output_file(os.path.join(dest_path, plot_title + '.html')) tools = 'box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save' # Create a new plot with the datatable above plot = figure(plot_width=1300, plot_height=700, title=plot_title, tools=tools) plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='width')) plot.add_tools(bokeh_tools.WheelZoomTool(dimensions='height')) plot.line('x', 'y', source=source, line_width=2) plot.circle('x', 'y', source=source, size=0.5, fill_color='color') plot.xaxis.axis_label = 'Time (s)' plot.yaxis.axis_label = 'Current (mA)' plot.title.text_font_size = {'value': '15pt'} # Callback JavaScript source.selected.js_on_change( "indices", CustomJS(args=dict(source=source, mytable=dt), code=""" const inds = source.selected.indices; const d1 = source.data; const d2 = mytable.source.data; var ym = 0 var ts = 0 var min=d1['x'][inds[0]] var max=d1['x'][inds[0]] d2['a'] = [] d2['b'] = [] d2['c'] = [] d2['d'] = [] d2['e'] = [] if (inds.length==0) {return;} for (var i = 0; i < inds.length; i++) { ym += d1['y'][inds[i]] d1['color'][inds[i]] = "red" if (d1['x'][inds[i]] < min) { min = d1['x'][inds[i]]} if (d1['x'][inds[i]] > max) { max = d1['x'][inds[i]]} } ym /= inds.length ts = max - min d2['a'].push(Math.round(ts*1000.0)/1000.0) d2['b'].push(Math.round(ym*100.0)/100.0) d2['c'].push(Math.round(ym*4.2*100.0)/100.0) d2['d'].push(Math.round(ym*4.2*ts*100.0)/100.0) d2['e'].push(Math.round(ym*ts*100.0)/100.0) source.change.emit(); mytable.change.emit(); """)) # Layout the plot and the datatable bar save(layout([[dt], [plot]])) return plot, dt
def monsoon_data_plot(mon_info, file_path, tag=""): """Plot the monsoon current data using bokeh interactive plotting tool. Plotting power measurement data with bokeh to generate interactive plots. You can do interactive data analysis on the plot after generating with the provided widgets, which make the debugging much easier. To realize that, bokeh callback java scripting is used. View a sample html output file: https://drive.google.com/open?id=0Bwp8Cq841VnpT2dGUUxLYWZvVjA Args: mon_info: obj with information of monsoon measurement, including monsoon device object, measurement frequency, duration and offset etc. file_path: the path to the monsoon log file with current data Returns: plot: the plotting object of bokeh, optional, will be needed if multiple plots will be combined to one html file. dt: the datatable object of bokeh, optional, will be needed if multiple datatables will be combined to one html file. """ log = logging.getLogger() log.info("Plot the power measurement data") #Get results as monsoon data object from the input file results = monsoon.MonsoonData.from_text_file(file_path) #Decouple current and timestamp data from the monsoon object current_data = [] timestamps = [] voltage = results[0].voltage [current_data.extend(x.data_points) for x in results] [timestamps.extend(x.timestamps) for x in results] period = 1 / float(mon_info.freq) time_relative = [x * period for x in range(len(current_data))] #Calculate the average current for the test current_data = [x * 1000 for x in current_data] avg_current = sum(current_data) / len(current_data) color = ['navy'] * len(current_data) #Preparing the data and source link for bokehn java callback source = ColumnDataSource( data=dict(x0=time_relative, y0=current_data, color=color)) s2 = ColumnDataSource( data=dict(z0=[mon_info.duration], y0=[round(avg_current, 2)], x0=[round(avg_current * voltage, 2)], z1=[round(avg_current * voltage * mon_info.duration, 2)], z2=[round(avg_current * mon_info.duration, 2)])) #Setting up data table for the output columns = [ TableColumn(field='z0', title='Total Duration (s)'), TableColumn(field='y0', title='Average Current (mA)'), TableColumn(field='x0', title='Average Power (4.2v) (mW)'), TableColumn(field='z1', title='Average Energy (mW*s)'), TableColumn(field='z2', title='Normalized Average Energy (mA*s)') ] dt = DataTable(source=s2, columns=columns, width=1300, height=60, editable=True) plot_title = file_path[file_path.rfind('/') + 1:-4] + tag output_file("%s/%s.html" % (mon_info.data_path, plot_title)) TOOLS = ('box_zoom,box_select,pan,crosshair,redo,undo,reset,hover,save') # Create a new plot with the datatable above plot = figure(plot_width=1300, plot_height=700, title=plot_title, tools=TOOLS, output_backend="webgl") plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="width")) plot.add_tools(bokeh_tools.WheelZoomTool(dimensions="height")) plot.line('x0', 'y0', source=source, line_width=2) plot.circle('x0', 'y0', source=source, size=0.5, fill_color='color') plot.xaxis.axis_label = 'Time (s)' plot.yaxis.axis_label = 'Current (mA)' plot.title.text_font_size = {'value': '15pt'} #Callback Java scripting source.callback = CustomJS(args=dict(mytable=dt), code=""" var inds = cb_obj.get('selected')['1d'].indices; var d1 = cb_obj.get('data'); var d2 = mytable.get('source').get('data'); ym = 0 ts = 0 d2['x0'] = [] d2['y0'] = [] d2['z1'] = [] d2['z2'] = [] d2['z0'] = [] min=max=d1['x0'][inds[0]] if (inds.length==0) {return;} for (i = 0; i < inds.length; i++) { ym += d1['y0'][inds[i]] d1['color'][inds[i]] = "red" if (d1['x0'][inds[i]] < min) { min = d1['x0'][inds[i]]} if (d1['x0'][inds[i]] > max) { max = d1['x0'][inds[i]]} } ym /= inds.length ts = max - min dx0 = Math.round(ym*4.2*100.0)/100.0 dy0 = Math.round(ym*100.0)/100.0 dz1 = Math.round(ym*4.2*ts*100.0)/100.0 dz2 = Math.round(ym*ts*100.0)/100.0 dz0 = Math.round(ts*1000.0)/1000.0 d2['z0'].push(dz0) d2['x0'].push(dx0) d2['y0'].push(dy0) d2['z1'].push(dz1) d2['z2'].push(dz2) mytable.trigger('change'); """) #Layout the plot and the datatable bar l = layout([[dt], [plot]]) save(l) return [plot, dt]