def tw_hourly_create(hourly_count_avg_df): # Gaussian kernel density estimation norm_c = np.array(hourly_count_avg_df['count']) / np.array( hourly_count_avg_df['count'].sum()) w1 = [ int(i) * [ind] for ind, i in enumerate(list(hourly_count_avg_df['count'])) ] w2 = [k for j in w1 for k in j] pdf = gaussian_kde(pd.Series(w2)) # initialize figure norm_c_df = ColumnDataSource(pd.DataFrame(norm_c, columns=['prob'])) hover = HoverTool(tooltips=[('Hour', '@index')], toggleable=False) p = figure(title=" ", plot_width=600, plot_height=500, tools=[hover]) # add data vbars b = p.vbar(x='index', top='prob', width=0.6, alpha=0.2, line_color='#1A96F8', fill_color='#1A96F8', source=norm_c_df) p.line(linspace(0, 23), pdf(linspace(0, 23)), line_width=4, alpha=0.8, line_color='#1A96F8') # prettify p.xaxis.axis_label_text_font_style = "normal" p.xaxis.axis_label_text_font = "Raleway" p.yaxis.axis_label_text_font_style = "normal" p.yaxis.axis_label_text_font = "Raleway" p.yaxis.axis_label_text_font_size = '15pt' p.xaxis.axis_label_text_font_size = '15pt' p.xaxis.major_label_text_font = 'Raleway' p.xaxis.major_label_text_font_size = '12pt' p.yaxis.major_label_text_font = 'Raleway' p.yaxis.major_label_text_font_size = '12pt' p.ygrid.grid_line_alpha = 0.8 p.xgrid.grid_line_alpha = None p.yaxis.minor_tick_line_color = None p.x_range.range_padding = 0.1 p.outline_line_color = None p.xaxis.minor_tick_line_color = None p.xaxis.axis_label = "Hour" p.yaxis.axis_label = "Probability of tweets" p.toolbar.logo = None p.toolbar_location = None hover.renderers = [b] p.title.text_font = "Raleway" p.title.text_font_style = "normal" p.title.align = 'center' p.title.text_color = 'black' p.title.text_font_size = '18pt' return p
def make_timeseries_plot(src, tooltipSrc): p = figure(plot_width=600, plot_height=600, title='Daily message counts by date', x_axis_type='datetime', x_axis_label='Date', y_axis_label='Message count') p.multi_line(xs='x', ys='y', source=src, color='color', line_width=3, legend_field='label', line_alpha=0.4) tooltipScatter = p.scatter('x', 'y', source=tooltipSrc, alpha=0) hover = HoverTool( tooltips=[('Message count', '@y'), ('Details', '@x{%F}, @label')], formatters={'@x': 'datetime'}, mode='vline' ) # vline means that tooltip will be shown when mouse is in a vertical line above glyph hover.renderers = [tooltipScatter] p.add_tools(hover) return p
def co2_barplot(decision_vars,dic,cmap): tec = list(dic[decision_vars[0]].keys()) total = [str(EngNumber(round(sum([dic[key][j] for key in decision_vars]),2))) for j in tec] #FIXME: Use Numberformater columns = [TableColumn(field="tec", title="Heat Producers")] + [TableColumn(field=key, title=key) for key in decision_vars] columns += [TableColumn(field="total", title="Total")] data = {**{key:[str(EngNumber(round(dic[key][j],2))) for j in tec] for key in decision_vars},**dict(tec=tec),**dict(total=total),} source = ColumnDataSource(data) data_table = DataTable(source=source, columns=columns, width=600, height=400) hover = HoverTool(tooltips=[ ("heat producer", "@hp"), ("Total CO2 Emission", "@co2_total kg"), ("CO2 Emission from Heat", "@co2_heat kg"), ("CO2 Emission from Electricity", "@co2_electricity kg") ], mode='mouse') hover.renderers = [] # for bokeh=1.0.4 p = figure(title = "CO2 Emissions of Technologies by Heat and Electricity", plot_width=700 , plot_height=400, tools = [hover,"pan,wheel_zoom,box_zoom,reset,save"]) p.xaxis.visible = False p.xgrid.visible = False p.grid.minor_grid_line_color = '#eeeeee' p.toolbar.logo = None p.toolbar_location = None val = [sum([dic[key][j] for key in decision_vars]) for j in tec] index = [i for i,v in enumerate(val) if v == 0] val = [v for i,v in enumerate(val) if i not in index] tec = [t for i,t in enumerate(tec) if i not in index] legend_items = [] l = len(decision_vars) fill_alpha= np.linspace(1,1/l,l,True) for i,j in enumerate(tec): save=0 items = [] for k,key in enumerate(decision_vars): items += [p.rect(x=5+i*15, y = save+dic[key][j]/2 , width=10, height= dic[key][j] , fill_alpha= fill_alpha[k], color=cmap[j],muted_alpha=0.2)] save += dic[key][j] legend_items += [(j,items)] hover.renderers += [ p.rect(x=5+i*15, y = val[i]/2 , width=10, height= val[i] , fill_alpha= None, color=None, source = {"hp":[j], "co2_total":[ str(EngNumber(round(val[i],2)))], "co2_heat":[str(EngNumber(round(dic[decision_vars[0]][j],2)))], "co2_electricity":[str(EngNumber(round(dic[decision_vars[1]][j],2)))]})] legend = Legend(items=legend_items,location=(10, 10)) p.add_layout(legend, 'right') p.legend.click_policy = "hide" # "mute" if tec == []: return layout([widgetbox(data_table)]) else: return layout([p,widgetbox(data_table)])
def plot_bokeh_events(src, plot=None, pitch_colour='green', event_hover=True): from bokeh.models import Arrow, OpenHead, LabelSet, HoverTool, Segment import Football_Pitch_Bokeh as fpbokeh line_colour = 'white' if pitch_colour == 'white': line_colour = 'black' if plot is None: p = fpbokeh.draw_pitch(hspan=[-53, 53], vspan=[-34, 34], fill_color=pitch_colour, line_color=line_colour) else: p = plot players = p.circle(source=src, x='Start X', y='Start Y', color='red', size=10, alpha=0.7) glyph = Segment(x0="Start X", y0="Start Y", x1="End X", y1="End Y", line_color="black", line_width=1) p.add_glyph(src, glyph) p.add_layout( LabelSet(x='Start X', y='Start Y', x_offset=3, y_offset=3, text='Shirt Number', text_font_size='10pt', text_color='red', source=src, level='glyph')) if event_hover == True: hover = HoverTool() hover.mode = 'mouse' hover.tooltips = [("Event", "@play_frame"), ("Player", "@{Shirt Number}"), ("Type", "@Type"), ("SubType", "@SubType"), ("(Start X,Start Y)", "($x, $y)")] hover.renderers = [players] p.add_tools(hover) return p
def plot_bokeh_surface_at_event(event_src, att_src, def_src, ball_src, surface_src, bokeh_attack, bokeh_defence, xgrid, ygrid, field_dimen=( 106., 68., ), new_grid_size=500, point_click=False): import matplotlib as plt import matplotlib.cm as cm import numpy as np from bokeh.models import ColumnDataSource, HoverTool, BooleanFilter, CDSView, LabelSet, PointDrawTool, Tabs colormap = cm.get_cmap("bwr") bokehpalette = [ plt.colors.rgb2hex(m) for m in colormap(np.arange(colormap.N)) ] p = plot_bokeh_frame(att_src, def_src, ball_src, pitch_colour='white', edit=point_click, tracking_hover=False) p = plot_bokeh_events(event_src, p, pitch_colour='white') surface = p.image(source=surface_src, image='image', x='x', y='y', dw='dw', dh='dh', palette=bokehpalette, level="image") hover = HoverTool() hover.mode = 'mouse' hover.tooltips = [("Value", "@image"), ("(x,y)", "($x, $y)")] hover.renderers = [surface] p.add_tools(hover) return p
def createHoverTool(simg, cols): """ """ # Make the hovertool only follow the patches (still a hack) htline = simg ht = HoverTool() ht.tooltips = [("Time", "@index{%F %T}")] for col in cols: fStr = "@%s{0.00}" % (col) ht.tooltips.append((col, fStr)) ht.formatters = {'index': 'datetime'} ht.show_arrow = False ht.point_policy = 'follow_mouse' ht.line_policy = 'nearest' ht.renderers = [htline] return ht
def _make_graph_plot(self): """ Builds the graph portion of the final model. """ import networkx as nx nodes = nx.nx_pydot.graphviz_layout(self._graph, prog="dot") node_x, node_y = zip(*nodes.values()) models = [self._graph.nodes[x]["model"] for x in nodes] node_id = list(nodes.keys()) node_source = ColumnDataSource( {"x": node_x, "y": node_y, "index": node_id, "model": models} ) edge_x_coords = [] edge_y_coords = [] for start_node, end_node in self._graph.edges: edge_x_coords.extend([[nodes[start_node][0], nodes[end_node][0]]]) edge_y_coords.extend([[nodes[start_node][1], nodes[end_node][1]]]) edge_source = ColumnDataSource({"xs": edge_x_coords, "ys": edge_y_coords}) p2 = Plot(outline_line_alpha=0.0) xinterval = max(max(node_x) - min(node_x), 200) yinterval = max(max(node_y) - min(node_y), 200) p2.x_range = Range1d( start=min(node_x) - 0.15 * xinterval, end=max(node_x) + 0.15 * xinterval ) p2.y_range = Range1d( start=min(node_y) - 0.15 * yinterval, end=max(node_y) + 0.15 * yinterval ) node_renderer = GlyphRenderer( data_source=node_source, glyph=Circle(x="x", y="y", size=15, fill_color="lightblue"), nonselection_glyph=Circle(x="x", y="y", size=15, fill_color="lightblue"), selection_glyph=Circle(x="x", y="y", size=15, fill_color="green"), ) edge_renderer = GlyphRenderer( data_source=edge_source, glyph=MultiLine(xs="xs", ys="ys") ) node_hover_tool = HoverTool(tooltips=[("id", "@index"), ("model", "@model")]) node_hover_tool.renderers = [node_renderer] tap_tool = TapTool() tap_tool.renderers = [node_renderer] labels = LabelSet( x="x", y="y", text="model", source=node_source, text_font_size="8pt", x_offset=-20, y_offset=7, ) help = Label( x=20, y=20, x_units="screen", y_units="screen", text_font_size="8pt", text_font_style="italic", text="Click on a model to see its attributes", ) p2.add_layout(help) p2.add_layout(edge_renderer) p2.add_layout(node_renderer) p2.tools.extend( [node_hover_tool, tap_tool, BoxZoomTool(), ResetTool(), PanTool()] ) p2.renderers.append(labels) self._node_source = node_source self._edge_source = edge_source return p2
# Hovertool für detailinformationen hinzufügen. TODO formatting h = HoverTool(mode='vline') h.tooltips = [ ('Brutto-Umsatz', '@umsatz_brutto €'), ('',''), ('P-alt: Basisrate auf Brutto', '@provisionsrate_alt{0.00 a}'), ('P-alt (untergrenze)', '@provision_alt_min{0.00 a} €'), ('P-alt (obergrenze)', '@provision_alt_max{0.00 a} €'), ('P-alt (tatsächlich)', '@provision_alt_actual{0.00 a} €'), ('',''), ('P-neu: Basisrate auf Brutto', '@provisionsrate_neu{0.00 a}'), ('P-neu (basis)', '@provision_neu{0.00 a} €'), ('P-neu (mit Teamumsatz)', '@provision_neu_plusteam{0.00 a} €'), ] h.renderers = [p5] plot.add_tools(h) # Eingabemöglichkeiten teamumsatz=0000 anteil_hausmarke=66 anteil_3prozent=100 #UI-Steuerelemente anteil_hausmarke_slider = Slider(start=0, end=100, step=1, value=anteil_hausmarke, title='ALT: Anteil Eigenmarkeprodukte in %') teamumsatz_slider = Slider(start=0, end=10000, step=50, value=teamumsatz, title='NEU: Teamumsatz / Monat in €') anteil_3prozent_slider = Slider(start=0, end=100, step=1, value=anteil_3prozent, title='NEU: Anteil 3% auf Teamumsatz') set_data(anteil_hausmarke, teamumsatz, anteil_3prozent) #Callbacks für UI-Steuerelemente def anteil_hausmarke_slider_callback(attr, old, new):
fig.yaxis.axis_label = FIGURE_Y_AXIS_LABEL fig.xaxis.axis_label_standoff = FIGURE_X_AXIS_LABEL_STANDOFF fig.yaxis.axis_label_standoff = FIGURE_Y_AXIS_LABEL_STANDOFF fig.xaxis.axis_label_text_font_size = FIGURE_X_AXIS_FONT_SIZE fig.yaxis.axis_label_text_font_size = FIGURE_Y_AXIS_FONT_SIZE fig.xaxis.axis_label_text_font_style = FIGURE_X_AXIS_FONT_STYLE fig.yaxis.axis_label_text_font_style = FIGURE_Y_AXIS_FONT_STYLE fig.yaxis.minor_tick_line_color = None fig.xaxis.minor_tick_line_color = None fig.xaxis.ticker.mantissas = FIGURE_X_AXIS_MANTISSAS fig.xaxis.ticker.min_interval = FIGURE_X_AXIS_MIN_INTERVAL fig.xaxis.ticker.desired_num_ticks = FIGURE_X_AXIS_DESIRED_NUM_TICKS hover = HoverTool() hover.tooltips = HOVER_TOOLTIPS hover.mode = HOVER_MODE hover.renderers = [line] fig.add_tools(hover) ############################# Controls ############################## prevalence_control = Slider(title = PREVALENCE_TITLE, start = PREVALENCE_START, end = PREVALENCE_END, value = PREVALENCE_VALUE, step = PREVALENCE_STEP, format = NumeralTickFormatter(format = PREVALENCE_FORMAT), sizing_mode = PREVALENCE_SIZING_MODE) correlation_control = Slider(title = CORRELATION_TITLE, start = CORRELATION_START, end = CORRELATION_END, value = CORRELATION_VALUE, step = CORRELATION_STEP,
def plotPeakResult(id_input, peakresult, outputfile="result.html"): """ Plots the peak results. Args: id_input (IdentificationInput): Identification input. peakresult (PeakResults): Peak result outputfile (str): The html file coontaining the plot """ energyScale = peakresult.getFit().energyScale centers = energyScale.getCenters() fit = peakresult.getFit().counts continuum = peakresult.getContinuum().counts hover = HoverTool() hover.mode = 'mouse' # activate hover by vertical line hover.tooltips = [("energy", "@energy"), ("intensity", "@intensity"), ("baseline", "@baseline"), ("width", "@width")] hover.renderers = [] panels = [] for axis_type in ["linear", "log"]: fig = figure(title="Peak Fitting Results", y_axis_type=axis_type, x_axis_label='Energy (keV)', y_axis_label='Counts') fig.varea(x=centers, y1=continuum, y2=fit, color="green", legend_label="peaks") for peak in peakresult.getPeaks(): ymax = fit[energyScale.findBin(peak.energy)] source = ColumnDataSource( data=dict(x=[peak.energy, peak.energy], y=[10e-10, ymax], energy=["%6.1f" % peak.energy] * 2, intensity=["%d" % peak.intensity] * 2, baseline=["%d" % peak.baseline] * 2, width=["%6.2f" % peak.width] * 2)) pline = fig.line('x', 'y', color="red", source=source) hover.renderers.append(pline) fig.add_tools(hover) fig.varea(x=centers, y1=np.ones(continuum.size) * 10e-10, y2=continuum, color="blue", legend_label="continuum") fig.line(centers, id_input.sample.counts, legend_label="sample", line_dash='dashed', color="black", line_width=2) panel = Panel(child=fig, title=axis_type) panels.append(panel) tabs = Tabs(tabs=panels) # add a line renderer with legend and line thickness # show the results output_file(outputfile) show(tabs)
def photometry_plot(obj_id, user, width=600, height=300): """Create scatter plot of photometry for object. Parameters ---------- obj_id : str ID of Obj to be plotted. Returns ------- (str, str) Returns (docs_json, render_items) json for the desired plot. """ data = pd.read_sql( DBSession().query( Photometry, Telescope.nickname.label("telescope"), Instrument.name.label("instrument"), ).join(Instrument, Instrument.id == Photometry.instrument_id).join( Telescope, Telescope.id == Instrument.telescope_id).filter( Photometry.obj_id == obj_id).filter( Photometry.groups.any( Group.id.in_([g.id for g in user.accessible_groups ]))).statement, DBSession().bind, ) if data.empty: return None, None, None data['color'] = [get_color(f) for f in data['filter']] data['label'] = [ f'{i}/{f}' for i, f in zip(data['instrument'], data['filter']) ] data['zp'] = PHOT_ZP data['magsys'] = 'ab' data['alpha'] = 1.0 data['lim_mag'] = -2.5 * np.log10( data['fluxerr'] * DETECT_THRESH) + data['zp'] # Passing a dictionary to a bokeh datasource causes the frontend to die, # deleting the dictionary column fixes that del data['original_user_data'] # keep track of things that are only upper limits data['hasflux'] = ~data['flux'].isna() # calculate the magnitudes - a photometry point is considered "significant" # or "detected" (and thus can be represented by a magnitude) if its snr # is above DETECT_THRESH obsind = data['hasflux'] & (data['flux'].fillna(0.0) / data['fluxerr'] >= DETECT_THRESH) data.loc[~obsind, 'mag'] = None data.loc[obsind, 'mag'] = -2.5 * np.log10(data[obsind]['flux']) + PHOT_ZP # calculate the magnitude errors using standard error propagation formulae # https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae data.loc[~obsind, 'magerr'] = None coeff = 2.5 / np.log(10) magerrs = np.abs(coeff * data[obsind]['fluxerr'] / data[obsind]['flux']) data.loc[obsind, 'magerr'] = magerrs data['obs'] = obsind data['stacked'] = False split = data.groupby('label', sort=False) finite = np.isfinite(data['flux']) fdata = data[finite] lower = np.min(fdata['flux']) * 0.95 upper = np.max(fdata['flux']) * 1.05 plot = figure( aspect_ratio=1.7, sizing_mode='scale_both', active_drag='box_zoom', tools='box_zoom,wheel_zoom,pan,reset,save', y_range=(lower, upper), ) imhover = HoverTool(tooltips=tooltip_format) imhover.renderers = [] plot.add_tools(imhover) model_dict = {} for i, (label, sdf) in enumerate(split): # for the flux plot, we only show things that have a flux value df = sdf[sdf['hasflux']] key = f'obs{i}' model_dict[key] = plot.scatter( x='mjd', y='flux', color='color', marker='circle', fill_color='color', alpha='alpha', source=ColumnDataSource(df), ) imhover.renderers.append(model_dict[key]) key = f'bin{i}' model_dict[key] = plot.scatter( x='mjd', y='flux', color='color', marker='circle', fill_color='color', source=ColumnDataSource(data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], stacked=[], instrument=[], )), ) imhover.renderers.append(model_dict[key]) key = 'obserr' + str(i) y_err_x = [] y_err_y = [] for d, ro in df.iterrows(): px = ro['mjd'] py = ro['flux'] err = ro['fluxerr'] y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', source=ColumnDataSource(data=dict(xs=y_err_x, ys=y_err_y, color=df['color'], alpha=[1.0] * len(df))), ) key = f'binerr{i}' model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])), ) plot.xaxis.axis_label = 'MJD' plot.yaxis.axis_label = 'Flux (μJy)' plot.toolbar.logo = None colors_labels = data[['color', 'label']].drop_duplicates() toggle = CheckboxWithLegendGroup( labels=colors_labels.label.tolist(), active=list(range(len(colors_labels))), colors=colors_labels.color.tolist(), width=width // 5, ) # TODO replace `eval` with Namespaces # https://github.com/bokeh/bokeh/pull/6340 toggle.js_on_click( CustomJS( args={ 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'togglef.js')).read(), )) slider = Slider(start=0.0, end=15.0, value=0.0, step=1.0, title='Binsize (days)', max_width=350) callback = CustomJS( args={ 'slider': slider, 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackf.js')).read().replace('default_zp', str(PHOT_ZP)).replace( 'detect_thresh', str(DETECT_THRESH)), ) slider.js_on_change('value', callback) # Mark the first and last detections detection_dates = data[data['hasflux']]['mjd'] if len(detection_dates) > 0: first = round(detection_dates.min(), 6) last = round(detection_dates.max(), 6) first_color = "#34b4eb" last_color = "#8992f5" midpoint = (upper + lower) / 2 line_top = 5 * upper - 4 * midpoint line_bottom = 5 * lower - 4 * midpoint y = np.linspace(line_bottom, line_top, num=5000) first_r = plot.line( x=np.full(5000, first), y=y, line_alpha=0.5, line_color=first_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("First detection", f'{first}')], renderers=[first_r], )) last_r = plot.line( x=np.full(5000, last), y=y, line_alpha=0.5, line_color=last_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("Last detection", f'{last}')], renderers=[last_r], )) layout = column(slider, row(plot, toggle), sizing_mode='scale_width', width=width) p1 = Panel(child=layout, title='Flux') # now make the mag light curve ymax = (np.nanmax(( np.nanmax(data.loc[obsind, 'mag']) if any(obsind) else np.nan, np.nanmax(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan, )) + 0.1) ymin = (np.nanmin(( np.nanmin(data.loc[obsind, 'mag']) if any(obsind) else np.nan, np.nanmin(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan, )) - 0.1) xmin = data['mjd'].min() - 2 xmax = data['mjd'].max() + 2 plot = figure( aspect_ratio=1.5, sizing_mode='scale_both', active_drag='box_zoom', tools='box_zoom,wheel_zoom,pan,reset,save', y_range=(ymax, ymin), x_range=(xmin, xmax), toolbar_location='above', toolbar_sticky=False, x_axis_location='above', ) # Mark the first and last detections again detection_dates = data[obsind]['mjd'] if len(detection_dates) > 0: first = round(detection_dates.min(), 6) last = round(detection_dates.max(), 6) midpoint = (ymax + ymin) / 2 line_top = 5 * ymax - 4 * midpoint line_bottom = 5 * ymin - 4 * midpoint y = np.linspace(line_bottom, line_top, num=5000) first_r = plot.line( x=np.full(5000, first), y=y, line_alpha=0.5, line_color=first_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("First detection", f'{first}')], renderers=[first_r], )) last_r = plot.line( x=np.full(5000, last), y=y, line_alpha=0.5, line_color=last_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("Last detection", f'{last}')], renderers=[last_r], point_policy='follow_mouse', )) imhover = HoverTool(tooltips=tooltip_format) imhover.renderers = [] plot.add_tools(imhover) model_dict = {} for i, (label, df) in enumerate(split): key = f'obs{i}' model_dict[key] = plot.scatter( x='mjd', y='mag', color='color', marker='circle', fill_color='color', alpha='alpha', source=ColumnDataSource(df[df['obs']]), ) imhover.renderers.append(model_dict[key]) unobs_source = df[~df['obs']].copy() unobs_source.loc[:, 'alpha'] = 0.8 key = f'unobs{i}' model_dict[key] = plot.scatter( x='mjd', y='lim_mag', color='color', marker='inverted_triangle', fill_color='white', line_color='color', alpha='alpha', source=ColumnDataSource(unobs_source), ) imhover.renderers.append(model_dict[key]) key = f'bin{i}' model_dict[key] = plot.scatter( x='mjd', y='mag', color='color', marker='circle', fill_color='color', source=ColumnDataSource(data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], instrument=[], stacked=[], )), ) imhover.renderers.append(model_dict[key]) key = 'obserr' + str(i) y_err_x = [] y_err_y = [] for d, ro in df[df['obs']].iterrows(): px = ro['mjd'] py = ro['mag'] err = ro['magerr'] y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', source=ColumnDataSource(data=dict( xs=y_err_x, ys=y_err_y, color=df[df['obs']]['color'], alpha=[1.0] * len(df[df['obs']]), )), ) key = f'binerr{i}' model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])), ) key = f'unobsbin{i}' model_dict[key] = plot.scatter( x='mjd', y='lim_mag', color='color', marker='inverted_triangle', fill_color='white', line_color='color', alpha=0.8, source=ColumnDataSource(data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], instrument=[], stacked=[], )), ) imhover.renderers.append(model_dict[key]) key = f'all{i}' model_dict[key] = ColumnDataSource(df) key = f'bold{i}' model_dict[key] = ColumnDataSource(df[[ 'mjd', 'flux', 'fluxerr', 'mag', 'magerr', 'filter', 'zp', 'magsys', 'lim_mag', 'stacked', ]]) plot.xaxis.axis_label = 'MJD' plot.yaxis.axis_label = 'AB mag' plot.toolbar.logo = None obj = DBSession().query(Obj).get(obj_id) if obj.dm is not None: plot.extra_y_ranges = { "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm) } plot.add_layout( LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right') now = Time.now().mjd plot.extra_x_ranges = { "Days Ago": Range1d(start=now - xmin, end=now - xmax) } plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"), 'below') colors_labels = data[['color', 'label']].drop_duplicates() toggle = CheckboxWithLegendGroup( labels=colors_labels.label.tolist(), active=list(range(len(colors_labels))), colors=colors_labels.color.tolist(), width=width // 5, ) # TODO replace `eval` with Namespaces # https://github.com/bokeh/bokeh/pull/6340 toggle.js_on_click( CustomJS( args={ 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'togglem.js')).read(), )) slider = Slider(start=0.0, end=15.0, value=0.0, step=1.0, title='Binsize (days)') button = Button(label="Export Bold Light Curve to CSV") button.js_on_click( CustomJS( args={ 'slider': slider, 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', "download.js")).read().replace('objname', obj_id).replace( 'default_zp', str(PHOT_ZP)), )) toplay = row(slider, button) callback = CustomJS( args={ 'slider': slider, 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackm.js')).read().replace('default_zp', str(PHOT_ZP)).replace( 'detect_thresh', str(DETECT_THRESH)), ) slider.js_on_change('value', callback) layout = column(toplay, row(plot, toggle), sizing_mode='scale_width', width=width) p2 = Panel(child=layout, title='Mag') tabs = Tabs(tabs=[p2, p1], width=width, height=height, sizing_mode='fixed') return bokeh_embed.json_item(tabs)
def photometry_plot(obj_id, user, width=600, height=300, device="browser"): """Create object photometry scatter plot. Parameters ---------- obj_id : str ID of Obj to be plotted. Returns ------- dict Returns Bokeh JSON embedding for the desired plot. """ data = pd.read_sql( DBSession().query( Photometry, Telescope.nickname.label("telescope"), Instrument.name.label("instrument"), ).join(Instrument, Instrument.id == Photometry.instrument_id).join( Telescope, Telescope.id == Instrument.telescope_id).filter( Photometry.obj_id == obj_id).filter( Photometry.groups.any( Group.id.in_([g.id for g in user.accessible_groups ]))).statement, DBSession().bind, ) if data.empty: return None, None, None # get spectra to annotate on phot plots spectra = (Spectrum.query_records_accessible_by(user).filter( Spectrum.obj_id == obj_id).all()) data['color'] = [get_color(f) for f in data['filter']] labels = [] for i, datarow in data.iterrows(): label = f'{datarow["instrument"]}/{datarow["filter"]}' if datarow['origin'] is not None: label += f'/{datarow["origin"]}' labels.append(label) data['label'] = labels data['zp'] = PHOT_ZP data['magsys'] = 'ab' data['alpha'] = 1.0 data['lim_mag'] = ( -2.5 * np.log10(data['fluxerr'] * PHOT_DETECTION_THRESHOLD) + data['zp']) # Passing a dictionary to a bokeh datasource causes the frontend to die, # deleting the dictionary column fixes that del data['original_user_data'] # keep track of things that are only upper limits data['hasflux'] = ~data['flux'].isna() # calculate the magnitudes - a photometry point is considered "significant" # or "detected" (and thus can be represented by a magnitude) if its snr # is above PHOT_DETECTION_THRESHOLD obsind = data['hasflux'] & (data['flux'].fillna(0.0) / data['fluxerr'] >= PHOT_DETECTION_THRESHOLD) data.loc[~obsind, 'mag'] = None data.loc[obsind, 'mag'] = -2.5 * np.log10(data[obsind]['flux']) + PHOT_ZP # calculate the magnitude errors using standard error propagation formulae # https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae data.loc[~obsind, 'magerr'] = None coeff = 2.5 / np.log(10) magerrs = np.abs(coeff * data[obsind]['fluxerr'] / data[obsind]['flux']) data.loc[obsind, 'magerr'] = magerrs data['obs'] = obsind data['stacked'] = False split = data.groupby('label', sort=False) finite = np.isfinite(data['flux']) fdata = data[finite] lower = np.min(fdata['flux']) * 0.95 upper = np.max(fdata['flux']) * 1.05 active_drag = None if "mobile" in device or "tablet" in device else "box_zoom" tools = ('box_zoom,pan,reset' if "mobile" in device or "tablet" in device else "box_zoom,wheel_zoom,pan,reset,save") plot = figure( aspect_ratio=2.0 if device == "mobile_landscape" else 1.5, sizing_mode='scale_both', active_drag=active_drag, tools=tools, toolbar_location='above', toolbar_sticky=True, y_range=(lower, upper), min_border_right=16, ) imhover = HoverTool(tooltips=tooltip_format) imhover.renderers = [] plot.add_tools(imhover) model_dict = {} for i, (label, sdf) in enumerate(split): # for the flux plot, we only show things that have a flux value df = sdf[sdf['hasflux']] key = f'obs{i}' model_dict[key] = plot.scatter( x='mjd', y='flux', color='color', marker='circle', fill_color='color', alpha='alpha', source=ColumnDataSource(df), ) imhover.renderers.append(model_dict[key]) key = f'bin{i}' model_dict[key] = plot.scatter( x='mjd', y='flux', color='color', marker='circle', fill_color='color', source=ColumnDataSource(data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], stacked=[], instrument=[], )), ) imhover.renderers.append(model_dict[key]) key = 'obserr' + str(i) y_err_x = [] y_err_y = [] for d, ro in df.iterrows(): px = ro['mjd'] py = ro['flux'] err = ro['fluxerr'] y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', source=ColumnDataSource(data=dict(xs=y_err_x, ys=y_err_y, color=df['color'], alpha=[1.0] * len(df))), ) key = f'binerr{i}' model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])), ) plot.xaxis.axis_label = 'MJD' if device == "mobile_portrait": plot.xaxis.ticker.desired_num_ticks = 5 plot.yaxis.axis_label = 'Flux (μJy)' plot.toolbar.logo = None colors_labels = data[['color', 'label']].drop_duplicates() toggle = CheckboxWithLegendGroup( labels=colors_labels.label.tolist(), active=list(range(len(colors_labels))), colors=colors_labels.color.tolist(), width=width // 5, inline=True if "tablet" in device else False, ) # TODO replace `eval` with Namespaces # https://github.com/bokeh/bokeh/pull/6340 toggle.js_on_click( CustomJS( args={ 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'togglef.js')).read(), )) slider = Slider( start=0.0, end=15.0, value=0.0, step=1.0, title='Binsize (days)', max_width=350, margin=(4, 10, 0, 10), ) callback = CustomJS( args={ 'slider': slider, 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackf.js')).read().replace( 'default_zp', str(PHOT_ZP)).replace( 'detect_thresh', str(PHOT_DETECTION_THRESHOLD)), ) slider.js_on_change('value', callback) # Mark the first and last detections detection_dates = data[data['hasflux']]['mjd'] if len(detection_dates) > 0: first = round(detection_dates.min(), 6) last = round(detection_dates.max(), 6) first_color = "#34b4eb" last_color = "#8992f5" midpoint = (upper + lower) / 2 line_top = 5 * upper - 4 * midpoint line_bottom = 5 * lower - 4 * midpoint y = np.linspace(line_bottom, line_top, num=5000) first_r = plot.line( x=np.full(5000, first), y=y, line_alpha=0.5, line_color=first_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("First detection", f'{first}')], renderers=[first_r], )) last_r = plot.line( x=np.full(5000, last), y=y, line_alpha=0.5, line_color=last_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("Last detection", f'{last}')], renderers=[last_r], )) # Mark when spectra were taken annotate_spec(plot, spectra, lower, upper) plot_layout = (column(plot, toggle) if "mobile" in device or "tablet" in device else row(plot, toggle)) layout = column(slider, plot_layout, sizing_mode='scale_width', width=width) p1 = Panel(child=layout, title='Flux') # now make the mag light curve ymax = (np.nanmax(( np.nanmax(data.loc[obsind, 'mag']) if any(obsind) else np.nan, np.nanmax(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan, )) + 0.1) ymin = (np.nanmin(( np.nanmin(data.loc[obsind, 'mag']) if any(obsind) else np.nan, np.nanmin(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan, )) - 0.1) xmin = data['mjd'].min() - 2 xmax = data['mjd'].max() + 2 plot = figure( aspect_ratio=2.0 if device == "mobile_landscape" else 1.5, sizing_mode='scale_both', width=width, active_drag=active_drag, tools=tools, y_range=(ymax, ymin), x_range=(xmin, xmax), toolbar_location='above', toolbar_sticky=True, x_axis_location='above', ) # Mark the first and last detections again detection_dates = data[obsind]['mjd'] if len(detection_dates) > 0: first = round(detection_dates.min(), 6) last = round(detection_dates.max(), 6) midpoint = (ymax + ymin) / 2 line_top = 5 * ymax - 4 * midpoint line_bottom = 5 * ymin - 4 * midpoint y = np.linspace(line_bottom, line_top, num=5000) first_r = plot.line( x=np.full(5000, first), y=y, line_alpha=0.5, line_color=first_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("First detection", f'{first}')], renderers=[first_r], )) last_r = plot.line( x=np.full(5000, last), y=y, line_alpha=0.5, line_color=last_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("Last detection", f'{last}')], renderers=[last_r], point_policy='follow_mouse', )) # Mark when spectra were taken annotate_spec(plot, spectra, ymax, ymin) imhover = HoverTool(tooltips=tooltip_format) imhover.renderers = [] plot.add_tools(imhover) model_dict = {} for i, (label, df) in enumerate(split): key = f'obs{i}' model_dict[key] = plot.scatter( x='mjd', y='mag', color='color', marker='circle', fill_color='color', alpha='alpha', source=ColumnDataSource(df[df['obs']]), ) imhover.renderers.append(model_dict[key]) unobs_source = df[~df['obs']].copy() unobs_source.loc[:, 'alpha'] = 0.8 key = f'unobs{i}' model_dict[key] = plot.scatter( x='mjd', y='lim_mag', color='color', marker='inverted_triangle', fill_color='white', line_color='color', alpha='alpha', source=ColumnDataSource(unobs_source), ) imhover.renderers.append(model_dict[key]) key = f'bin{i}' model_dict[key] = plot.scatter( x='mjd', y='mag', color='color', marker='circle', fill_color='color', source=ColumnDataSource(data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], instrument=[], stacked=[], )), ) imhover.renderers.append(model_dict[key]) key = 'obserr' + str(i) y_err_x = [] y_err_y = [] for d, ro in df[df['obs']].iterrows(): px = ro['mjd'] py = ro['mag'] err = ro['magerr'] y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', source=ColumnDataSource(data=dict( xs=y_err_x, ys=y_err_y, color=df[df['obs']]['color'], alpha=[1.0] * len(df[df['obs']]), )), ) key = f'binerr{i}' model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])), ) key = f'unobsbin{i}' model_dict[key] = plot.scatter( x='mjd', y='lim_mag', color='color', marker='inverted_triangle', fill_color='white', line_color='color', alpha=0.8, source=ColumnDataSource(data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], instrument=[], stacked=[], )), ) imhover.renderers.append(model_dict[key]) key = f'all{i}' model_dict[key] = ColumnDataSource(df) key = f'bold{i}' model_dict[key] = ColumnDataSource(df[[ 'mjd', 'flux', 'fluxerr', 'mag', 'magerr', 'filter', 'zp', 'magsys', 'lim_mag', 'stacked', ]]) plot.xaxis.axis_label = 'MJD' plot.yaxis.axis_label = 'AB mag' plot.toolbar.logo = None obj = DBSession().query(Obj).get(obj_id) if obj.dm is not None: plot.extra_y_ranges = { "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm) } plot.add_layout( LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right') now = Time.now().mjd plot.extra_x_ranges = { "Days Ago": Range1d(start=now - xmin, end=now - xmax) } plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"), 'below') colors_labels = data[['color', 'label']].drop_duplicates() toggle = CheckboxWithLegendGroup( labels=colors_labels.label.tolist(), active=list(range(len(colors_labels))), colors=colors_labels.color.tolist(), width=width // 5, inline=True if "tablet" in device else False, ) # TODO replace `eval` with Namespaces # https://github.com/bokeh/bokeh/pull/6340 toggle.js_on_click( CustomJS( args={ 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'togglem.js')).read(), )) slider = Slider( start=0.0, end=15.0, value=0.0, step=1.0, title='Binsize (days)', max_width=350, margin=(4, 10, 0, 10), ) button = Button(label="Export Bold Light Curve to CSV") button.js_on_click( CustomJS( args={ 'slider': slider, 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', "download.js")).read().replace('objname', obj_id).replace( 'default_zp', str(PHOT_ZP)), )) # Don't need to expose CSV download on mobile top_layout = (slider if "mobile" in device or "tablet" in device else row( slider, button)) callback = CustomJS( args={ 'slider': slider, 'toggle': toggle, **model_dict }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackm.js')).read().replace( 'default_zp', str(PHOT_ZP)).replace( 'detect_thresh', str(PHOT_DETECTION_THRESHOLD)), ) slider.js_on_change('value', callback) plot_layout = (column(plot, toggle) if "mobile" in device or "tablet" in device else row(plot, toggle)) layout = column(top_layout, plot_layout, sizing_mode='scale_width', width=width) p2 = Panel(child=layout, title='Mag') # now make period plot # get periods from annotations annotation_list = obj.get_annotations_readable_by(user) period_labels = [] period_list = [] for an in annotation_list: if 'period' in an.data: period_list.append(an.data['period']) period_labels.append(an.origin + ": %.9f" % an.data['period']) if len(period_list) > 0: period = period_list[0] else: period = None # don't generate if no period annotated if period is not None: # bokeh figure for period plotting period_plot = figure( aspect_ratio=1.5, sizing_mode='scale_both', active_drag='box_zoom', tools='box_zoom,wheel_zoom,pan,reset,save', y_range=(ymax, ymin), x_range=(-0.1, 1.1), # initially one phase toolbar_location='above', toolbar_sticky=False, x_axis_location='below', ) # axis labels period_plot.xaxis.axis_label = 'phase' period_plot.yaxis.axis_label = 'mag' period_plot.toolbar.logo = None # do we have a distance modulus (dm)? obj = DBSession().query(Obj).get(obj_id) if obj.dm is not None: period_plot.extra_y_ranges = { "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm) } period_plot.add_layout( LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right') # initiate hover tool period_imhover = HoverTool(tooltips=tooltip_format) period_imhover.renderers = [] period_plot.add_tools(period_imhover) # initiate period radio buttons period_selection = RadioGroup(labels=period_labels, active=0) phase_selection = RadioGroup(labels=["One phase", "Two phases"], active=0) # store all the plot data period_model_dict = {} # iterate over each filter for i, (label, df) in enumerate(split): # fold x-axis on period in days df['mjd_folda'] = (df['mjd'] % period) / period df['mjd_foldb'] = df['mjd_folda'] + 1.0 # phase plotting for ph in ['a', 'b']: key = 'fold' + ph + f'{i}' period_model_dict[key] = period_plot.scatter( x='mjd_fold' + ph, y='mag', color='color', marker='circle', fill_color='color', alpha='alpha', visible=('a' in ph), source=ColumnDataSource( df[df['obs']]), # only visible data ) # add to hover tool period_imhover.renderers.append(period_model_dict[key]) # errorbars for phases key = 'fold' + ph + f'err{i}' y_err_x = [] y_err_y = [] # get each visible error value for d, ro in df[df['obs']].iterrows(): px = ro['mjd_fold' + ph] py = ro['mag'] err = ro['magerr'] # set up error tuples y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) # plot phase errors period_model_dict[key] = period_plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', visible=('a' in ph), source=ColumnDataSource(data=dict( xs=y_err_x, ys=y_err_y, color=df[df['obs']]['color'], alpha=[1.0] * len(df[df['obs']]), )), ) # toggle for folded photometry period_toggle = CheckboxWithLegendGroup( labels=colors_labels.label.tolist(), active=list(range(len(colors_labels))), colors=colors_labels.color.tolist(), width=width // 5, ) # use javascript to perform toggling on click # TODO replace `eval` with Namespaces # https://github.com/bokeh/bokeh/pull/6340 period_toggle.js_on_click( CustomJS( args={ 'toggle': period_toggle, 'numphases': phase_selection, 'p': period_plot, **period_model_dict, }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'togglep.js')).read(), )) # set up period adjustment text box period_title = Div(text="Period (days): ") period_textinput = TextInput( value=str(period if period is not None else 0.0)) period_textinput.js_on_change( 'value', CustomJS( args={ 'textinput': period_textinput, 'toggle': period_toggle, 'numphases': phase_selection, 'p': period_plot, **period_model_dict, }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'foldphase.js')).read(), ), ) # a way to modify the period period_double_button = Button(label="*2") period_double_button.js_on_click( CustomJS( args={'textinput': period_textinput}, code=""" const period = parseFloat(textinput.value); textinput.value = parseFloat(2.*period).toFixed(9); """, )) period_halve_button = Button(label="/2") period_halve_button.js_on_click( CustomJS( args={'textinput': period_textinput}, code=""" const period = parseFloat(textinput.value); textinput.value = parseFloat(period/2.).toFixed(9); """, )) # a way to select the period period_selection.js_on_click( CustomJS( args={ 'textinput': period_textinput, 'periods': period_list }, code=""" textinput.value = parseFloat(periods[this.active]).toFixed(9); """, )) phase_selection.js_on_click( CustomJS( args={ 'textinput': period_textinput, 'toggle': period_toggle, 'numphases': phase_selection, 'p': period_plot, **period_model_dict, }, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'foldphase.js')).read(), )) # layout period_column = column( period_toggle, period_title, period_textinput, period_selection, row(period_double_button, period_halve_button, width=180), phase_selection, width=180, ) period_layout = column( row(period_plot, period_column), sizing_mode='scale_width', width=width, ) # Period panel p3 = Panel(child=period_layout, title='Period') # tabs for mag, flux, period tabs = Tabs(tabs=[p2, p1, p3], width=width, height=height, sizing_mode='fixed') else: # tabs for mag, flux tabs = Tabs(tabs=[p2, p1], width=width, height=height, sizing_mode='fixed') return bokeh_embed.json_item(tabs)
def plot_benchmark(savefile, benchmarks, title, xaxis_type, yaxis_type, save_prefix=""): show_backends = False show_os = False # Determine the number of backends backends = list_recordTable_attribute(benchmarks, 'AF_PLATFORM') if len(backends) > 1: show_backends = True operating_sys = list_recordTable_attribute(benchmarks, 'AF_OS') if len(operating_sys) > 1: show_os = True # Sort the benchmarks by device name bmarks_sorted = sorted(benchmarks, key=lambda k: k['extra_data']['AF_DEVICE']) benchmarks = bmarks_sorted # configure the colors colors = unique_colors() # configure the hover box hover = HoverTool( tooltips = [ ("Device", "@device"), ("Backend", "@platform"), ("OS", "@os"), ("(x,y)", "(@x,@y)") ]) # configure the plot title and axis labels, use CDN for the data source #bplt.output_file(save_prefix + savefile + ".html", title=title, mode='cdn') bplt.output_file(save_prefix + savefile + ".html", title=title) plot = bplt.figure(title=title, tools=[hover,'save,box_zoom,resize,reset']) xlabel = "" ylabel = "" legend_location = "top_right" # plot images/second vs. data size scatter_renderers = list() for benchmark in benchmarks: # get the color we will use for this plot color = colors.next() # extract benchmarks x,xlabel,legend_location = format_data(benchmark, xaxis_type) y,ylabel,legend_location = format_data(benchmark, yaxis_type) platform = benchmark['extra_data']['AF_PLATFORM'] # get the device name, override if necessary device = benchmark['extra_data']['AF_DEVICE'] operating_system = benchmark['extra_data']['AF_OS'] if 'AF_LABEL' in benchmark['extra_data'].keys(): device = benchmark['extra_data']['AF_LABEL'] source = bplt.ColumnDataSource( data = dict(x=x,y=y, device=[device]*len(x), platform=[platform]*len(x), os=[operating_system]*len(x), )) # Generate the legend, automatically add the platform if needed legend = device if show_os or show_backends: legend += "( " if show_os: legend += operating_system + " " if show_backends: legend += platform + " " if show_os or show_backends: legend += ")" # generate the plot plot.line(x,y, legend=legend, color=color, line_width=2) sr = plot.scatter('x', 'y', source=source, legend=legend, color=color, fill_color="white", size=8) scatter_renderers.append(sr) hover = plot.select(HoverTool) hover.renderers = scatter_renderers plot.xaxis.axis_label = xlabel plot.yaxis.axis_label = ylabel plot.legend.location = legend_location # save the plot bplt.save(plot)
def photometry_plot(obj_id, user, width=600, device="browser"): """Create object photometry scatter plot. Parameters ---------- obj_id : str ID of Obj to be plotted. Returns ------- dict Returns Bokeh JSON embedding for the desired plot. """ data = pd.read_sql( DBSession() .query( Photometry, Telescope.nickname.label("telescope"), Instrument.name.label("instrument"), ) .join(Instrument, Instrument.id == Photometry.instrument_id) .join(Telescope, Telescope.id == Instrument.telescope_id) .filter(Photometry.obj_id == obj_id) .filter( Photometry.groups.any(Group.id.in_([g.id for g in user.accessible_groups])) ) .statement, DBSession().bind, ) if data.empty: return None, None, None # get spectra to annotate on phot plots spectra = ( Spectrum.query_records_accessible_by(user) .filter(Spectrum.obj_id == obj_id) .all() ) data['color'] = [get_color(f) for f in data['filter']] # get marker for each unique instrument instruments = list(data.instrument.unique()) markers = [] for i, inst in enumerate(instruments): markers.append(phot_markers[i % len(phot_markers)]) filters = list(set(data['filter'])) colors = [get_color(f) for f in filters] color_mapper = CategoricalColorMapper(factors=filters, palette=colors) color_dict = {'field': 'filter', 'transform': color_mapper} labels = [] for i, datarow in data.iterrows(): label = f'{datarow["instrument"]}/{datarow["filter"]}' if datarow['origin'] is not None: label += f'/{datarow["origin"]}' labels.append(label) data['label'] = labels data['zp'] = PHOT_ZP data['magsys'] = 'ab' data['alpha'] = 1.0 data['lim_mag'] = ( -2.5 * np.log10(data['fluxerr'] * PHOT_DETECTION_THRESHOLD) + data['zp'] ) # Passing a dictionary to a bokeh datasource causes the frontend to die, # deleting the dictionary column fixes that del data['original_user_data'] # keep track of things that are only upper limits data['hasflux'] = ~data['flux'].isna() # calculate the magnitudes - a photometry point is considered "significant" # or "detected" (and thus can be represented by a magnitude) if its snr # is above PHOT_DETECTION_THRESHOLD obsind = data['hasflux'] & ( data['flux'].fillna(0.0) / data['fluxerr'] >= PHOT_DETECTION_THRESHOLD ) data.loc[~obsind, 'mag'] = None data.loc[obsind, 'mag'] = -2.5 * np.log10(data[obsind]['flux']) + PHOT_ZP # calculate the magnitude errors using standard error propagation formulae # https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae data.loc[~obsind, 'magerr'] = None coeff = 2.5 / np.log(10) magerrs = np.abs(coeff * data[obsind]['fluxerr'] / data[obsind]['flux']) data.loc[obsind, 'magerr'] = magerrs data['obs'] = obsind data['stacked'] = False split = data.groupby('label', sort=False) finite = np.isfinite(data['flux']) fdata = data[finite] lower = np.min(fdata['flux']) * 0.95 upper = np.max(fdata['flux']) * 1.05 xmin = data['mjd'].min() - 2 xmax = data['mjd'].max() + 2 # Layout parameters based on device type active_drag = None if "mobile" in device or "tablet" in device else "box_zoom" tools = ( 'box_zoom,pan,reset' if "mobile" in device or "tablet" in device else "box_zoom,wheel_zoom,pan,reset,save" ) legend_loc = "below" if "mobile" in device or "tablet" in device else "right" legend_orientation = ( "vertical" if device in ["browser", "mobile_portrait"] else "horizontal" ) # Compute a plot component height based on rough number of legend rows added below the plot # Values are based on default sizing of bokeh components and an estimate of how many # legend items would fit on the average device screen. Note that the legend items per # row is computed more exactly later once labels are extracted from the data (with the # add_plot_legend() function). # # The height is manually computed like this instead of using built in aspect_ratio/sizing options # because with the new Interactive Legend approach (instead of the legacy CheckboxLegendGroup), the # Legend component is considered part of the plot and plays into the sizing computations. Since the # number of items in the legend can alter the needed heights of the plot, using built-in Bokeh options # for sizing does not allow for keeping the actual graph part of the plot at a consistent aspect ratio. # # For the frame width, by default we take the desired plot width minus 64 for the y-axis/label taking # up horizontal space frame_width = width - 64 if device == "mobile_portrait": legend_items_per_row = 1 legend_row_height = 24 aspect_ratio = 1 elif device == "mobile_landscape": legend_items_per_row = 4 legend_row_height = 50 aspect_ratio = 1.8 elif device == "tablet_portrait": legend_items_per_row = 5 legend_row_height = 50 aspect_ratio = 1.5 elif device == "tablet_landscape": legend_items_per_row = 7 legend_row_height = 50 aspect_ratio = 1.8 elif device == "browser": # Width minus some base width for the legend, which is only a column to the right # for browser mode frame_width = width - 200 height = ( 500 if device == "browser" else math.floor(width / aspect_ratio) + legend_row_height * int(len(split) / legend_items_per_row) + 30 # 30 is the height of the toolbar ) plot = figure( frame_width=frame_width, height=height, active_drag=active_drag, tools=tools, toolbar_location='above', toolbar_sticky=True, y_range=(lower, upper), min_border_right=16, x_axis_location='above', sizing_mode="stretch_width", ) plot.xaxis.axis_label = 'MJD' now = Time.now().mjd plot.extra_x_ranges = {"Days Ago": Range1d(start=now - xmin, end=now - xmax)} plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"), 'below') imhover = HoverTool(tooltips=tooltip_format) imhover.renderers = [] plot.add_tools(imhover) model_dict = {} legend_items = [] for i, (label, sdf) in enumerate(split): renderers = [] # for the flux plot, we only show things that have a flux value df = sdf[sdf['hasflux']] key = f'obs{i}' model_dict[key] = plot.scatter( x='mjd', y='flux', color='color', marker=factor_mark('instrument', markers, instruments), fill_color=color_dict, alpha='alpha', source=ColumnDataSource(df), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) key = f'bin{i}' model_dict[key] = plot.scatter( x='mjd', y='flux', color='color', marker=factor_mark('instrument', markers, instruments), fill_color=color_dict, source=ColumnDataSource( data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], stacked=[], instrument=[], ) ), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) key = 'obserr' + str(i) y_err_x = [] y_err_y = [] for d, ro in df.iterrows(): px = ro['mjd'] py = ro['flux'] err = ro['fluxerr'] y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', source=ColumnDataSource( data=dict( xs=y_err_x, ys=y_err_y, color=df['color'], alpha=[1.0] * len(df) ) ), ) renderers.append(model_dict[key]) key = f'binerr{i}' model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', # legend_label=label, source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])), ) renderers.append(model_dict[key]) legend_items.append(LegendItem(label=label, renderers=renderers)) if device == "mobile_portrait": plot.xaxis.ticker.desired_num_ticks = 5 plot.yaxis.axis_label = 'Flux (μJy)' plot.toolbar.logo = None add_plot_legend(plot, legend_items, width, legend_orientation, legend_loc) slider = Slider( start=0.0, end=15.0, value=0.0, step=1.0, title='Binsize (days)', max_width=350, margin=(4, 10, 0, 10), ) callback = CustomJS( args={'slider': slider, 'n_labels': len(split), **model_dict}, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackf.js') ) .read() .replace('default_zp', str(PHOT_ZP)) .replace('detect_thresh', str(PHOT_DETECTION_THRESHOLD)), ) slider.js_on_change('value', callback) # Mark the first and last detections detection_dates = data[data['hasflux']]['mjd'] if len(detection_dates) > 0: first = round(detection_dates.min(), 6) last = round(detection_dates.max(), 6) first_color = "#34b4eb" last_color = "#8992f5" midpoint = (upper + lower) / 2 line_top = 5 * upper - 4 * midpoint line_bottom = 5 * lower - 4 * midpoint y = np.linspace(line_bottom, line_top, num=5000) first_r = plot.line( x=np.full(5000, first), y=y, line_alpha=0.5, line_color=first_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("First detection", f'{first}')], renderers=[first_r], ) ) last_r = plot.line( x=np.full(5000, last), y=y, line_alpha=0.5, line_color=last_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("Last detection", f'{last}')], renderers=[last_r], ) ) # Mark when spectra were taken annotate_spec(plot, spectra, lower, upper) layout = column(slider, plot, width=width, height=height) p1 = Panel(child=layout, title='Flux') # now make the mag light curve ymax = ( np.nanmax( ( np.nanmax(data.loc[obsind, 'mag']) if any(obsind) else np.nan, np.nanmax(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan, ) ) + 0.1 ) ymin = ( np.nanmin( ( np.nanmin(data.loc[obsind, 'mag']) if any(obsind) else np.nan, np.nanmin(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan, ) ) - 0.1 ) plot = figure( frame_width=frame_width, height=height, active_drag=active_drag, tools=tools, y_range=(ymax, ymin), x_range=(xmin, xmax), toolbar_location='above', toolbar_sticky=True, x_axis_location='above', sizing_mode="stretch_width", ) plot.xaxis.axis_label = 'MJD' now = Time.now().mjd plot.extra_x_ranges = {"Days Ago": Range1d(start=now - xmin, end=now - xmax)} plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"), 'below') obj = DBSession().query(Obj).get(obj_id) if obj.dm is not None: plot.extra_y_ranges = { "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm) } plot.add_layout( LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right' ) # Mark the first and last detections again detection_dates = data[obsind]['mjd'] if len(detection_dates) > 0: first = round(detection_dates.min(), 6) last = round(detection_dates.max(), 6) midpoint = (ymax + ymin) / 2 line_top = 5 * ymax - 4 * midpoint line_bottom = 5 * ymin - 4 * midpoint y = np.linspace(line_bottom, line_top, num=5000) first_r = plot.line( x=np.full(5000, first), y=y, line_alpha=0.5, line_color=first_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("First detection", f'{first}')], renderers=[first_r], ) ) last_r = plot.line( x=np.full(5000, last), y=y, line_alpha=0.5, line_color=last_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("Last detection", f'{last}')], renderers=[last_r], point_policy='follow_mouse', ) ) # Mark when spectra were taken annotate_spec(plot, spectra, ymax, ymin) imhover = HoverTool(tooltips=tooltip_format) imhover.renderers = [] plot.add_tools(imhover) model_dict = {} # Legend items are individually stored instead of being applied # directly when plotting so that they can be separated into multiple # Legend() components if needed (to simulate horizontal row wrapping). # This is necessary because Bokeh does not support row wrapping with # horizontally-oriented legends out-of-the-box. legend_items = [] for i, (label, df) in enumerate(split): renderers = [] key = f'obs{i}' model_dict[key] = plot.scatter( x='mjd', y='mag', color='color', marker=factor_mark('instrument', markers, instruments), fill_color=color_dict, alpha='alpha', source=ColumnDataSource(df[df['obs']]), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) unobs_source = df[~df['obs']].copy() unobs_source.loc[:, 'alpha'] = 0.8 key = f'unobs{i}' model_dict[key] = plot.scatter( x='mjd', y='lim_mag', color=color_dict, marker='inverted_triangle', fill_color='white', line_color='color', alpha='alpha', source=ColumnDataSource(unobs_source), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) key = f'bin{i}' model_dict[key] = plot.scatter( x='mjd', y='mag', color=color_dict, marker=factor_mark('instrument', markers, instruments), fill_color='color', source=ColumnDataSource( data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], instrument=[], stacked=[], ) ), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) key = 'obserr' + str(i) y_err_x = [] y_err_y = [] for d, ro in df[df['obs']].iterrows(): px = ro['mjd'] py = ro['mag'] err = ro['magerr'] y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', source=ColumnDataSource( data=dict( xs=y_err_x, ys=y_err_y, color=df[df['obs']]['color'], alpha=[1.0] * len(df[df['obs']]), ) ), ) renderers.append(model_dict[key]) key = f'binerr{i}' model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])), ) renderers.append(model_dict[key]) key = f'unobsbin{i}' model_dict[key] = plot.scatter( x='mjd', y='lim_mag', color='color', marker='inverted_triangle', fill_color='white', line_color=color_dict, alpha=0.8, source=ColumnDataSource( data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], instrument=[], stacked=[], ) ), ) imhover.renderers.append(model_dict[key]) renderers.append(model_dict[key]) key = f'all{i}' model_dict[key] = ColumnDataSource(df) key = f'bold{i}' model_dict[key] = ColumnDataSource( df[ [ 'mjd', 'flux', 'fluxerr', 'mag', 'magerr', 'filter', 'zp', 'magsys', 'lim_mag', 'stacked', ] ] ) legend_items.append(LegendItem(label=label, renderers=renderers)) add_plot_legend(plot, legend_items, width, legend_orientation, legend_loc) plot.yaxis.axis_label = 'AB mag' plot.toolbar.logo = None slider = Slider( start=0.0, end=15.0, value=0.0, step=1.0, title='Binsize (days)', max_width=350, margin=(4, 10, 0, 10), ) button = Button(label="Export Bold Light Curve to CSV") button.js_on_click( CustomJS( args={'slider': slider, 'n_labels': len(split), **model_dict}, code=open( os.path.join( os.path.dirname(__file__), '../static/js/plotjs', "download.js" ) ) .read() .replace('objname', obj_id) .replace('default_zp', str(PHOT_ZP)), ) ) # Don't need to expose CSV download on mobile top_layout = ( slider if "mobile" in device or "tablet" in device else row(slider, button) ) callback = CustomJS( args={'slider': slider, 'n_labels': len(split), **model_dict}, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackm.js') ) .read() .replace('default_zp', str(PHOT_ZP)) .replace('detect_thresh', str(PHOT_DETECTION_THRESHOLD)), ) slider.js_on_change('value', callback) layout = column(top_layout, plot, width=width, height=height) p2 = Panel(child=layout, title='Mag') # now make period plot # get periods from annotations annotation_list = obj.get_annotations_readable_by(user) period_labels = [] period_list = [] for an in annotation_list: if 'period' in an.data: period_list.append(an.data['period']) period_labels.append(an.origin + ": %.9f" % an.data['period']) if len(period_list) > 0: period = period_list[0] else: period = None # don't generate if no period annotated if period is not None: # bokeh figure for period plotting period_plot = figure( frame_width=frame_width, height=height, active_drag=active_drag, tools=tools, y_range=(ymax, ymin), x_range=(-0.01, 2.01), # initially one phase toolbar_location='above', toolbar_sticky=False, x_axis_location='below', sizing_mode="stretch_width", ) # axis labels period_plot.xaxis.axis_label = 'phase' period_plot.yaxis.axis_label = 'mag' period_plot.toolbar.logo = None # do we have a distance modulus (dm)? obj = DBSession().query(Obj).get(obj_id) if obj.dm is not None: period_plot.extra_y_ranges = { "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm) } period_plot.add_layout( LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right' ) # initiate hover tool period_imhover = HoverTool(tooltips=tooltip_format) period_imhover.renderers = [] period_plot.add_tools(period_imhover) # initiate period radio buttons period_selection = RadioGroup(labels=period_labels, active=0) phase_selection = RadioGroup(labels=["One phase", "Two phases"], active=1) # store all the plot data period_model_dict = {} # iterate over each filter legend_items = [] for i, (label, df) in enumerate(split): renderers = [] # fold x-axis on period in days df['mjd_folda'] = (df['mjd'] % period) / period df['mjd_foldb'] = df['mjd_folda'] + 1.0 # phase plotting for ph in ['a', 'b']: key = 'fold' + ph + f'{i}' period_model_dict[key] = period_plot.scatter( x='mjd_fold' + ph, y='mag', color='color', marker=factor_mark('instrument', markers, instruments), fill_color=color_dict, alpha='alpha', # visible=('a' in ph), source=ColumnDataSource(df[df['obs']]), # only visible data ) # add to hover tool period_imhover.renderers.append(period_model_dict[key]) renderers.append(period_model_dict[key]) # errorbars for phases key = 'fold' + ph + f'err{i}' y_err_x = [] y_err_y = [] # get each visible error value for d, ro in df[df['obs']].iterrows(): px = ro['mjd_fold' + ph] py = ro['mag'] err = ro['magerr'] # set up error tuples y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) # plot phase errors period_model_dict[key] = period_plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', # visible=('a' in ph), source=ColumnDataSource( data=dict( xs=y_err_x, ys=y_err_y, color=df[df['obs']]['color'], alpha=[1.0] * len(df[df['obs']]), ) ), ) renderers.append(period_model_dict[key]) legend_items.append(LegendItem(label=label, renderers=renderers)) add_plot_legend( period_plot, legend_items, width, legend_orientation, legend_loc ) # set up period adjustment text box period_title = Div(text="Period (days): ") period_textinput = TextInput(value=str(period if period is not None else 0.0)) period_textinput.js_on_change( 'value', CustomJS( args={ 'textinput': period_textinput, 'numphases': phase_selection, 'n_labels': len(split), 'p': period_plot, **period_model_dict, }, code=open( os.path.join( os.path.dirname(__file__), '../static/js/plotjs', 'foldphase.js' ) ).read(), ), ) # a way to modify the period period_double_button = Button(label="*2", width=30) period_double_button.js_on_click( CustomJS( args={'textinput': period_textinput}, code=""" const period = parseFloat(textinput.value); textinput.value = parseFloat(2.*period).toFixed(9); """, ) ) period_halve_button = Button(label="/2", width=30) period_halve_button.js_on_click( CustomJS( args={'textinput': period_textinput}, code=""" const period = parseFloat(textinput.value); textinput.value = parseFloat(period/2.).toFixed(9); """, ) ) # a way to select the period period_selection.js_on_click( CustomJS( args={'textinput': period_textinput, 'periods': period_list}, code=""" textinput.value = parseFloat(periods[this.active]).toFixed(9); """, ) ) phase_selection.js_on_click( CustomJS( args={ 'textinput': period_textinput, 'numphases': phase_selection, 'n_labels': len(split), 'p': period_plot, **period_model_dict, }, code=open( os.path.join( os.path.dirname(__file__), '../static/js/plotjs', 'foldphase.js' ) ).read(), ) ) # layout if device == "mobile_portrait": period_controls = column( row( period_title, period_textinput, period_double_button, period_halve_button, width=width, sizing_mode="scale_width", ), phase_selection, period_selection, width=width, ) # Add extra height to plot based on period control components added # 18 is the height of each period selection radio option (per default font size) # and the 130 encompasses the other components which are consistent no matter # the data size. height += 130 + 18 * len(period_labels) else: period_controls = column( row( period_title, period_textinput, period_double_button, period_halve_button, phase_selection, width=width, sizing_mode="scale_width", ), period_selection, margin=10, ) # Add extra height to plot based on period control components added # Numbers are derived in similar manner to the "mobile_portrait" case above height += 90 + 18 * len(period_labels) period_layout = column(period_plot, period_controls, width=width, height=height) # Period panel p3 = Panel(child=period_layout, title='Period') # tabs for mag, flux, period tabs = Tabs(tabs=[p2, p1, p3], width=width, height=height, sizing_mode='fixed') else: # tabs for mag, flux tabs = Tabs(tabs=[p2, p1], width=width, height=height + 90, sizing_mode='fixed') return bokeh_embed.json_item(tabs)
def plot_stock(stock_code, value_type="Adj Close"): """plot stock values Args: value_type (String): specify which value is used to plot """ tooltips = [("Date", "@Date{%F}"), ("High", "@High"), ("Low", "@Low"), ("Upper", "@UPPER_BAND"), ("Lower", "@LOWER_BAND")] formatters = {"Date": "datetime"} hover = HoverTool(tooltips=tooltips, formatters=formatters, mode="vline") price = figure(y_axis_label="Stock value", x_axis_type="datetime", plot_height=500, plot_width=1200, title=stock_code) adj_close = price.line(x="Date", y=value_type, source=source, line_color="blue", legend="adj_close") price.line(x="Date", y="SMA50", source=source, line_color="lightsteelblue", legend="sma50") price.line(x="Date", y="SMA200", source=source, line_color="darkblue", legend="sma200") price.line(x="Date", y="MID_BAND", source=source, line_color="dimgray", legend="bb(20,2)", line_dash="dashed") price.line(x="Date", y="UPPER_BAND", source=source, line_color="dimgray") price.line(x="Date", y="LOWER_BAND", source=source, line_color="dimgray") price.grid.grid_line_alpha = 0.3 hover.renderers = [adj_close] price.add_tools(hover) price.legend.location = "top_left" price.legend.orientation = "horizontal" price.legend.background_fill_alpha = 0.0 price.legend.border_line_alpha = 0.0 price.toolbar.logo = None tooltips = [("Date", "@Date{%F}"), ("Volume", "@Volume{0.0 a}")] formatters = {"Date": "datetime"} hover = HoverTool(tooltips=tooltips, formatters=formatters) # volume plot volume = figure(x_axis_type="datetime", plot_height=200, plot_width=380, title="Volume") volume.vbar(x="Date", top="Volume", width=0.1, source=source) volume.line(x="Date", y="Volume", source=source, line_alpha=0.0) volume.yaxis[0].formatter = NumeralTickFormatter(format="0,0 a") volume.add_tools(hover) volume.grid.grid_line_alpha = 0.3 volume.toolbar.logo = None volume.toolbar_location = None # stochatic plot stochastic = figure(x_axis_type="datetime", plot_height=200, plot_width=380, title="Stochastic") stochastic.line(x="Date", y="%K", source=source, legend="%k") stochastic.line(x="Date", y="%D", source=source, line_dash="dashed", legend="%d") stochastic.line(x="Date", y=80, source=source, line_color="black") stochastic.line(x="Date", y=20, source=source, line_color="black") stochastic.xgrid.grid_line_alpha = 0.0 stochastic.ygrid.grid_line_alpha = 0.0 stochastic.legend.location = "top_left" stochastic.legend.orientation = "horizontal" stochastic.legend.background_fill_alpha = 0.0 stochastic.legend.border_line_alpha = 0.0 stochastic.toolbar.logo = None stochastic.toolbar_location = None # MACD plot macd = figure(x_axis_type="datetime", plot_height=200, plot_width=380, title="MACD") macd.line(x="Date", y="MACD", source=source, legend="macd") macd.line(x="Date", y="9EMA", source=source, legend="macd", line_color="black") macd.vbar(x="Date", top="MACD_HIST", width=0.1, source=source) macd.xgrid.grid_line_alpha = 0.0 macd.legend.location = "top_left" macd.legend.orientation = "horizontal" macd.legend.background_fill_alpha = 0.0 macd.legend.border_line_alpha = 0.0 macd.toolbar.logo = None macd.toolbar_location = None return price, column(price, row(volume, stochastic, macd))
from bokeh.io import show from bokeh.plotting import figure from bokeh.layouts import row from bokeh.models import HoverTool, ColumnDataSource, TapTool, ResetTool, BoxZoomTool from BokehStructureGraph.BokehStructureGraph import BokehStructureGraph import numpy as np x=np.linspace(-10,10,100) y=np.sin(x) signal=y+np.random.normal(0,.3,size=x.shape[0]) source=ColumnDataSource({'x':x,'y':y,'signal':signal}) # draw the structure graph of a basic figure model f=figure() f.scatter(x='x',y='signal',source=source, color='red', legend_label="measured") f.line(x='x',y='y',source=source, color='blue', legend_label="truth") f.xgrid.grid_line_color='white' f.ygrid.grid_line_color='white' f.background_fill_color="#eeeeee" f.title.text = "Figure being analyzed" f.legend.location = "top_left" f.legend.click_policy="hide" f.legend.background_fill_alpha = 0.0 T = HoverTool() T.renderers=[f.renderers[0]] S = TapTool() S.renderers=[f.renderers[0]] f.tools=[T,S, ResetTool(),BoxZoomTool()] show(row(f,BokehStructureGraph(f).model))
def create_logistic_growth_subtab(params_getter, time_series_getter, starting_regions, tab_title): # Data Sources params_df, params_CDS = params_getter() time_series_df, time_series_CDS = time_series_getter() dates = np.arange(START_DATE_STRING, '2020-07-01', dtype='datetime64[D]') bands_CDS = ColumnDataSource({'date': dates}) lines_CDS = ColumnDataSource({'date': dates}) # Set up Figure plot = figure(x_axis_type='datetime', x_axis_label='Date', y_axis_label='Number of Cases', title='Logistic Growth Modeling', active_scroll='wheel_zoom') plot.yaxis.formatter.use_scientific = False lines = {} # region, line pairs housing which have been drawn already bands = {} # region, band pairs housing which have been drawn already def logistic_function(x, L, x0, k): return L / (1 + np.exp(-k * (x - x0))) def z_star(conf_level): return st.norm.ppf(1 - (1 - conf_level) / 2) def get_offset_from_start_date(region): subdf = time_series_df[region] nonzero_subdf = subdf[subdf > 0] offset = (nonzero_subdf.index[0] - pd.to_datetime(START_DATE)).days return offset def draw_prediction_line(region, **kwargs): plot_params = { 'line_width': 4, 'line_alpha': 0.4, 'line_dash': 'dashed' } plot_params.update(kwargs) L, x0, k, L_std, x0_std, k_std = params_CDS.data[region] xs = np.arange(lines_CDS.data['date'].size) offset = get_offset_from_start_date(region) line = logistic_function(xs, L, x0 + offset, k) lines_CDS.data[region] = line lines[f'{region}_prediction'] = plot.line(x='date', y=region, source=lines_CDS, name=region, **plot_params) def draw_prediction_band(region, conf_level, **kwargs): plot_params = {'line_alpha': 0, 'fill_alpha': 0.4} plot_params.update(kwargs) L, x0, k, L_std, x0_std, k_std = params_CDS.data[region] xs = np.arange(lines_CDS.data['date'].size) offset = get_offset_from_start_date(region) bands_CDS.data[f'{region}_lower'], bands_CDS.data[ f'{region}_upper'] = (logistic_function( xs, L - L_std * z_star(conf_level), x0 + offset, k), logistic_function( xs, L + L_std * z_star(conf_level), x0 + offset, k)) bands[region] = Band(base='date', lower=f'{region}_lower', upper=f'{region}_upper', source=bands_CDS, level='underlay', **plot_params) plot.add_layout(bands[region]) def draw_data_line(region, **kwargs): plot_params = { 'line_width': 4, } plot_params.update(kwargs) lines[region] = plot.line(x='date', y=region, source=time_series_CDS, name=region, **plot_params) confidence_level = 0.95 for region, color in zip(starting_regions, viridis(len(starting_regions))): color = RGB(*hex_string_to_rgb(color)) darkened_color = color.darken(.15) lightened_color = color.lighten(.15) # draw prediction band draw_prediction_band(region, confidence_level, fill_color=color) # draw prediction line draw_prediction_line(region, line_color=darkened_color) # draw data line draw_data_line(region, line_color=color) # Hover Tool hover_tool = HoverTool(tooltips=[('Date', '@date{%F}'), ('Region', '$name'), ('Num. Cases', '@$name{0,0}')], formatters={ '@date': 'datetime', }) plot.add_tools(hover_tool) hover_tool.renderers = list(lines.values()) # Legend prediction_line_glyph = plot.line(line_color='black', line_dash='dashed', name='prediction_line_glyph', line_width=4) prediction_line_glyph.visible = False data_line_glyph = plot.line(line_color='black', name='data_line_glyph', line_width=4) data_line_glyph.visible = False confidence_interval_glyph = plot.patch( [0, 0, 1, 1], [0, 1, 1, 0], name='confidence_interval_glyph', line_color='black', line_width=1, fill_alpha=0.3, fill_color='black', ) confidence_interval_glyph.visible = False legend = Legend(items=[ LegendItem(label="Data", renderers=[plot.select_one({'name': 'data_line_glyph'})]), LegendItem( label="Prediction", renderers=[plot.select_one({'name': 'prediction_line_glyph'})]), LegendItem( label="95% Confidence Interval", renderers=[plot.select_one({'name': 'confidence_interval_glyph'})]) ], location='top_left') plot.add_layout(legend) ## Prevent legend glyphs from affecting plotting ranges def fit_to_visible_lines(): plot.x_range.renderers = list( filter(lambda line: line.visible, lines.values())) plot.y_range.renderers = list( filter(lambda line: line.visible, lines.values())) # Region Selector excluded_columns_set = {'index', 'parameters'} labels = [ key for key in params_CDS.data.keys() if key not in excluded_columns_set ] def region_select_callback(attr, old, new): new_lines = set(new) - set(old) old_lines = set(old) - set(new) for key in old_lines: lines[key].visible = False lines[f'{key}_prediction'].visible = False bands[key].visible = False for key in new_lines: if key in lines.keys(): lines[key].visible = True lines[f'{key}_prediction'].visible = True bands[key].visible = True else: color = RGB(*hex_string_to_rgb(np.random.choice(Viridis256))) darkened_color = color.darken(.15) lightened_color = color.lighten(.15) draw_prediction_line(key, line_color=darkened_color) draw_prediction_band(key, conf_level=confidence_level, fill_color=lightened_color) draw_data_line(key, line_color=color) hover_tool.renderers = [ *hover_tool.renderers, lines[key], lines[f'{key}_prediction'] ] region_select = MultiSelect(title='Select Regions to Show', value=starting_regions, options=labels, sizing_mode='stretch_height') region_select.on_change('value', region_select_callback) # Final Setup fit_to_visible_lines() child = row(column([plot]), column([region_select])) return Panel(child=child, title=tab_title)
def show_portfolio_future_plot(gbm_sim, init_cap, days_sim, hist_data): """ Returns the plot of possible future projections from gbm_sim, at the 5%, 50%, 95% confidence :param gbm_sim: pd.DataFrame - simulation results from utils.simulate_gbm() :param init_cap: np.float64 - initial capital :param days_sim: int - number of days to simulate :param hist_data: pd.DataFrame - the historical prices (to get the last day of data) :returns: Bokeh.line - 3 Lines outlining the % chance of getting above certain values """ if gbm_sim is not None: last_day = hist_data.index.max() dates = [last_day + timedelta(days=i) for i in range(days_sim)] sim_res = get_sim_results_stats(gbm_sim) bottom = sim_res.filter(['net_asset_change']).quantile(.05) / len( gbm_sim) #arithmetic average daily returns middle = sim_res.filter(['net_asset_change' ]).quantile(.5) / len(gbm_sim) top = sim_res.filter(['net_asset_change']).quantile(.95) / len(gbm_sim) # print(bottom) bottom_ind_value = init_cap * np.cumprod([1 + bottom] * days_sim) middle_ind_value = init_cap * np.cumprod([1 + middle] * days_sim) top_ind_value = init_cap * np.cumprod([1 + top] * days_sim) ind_value = pd.DataFrame( data={ "bottom_ind_value": bottom_ind_value, "middle_ind_value": middle_ind_value, "top_ind_value": top_ind_value }) ind_value['dates'] = dates source = ColumnDataSource(ind_value) plot_proj = figure(x_axis_type='datetime', height=250, tools="reset, save, wheel_zoom, pan") plot_proj.sizing_mode = "scale_width" plot_proj.grid.grid_line_alpha = 0 plot_proj.xaxis.axis_label = 'Date' plot_proj.yaxis.axis_label = 'Indicative Value' plot_proj.ygrid.band_fill_color = None plot_proj.ygrid.band_fill_alpha = 0 plot_proj.yaxis.formatter = NumeralTickFormatter(format="$0,0") plot_proj.xaxis.minor_tick_line_color = None plot_proj.line(x="dates", y="bottom_ind_value", color='#006565', source=source, legend_label='5th Percentile', line_width=1.5) r1 = plot_proj.line(x="dates", y="middle_ind_value", color='#008c8c', source=source, legend_label='50th Percentile', line_width=1.5) plot_proj.line(x="dates", y="top_ind_value", color='#00eeee', source=source, legend_label='95% Percentile', line_width=1.5) hover = HoverTool(tooltips=[ ('Date', '@dates{%F}'), ("Projected Value, 5% chance of having more than", '$@top_ind_value{0,0.00}'), ("Projected Value, 50% chance of having more than", '$@middle_ind_value{0,0.00}'), ("Projected Value, 95% chance of having more than", '$@bottom_ind_value{0,0.00}') ], formatters={"@dates": "datetime"}) hover.renderers = [r1] hover.mode = 'vline' plot_proj.add_tools(hover) plot_proj.legend.location = "top_left" return plot_proj
def tw_daily_create(daily_count_df, daily_count_avg_df): # ----- Daily count ----- # initialize figure daily_count_tp = ColumnDataSource(daily_count_df) hover_a = HoverTool(tooltips=[('Day', '@day'), ('Date', '@timePeriod{%Y-%m-%d}'), ('Tweets', '@count')], formatters={ 'timePeriod': 'datetime', 'height': 'printf' }, toggleable=False) p_a = figure(title=" ", x_axis_label='Date', x_axis_type='datetime', tools=[hover_a], y_axis_label='Tweets with #FridaysForFuture', y_axis_type="log", plot_width=750, plot_height=500) # add data lines rd = p_a.line(x='timePeriod', y='count', line_color='grey', line_alpha=0.6, source=daily_count_tp) p_a.line(x='timePeriod', y='ra_count', line_color='black', source=daily_count_tp, line_width=3, line_alpha=0.5) start = Span(location=daily_count_df.loc[14, 'timePeriod'], dimension='height', line_color='red', line_dash='dotted', line_width=3, line_alpha=0.5) p_a.add_layout(start) # prettify p_a.title.text_font = "Raleway" p_a.title.text_font_style = "normal" p_a.title.align = 'center' p_a.title.text_color = 'black' p_a.title.text_font_size = '18pt' p_a.xaxis.axis_label_text_font_style = "normal" p_a.xaxis.axis_label_text_font = "Raleway" p_a.yaxis.axis_label_text_font_style = "normal" p_a.yaxis.axis_label_text_font = "Raleway" p_a.yaxis.axis_label_text_font_size = '15pt' p_a.xaxis.axis_label_text_font_size = '15pt' p_a.xaxis.major_label_text_font = 'Raleway' p_a.xaxis.major_label_text_font_size = '12pt' p_a.yaxis.major_label_text_font = 'Raleway' p_a.yaxis.major_label_text_font_size = '12pt' p_a.ygrid.grid_line_alpha = 0.8 p_a.xgrid.grid_line_alpha = None p_a.yaxis.minor_tick_line_color = None hover_a.renderers = [rd] p_a.toolbar_location = 'left' p_a.toolbar.logo = None p_a.outline_line_color = None # ----- Daily average ----- # initialize figure p_b = figure(x_range=list(daily_count_avg_df.index), y_axis_label='Average number of tweets', plot_width=300, plot_height=500, tools='') # add data vbars p_b.vbar(x=daily_count_avg_df.index, top=daily_count_avg_df['count'], width=0.9, alpha=0.4, line_color='#1A96F8', fill_color='#1A96F8') # prettify p_b.xaxis.axis_label_text_font_style = "normal" p_b.xaxis.axis_label_text_font = "Raleway" p_b.yaxis.axis_label_text_font_style = "normal" p_b.yaxis.axis_label_text_font = "Raleway" p_b.yaxis.axis_label_text_font_size = '15pt' p_b.xaxis.axis_label_text_font_size = '15pt' p_b.xaxis.major_label_text_font = 'Raleway' p_b.xaxis.major_label_text_font_size = '12pt' p_b.yaxis.major_label_text_font = 'Raleway' p_b.yaxis.major_label_text_font_size = '12pt' p_b.ygrid.grid_line_alpha = 0.8 p_b.xgrid.grid_line_alpha = None p_b.yaxis.minor_tick_line_color = None p_b.x_range.range_padding = 0.1 p_b.xaxis.major_label_orientation = 0.9 p_b.outline_line_color = None p_b.toolbar.logo = None p_b.toolbar_location = None return row(p_a, p_b)
def plot_bokeh_frame(att_src, def_src, ball_src, plot=None, pitch_colour='green', edit=False, tracking_hover=True): from bokeh.plotting import figure from bokeh.models import ColumnDataSource, HoverTool, BooleanFilter, CDSView, LabelSet, PointDrawTool from bokeh.io import output_notebook, show import Football_Pitch_Bokeh as fpbokeh line_colour = 'white' if pitch_colour == 'white': line_colour = 'black' if plot is None: p = fpbokeh.draw_pitch(hspan=[-53, 53], vspan=[-34, 34], fill_color=pitch_colour, line_color=line_colour) else: p = plot #attack patt = p.circle(x='x', y='y', source=att_src, color='red', alpha=0.7, size=10) #defence pdef = p.circle(x='x', y='y', source=def_src, color='blue', alpha=0.7, size=10) # ball pball = p.circle(x='x', y='y', source=ball_src, color='black', alpha=0.7, size=5) att_labels = LabelSet(x='x', y='y', x_offset=5, y_offset=5, text='Shirt Number', text_font_size="10pt", text_color='red', source=att_src) p.add_layout(att_labels) def_labels = LabelSet(x='x', y='y', x_offset=5, y_offset=5, text='Shirt Number', text_font_size="10pt", text_color='blue', source=def_src) p.add_layout(def_labels) if tracking_hover == True: hover = HoverTool() hover.mode = 'mouse' hover.tooltips = [ #("Player", "@{Shirt Number}"), ("(x,y)", "($x, $y)") ] hover.renderers = [patt, pdef, pball] p.add_tools(hover) if edit == True: point_draw_att = PointDrawTool() point_draw_att.renderers = [patt] point_draw_att.num_objects = 12 p.add_tools(point_draw_att) point_draw_def = PointDrawTool() point_draw_def.renderers = [pdef] point_draw_def.num_objects = 12 p.add_tools(point_draw_def) return p
def assembly_chart(df, complements): """function to assembly the chart""" print('starting the plot...') # specify the output file name output_file("movigrama_chart.html") # force to show only one plot when multiples executions of the code occur # otherwise the plots will append each time one new calling is done reset_output() # create ColumnDataSource objects directly from Pandas data frames source = ColumnDataSource(df) # use the column DT as index df.set_index('DT', inplace=True) ########################################################################### # # Movigrama Plot # ########################################################################### # build figure of the plot p = figure(x_axis_type='datetime', x_axis_label='days of moviment', y_axis_label='unities movimented', plot_width=1230, plot_height=500, active_scroll='wheel_zoom') # TODO Specify X range (not all plots have 365 days of moviment) # build the Stock Level bar r1 = p.vbar(x='DT', bottom=0, top='STOCK', width=pd.Timedelta(days=1), fill_alpha=0.4, color='paleturquoise', source=source) # build the OUT bar p.vbar(x='DT', bottom=0, top='SOMA_SAI', width=pd.Timedelta(days=1), fill_alpha=0.8, color='crimson', source=source) # build the IN bar p.vbar(x='DT', bottom=0, top='SOMA_ENTRA', width=pd.Timedelta(days=1), fill_alpha=0.8, color='seagreen', source=source) # edit title # adds warehouse title p.add_layout( Title(text=complements['warehouse'], text_font='helvetica', text_font_size='10pt', text_color='orangered', text_alpha=0.5, align='center', text_font_style="italic"), 'above') # adds product title p.add_layout( Title(text=complements['product'], text_font='helvetica', text_font_size='10pt', text_color='orangered', text_alpha=0.5, align='center', text_font_style="italic"), 'above') # adds main title p.add_layout( Title(text='Movigrama Endicon', text_font='helvetica', text_font_size='16pt', text_color='orangered', text_alpha=0.9, align='center', text_font_style="bold"), 'above') # adds horizontal line hline = Span(location=0, line_alpha=0.4, dimension='width', line_color='gray', line_width=3) p.renderers.extend([hline]) # adapt the range to the plot p.x_range.range_padding = 0.1 p.y_range.range_padding = 0.1 # format the plot's outline p.outline_line_width = 4 p.outline_line_alpha = 0.1 p.outline_line_color = 'orangered' # format major labels p.axis.major_label_text_color = 'gray' p.axis.major_label_text_font_style = 'bold' # format labels p.axis.axis_label_text_color = 'gray' p.axis.axis_label_text_font_style = 'bold' # p.xgrid.grid_line_color = None # disable vertical bars # p.ygrid.grid_line_color = None # disable horizontal bars # change placement of minor and major ticks in the plot p.axis.major_tick_out = 10 p.axis.minor_tick_in = -3 p.axis.minor_tick_out = 6 p.axis.minor_tick_line_color = 'gray' # format properly the X datetime axis p.xaxis.formatter = DatetimeTickFormatter(days=['%d/%m'], months=['%m/%Y'], years=['%Y']) # iniciate hover object hover = HoverTool() hover.mode = "vline" # activate hover by vertical line hover.tooltips = [("SUM-IN", "@SOMA_ENTRA"), ("SUM-OUT", "@SOMA_SAI"), ("COUNT-IN", "@TRANSACT_ENTRA"), ("COUNT-OUT", "@TRANSACT_SAI"), ("STOCK", "@STOCK"), ("DT", "@DT{%d/%m/%Y}")] # use 'datetime' formatter for 'DT' field hover.formatters = {"DT": 'datetime'} hover.renderers = [r1] # display tolltip only to one render p.add_tools(hover) ########################################################################### # # Demand analysis # ########################################################################### # change to positive values df['out_invert'] = df['SOMA_SAI'] * -1 # moving average with n=30 days df['MA30'] = df['out_invert'].rolling(30).mean().round(0) # moving standard deviation with n=30 days df['MA30_std'] = df['out_invert'].rolling(30).std().round(0) # lower control limit for 1 sigma deviation df['lcl_1sigma'] = (df['MA30'] - df['MA30_std']) # upper control limit for 1 sigma deviation df['ucl_1sigma'] = (df['MA30'] + df['MA30_std']) source = ColumnDataSource(df) p1 = figure(plot_width=1230, plot_height=500, x_range=p.x_range, x_axis_type="datetime", active_scroll='wheel_zoom') # build the Sum_out bar r1 = p1.vbar(x='DT', top='out_invert', width=pd.Timedelta(days=1), color='darkred', line_color='salmon', fill_alpha=0.4, source=source) # build the moving average line p1.line(x='DT', y='MA30', source=source) # build the confidence interval band = Band(base='DT', lower='lcl_1sigma', upper='ucl_1sigma', source=source, level='underlay', fill_alpha=1.0, line_width=1, line_color='black') p1.renderers.extend([band]) # adds title p1.add_layout( Title(text='Demand Variability', text_font='helvetica', text_font_size='16pt', text_color='orangered', text_alpha=0.5, align='center', text_font_style="bold"), 'above') # adds horizontal line hline = Span(location=0, line_alpha=0.4, dimension='width', line_color='gray', line_width=3) p1.renderers.extend([hline]) # format the plot's outline p1.outline_line_width = 4 p1.outline_line_alpha = 0.1 p1.outline_line_color = 'orangered' # format major labels p1.axis.major_label_text_color = 'gray' p1.axis.major_label_text_font_style = 'bold' # format labels p1.axis.axis_label_text_color = 'gray' p1.axis.axis_label_text_font_style = 'bold' # change placement of minor and major ticks in the plot p1.axis.major_tick_out = 10 p1.axis.minor_tick_in = -3 p1.axis.minor_tick_out = 6 p1.axis.minor_tick_line_color = 'gray' # format properly the X datetime axis p1.xaxis.formatter = DatetimeTickFormatter(days=['%d/%m'], months=['%m/%Y'], years=['%Y']) # iniciate hover object hover = HoverTool() hover.mode = "vline" # activate hover by vertical line hover.tooltips = [("DEMAND", '@out_invert'), ("UCL 1σ", "@ucl_1sigma"), ("LCL 1σ", "@lcl_1sigma"), ("M AVG 30d", "@MA30"), ("DT", "@DT{%d/%m/%Y}")] # use 'datetime' formatter for 'DT' field hover.formatters = {"DT": 'datetime'} hover.renderers = [r1] # display tolltip only to one render p1.add_tools(hover) ########################################################################### # # Demand groupped by month # ########################################################################### resample_M = df.iloc[:, 0:6].resample('M').sum() # resample to month # create column date as string resample_M['date'] = resample_M.index.strftime('%b/%y').values # moving average with n=3 months resample_M['MA3'] = resample_M['out_invert'].rolling(3).mean() resample_M['MA3'] = np.ceil(resample_M.MA3) # round up the column MA3 # resample to month with mean resample_M['mean'] = np.ceil(resample_M['out_invert'].mean()) # resample to month with standard deviation resample_M['std'] = np.ceil(resample_M['out_invert'].std()) # moving standard deviation with n=30 days resample_M['MA3_std'] = np.ceil(resample_M['out_invert'].rolling(3).std()) # lower control limit for 1 sigma deviation resample_M['lcl_1sigma'] = resample_M['MA3'] - resample_M['MA3_std'] # upper control limit for 1 sigma deviation resample_M['ucl_1sigma'] = resample_M['MA3'] + resample_M['MA3_std'] source = ColumnDataSource(resample_M) p2 = figure(plot_width=1230, plot_height=500, x_range=FactorRange(factors=list(resample_M.date)), title='demand groupped by month') colors = factor_cmap('date', palette=Category20_20, factors=list(resample_M.date)) p2.vbar(x='date', top='out_invert', width=0.8, fill_color=colors, fill_alpha=0.8, source=source, legend=value('OUT')) p2.line(x='date', y='MA3', color='red', line_width=3, line_dash='dotted', source=source, legend=value('MA3')) p2.line(x='date', y='mean', color='blue', line_width=3, line_dash='dotted', source=source, legend=value('mean')) band = Band(base='date', lower='lcl_1sigma', upper='ucl_1sigma', source=source, level='underlay', fill_alpha=1.0, line_width=1, line_color='black') labels1 = LabelSet(x='date', y='MA3', text='MA3', level='glyph', y_offset=5, source=source, render_mode='canvas', text_font_size="8pt", text_color='darkred') labels2 = LabelSet(x='date', y='out_invert', text='out_invert', level='glyph', y_offset=5, source=source, render_mode='canvas', text_font_size="8pt", text_color='gray') low_box = BoxAnnotation( top=resample_M['mean'].iloc[0] - resample_M['std'].iloc[0], # analysis:ignore fill_alpha=0.1, fill_color='red') mid_box = BoxAnnotation( bottom=resample_M['mean'].iloc[0] - resample_M['std'].iloc[0], # analysis:ignore top=resample_M['mean'].iloc[0] + resample_M['std'].iloc[0], # analysis:ignore fill_alpha=0.1, fill_color='green') high_box = BoxAnnotation( bottom=resample_M['mean'].iloc[0] + resample_M['std'].iloc[0], # analysis:ignore fill_alpha=0.1, fill_color='red') p2.renderers.extend([band, labels1, labels2, low_box, mid_box, high_box]) p2.legend.click_policy = "hide" p2.legend.background_fill_alpha = 0.4 p2.add_layout( Title(text='Demand Grouped by Month', text_font='helvetica', text_font_size='16pt', text_color='orangered', text_alpha=0.5, align='center', text_font_style="bold"), 'above') # adds horizontal line hline = Span(location=0, line_alpha=0.4, dimension='width', line_color='gray', line_width=3) p2.renderers.extend([hline]) # format the plot's outline p2.outline_line_width = 4 p2.outline_line_alpha = 0.1 p2.outline_line_color = 'orangered' # format major labels p2.axis.major_label_text_color = 'gray' p2.axis.major_label_text_font_style = 'bold' # format labels p2.axis.axis_label_text_color = 'gray' p2.axis.axis_label_text_font_style = 'bold' # change placement of minor and major ticks in the plot p2.axis.major_tick_out = 10 p2.axis.minor_tick_in = -3 p2.axis.minor_tick_out = 6 p2.axis.minor_tick_line_color = 'gray' # iniciate hover object # TODO develop hoverTool # hover = HoverTool() # hover.mode = "vline" # activate hover by vertical line # hover.tooltips = [("SUM-IN", "@SOMA_ENTRA"), # ("SUM-OUT", "@SOMA_SAI"), # ("COUNT-IN", "@TRANSACT_ENTRA"), # ("COUNT-OUT", "@TRANSACT_SAI"), # ("STOCK", "@STOCK")] # hover.renderers = [r1] # display tolltip only to one render # p2.add_tools(hover) ########################################################################### # # Plot figures # ########################################################################### # put the results in a column and show show(column(p, p1, p2)) # show(p) # plot action print('plot finished')
p.add_glyph(source_boarder, patch) circ = Circle(x='xw', y='yw', size=10, line_alpha=0.0, fill_color='color', fill_alpha='rate', name='wells') #Draws the wells circ_r = p.add_glyph(source, circ) #p.line(xc, yc, line_color='black', line_width=1.0) #p.patches('x', 'y', source=source, # fill_color='color', fill_alpha=0.0, # line_color="black", line_width=0.5) hover = HoverTool() hover.renderers = [circ_r] p.add_tools(PanTool(), WheelZoomTool(), BoxSelectTool(), hover) #hover.point_policy = "follow_mouse" hover.tooltips = [ # ("Name", "@name"), ("(Well Level)", "@depth ft"), ("(Water Type)", "@color"), ("(Long, Lat)", "(@xw, @yw)"), ] output_file("california.html", title="california.py example") checkbox_button_group = CheckboxButtonGroup( #Checkbox group, currently doesn't work. Still figuring out how callbacks work on Bokeh. labels=["Groundwater", "Recycled"], active=[0, 1])
def plot_merged_benchmark(savefile, benchmarks, title, xaxis_type, yaxis_type, save_prefix=""): # Sort the benchmarks by device name bmarks_sorted = sorted(benchmarks, key=lambda k: k['extra_data']['AF_DEVICE']) benchmarks = bmarks_sorted # configure the colors colors = unique_colors() assigned_colors = dict() # configure the hover box hover = HoverTool( tooltips = [ ("Device", "@device"), ("Benchmark", "@benchmark"), ("Backend", "@platform"), ("OS", "@os"), ("(x,y)", "(@x,@y)") ]) # configure the plot title and axis labels, use CDN for the data source #bplt.output_file(save_prefix + savefile + ".html", title=title, mode='cdn') bplt.output_file(save_prefix + savefile + ".html", title=title) plot = bplt.figure(title=title, tools=[hover,'save,box_zoom,resize,reset']) xlabel = "" ylabel = "" legend_location = "top_right" # plot images/second vs. data size scatter_renderers = list() for benchmark in benchmarks: bmark_name = benchmark['benchmark_name'] # Look up the color device = benchmark['extra_data']['AF_DEVICE'] platform = benchmark['extra_data']['AF_PLATFORM'] operating_system = benchmark['extra_data']['AF_OS'] # key = device key = bmark_name + device + platform if key in assigned_colors: color = assigned_colors[key] else: color = colors.next() assigned_colors[key] = color # extract benchmarks x,xlabel,legend_location = format_data(benchmark, xaxis_type) y,ylabel,legend_location = format_data(benchmark, yaxis_type) # get the device name, override if necessary if 'AF_LABEL' in benchmark['extra_data'].keys(): device = benchmark['extra_data']['AF_LABEL'] source = bplt.ColumnDataSource( data = dict(x=x,y=y, device=[device]*len(x), benchmark=[bmark_name]*len(x), platform=[platform]*len(x), os=[operating_system]*len(x), )) # Generate the legend, automatically add the platform if needed # legend = device legend = device + " (" + platform + ") " + bmark_name # generate the plot plot.line(x,y, legend=legend, color=color, line_width=2) sr = plot.scatter('x', 'y', source=source, legend=legend, color=color, fill_color="white", size=8) scatter_renderers.append(sr) hover = plot.select(HoverTool) hover.renderers = scatter_renderers plot.xaxis.axis_label = xlabel plot.yaxis.axis_label = ylabel plot.legend.location = legend_location # save the plot bplt.save(plot)
def construct(): import pandas as pd import os from datetime import datetime os.system("tzutil /s \"Eastern Standard Time\"") # windows alert! import matplotlib.pyplot as plt # -------------initialize data ---------------------------- infections = pd.concat([pd.read_csv('../data/{}'.format(file), sep='\t', names=['datetime', 'infected_user', 'infected_comment', 'cause_user', 'cause', 'cause_type'], parse_dates=['datetime']) for file in os.listdir('../data') if 'infections' in file], axis=0, ignore_index=True).set_index('infected_user').sort_values('datetime', ascending=True) # drop weird duplicates infections = infections.loc[~infections.index.duplicated(keep='first')] comments = pd.concat([pd.read_csv('../data/{}'.format(file), sep='\t', names=['datetime', 'comment_user', 'comment', 'parent_user', 'parent', 'parent_type', 'parent_inf'], parse_dates=['datetime']) for file in os.listdir('../data') if 'comments' in file], axis=0, ignore_index=True).set_index('comment') # clean up the microseconds where one file overlaps the hour, if applicable if infections['datetime'].max().hour!=comments['datetime'].max().hour: cutoff = min(infections['datetime'].max(), comments['datetime'].max()) comments = comments[comments['datetime']<=cutoff] infections = infections[infections['datetime']<=cutoff] # indicate whether user was infected when they made the comment comments['user_status'] = 0 f = comments.merge(infections, left_on='comment_user', right_index=True, suffixes=('_comment', '_infected'), how='inner') comments.loc[f[f['datetime_comment']>f['datetime_infected']].index, 'user_status'] = 1 del f # ---------------- create visualizations---------------------- from bokeh.io import show from bokeh.themes import built_in_themes from bokeh.io import curdoc curdoc().theme = 'dark_minimal' from bokeh.plotting import figure from bokeh.layouts import widgetbox from bokeh.models import ColumnDataSource, HoverTool, DatetimeTickFormatter, NumeralTickFormatter, Band, Label, DataTable, TableColumn # vis 1 hover = HoverTool(tooltips=[ ("% of comments containing virus", "@y{%0%}"), ("# of comments", "@scale{0,0}"), ("Hour", '@x{%I:%M %p EST, %b %d}')], formatters={'x': 'datetime'} ) fig1 = figure(title='Pandemic spread (comments made during event)', plot_width=500, plot_height=300, tools=[hover,"ywheel_zoom,pan,reset"], y_range=(0,1), x_axis_type='datetime') x = comments.groupby(pd.Grouper(key='datetime', freq='H'))['user_status'].mean() data = ColumnDataSource(data=dict(y=x.values, x=x.index.values, scale=comments.groupby(pd.Grouper(key='datetime', freq='H')).size().values)) fig1.line(y='y',x='x', line_width=2, source=data, color='red') fig1.xaxis.formatter=DatetimeTickFormatter( hours=["%I:%M %p EST, %b %d"], days=["%I:%M %p EST, %b %d"]) fig1.xaxis.major_label_orientation = 3.14/4 band = Band(base='x', upper='y', source=data, level='underlay', fill_alpha=0.35, fill_color='red') fig1.add_layout(band) fig1.yaxis.formatter = NumeralTickFormatter(format="%0%") fig1.yaxis.axis_label = '% of comments' # vis 2 hover = HoverTool(tooltips=[ ("% of users infected", "@ratio{%0%}"), ("# of infected users", "@y{0,0}"), ("# of active users", "@active{0,0}"), ("Hour", '@x{%I:%M %p EST, %b %d}')], formatters={'x': 'datetime'} ) fig2 = figure(title='Pandemic spread (users active during event)', plot_width=500, plot_height=300, tools=[hover,"ywheel_zoom,pan,reset"], y_range=(0,1), x_axis_type='datetime') x = infections.groupby(pd.Grouper(key='datetime', freq='H')).size().cumsum() active_users = pd.concat([comments.set_index('comment_user')['datetime'], infections['datetime']]).sort_values(ascending=False).groupby(level=0).first().reset_index().groupby(pd.Grouper(key='datetime', freq='H')).size()[1:].cumsum() active_users = active_users.reindex(x.index, fill_value=active_users.min()) data = ColumnDataSource(data=dict(y=x.values, x=x.index.values, active=active_users.values, ratio=x.values/active_users.values)) main_line = fig2.line(y='ratio',x='x', line_width=2, source=data, color='red') hover.renderers =[main_line] fig2.xaxis.formatter=DatetimeTickFormatter( hours=["%I:%M %p EST, %b %d"], days=["%I:%M %p EST, %b %d"]) fig2.xaxis.major_label_orientation = 3.14/4 band = Band(base='x', upper='ratio', source=data, level='underlay', fill_alpha=0.35, fill_color='red') fig2.add_layout(band) fig2.yaxis.formatter = NumeralTickFormatter(format="%0%") fig2.yaxis.axis_label = '% of active users' # ---------------- create tables---------------------- from bokeh.models.widgets import Tabs, Panel #table 1 data = infections.groupby('cause_user')['cause_type'].value_counts().unstack(fill_value=0).loc[infections['cause_user'].value_counts().nlargest(20).index] data = ColumnDataSource(data=dict(C=data['C'], S=data['S'], names=data.index, rank=range(1, len(data)+1) )) columns = [ TableColumn(field="names", title="User"), TableColumn(field="rank", title="Ranking"), TableColumn(field="C", title="Infections via comment"), TableColumn(field="S", title="Infections via submission"), ] tab1 = DataTable(source=data, columns=columns, width=1000, index_position=None) pan1 = Panel(child=tab1,title="Best infectors") # table 2 a = comments[~comments['comment_user'].isin(infections.index)].set_index('comment_user') a = a[a['parent_inf']!='N'].groupby(level=0)['parent_type'].value_counts().unstack(fill_value=0) data = a.loc[a.sum(axis=1).nlargest(20).index] data = ColumnDataSource(data=dict(C=data['C'], S=data['S'], names=data.index, rank=range(1, len(data)+1) )) columns = [ TableColumn(field="names", title="User"), TableColumn(field="rank", title="Ranking"), TableColumn(field="C", title="# of comments under infected user"), TableColumn(field="S", title="# of comments on infected thread"), ] tab2 = DataTable(source=data, columns=columns, width=1000, index_position=None) pan2 = Panel(child=tab2,title="Most resilient") tabs = Tabs(tabs=[pan1, pan2]) # put tables in same object # ---------------------------------- make webpage itself -------------------- # script for countdown shamelessly copied from https://www.w3schools.com/howto/howto_js_countdown.asp countdown_script = ''' <script> // Set the date we're counting down to var countDownDate = new Date("Apr 1, 2020 00:00:00 EDT").getTime(); // Update the count down every 1 second var x = setInterval(function() { // Get today's date and time var now = new Date().getTime(); // Find the distance between now and the count down date var distance = countDownDate - now; // Time calculations for days, hours, minutes and seconds var days = Math.floor(distance / (1000 * 60 * 60 * 24)); var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); var seconds = Math.floor((distance % (1000 * 60)) / 1000); // Output the result in an element with id="demo" document.getElementById("demo").innerHTML = days + "d " + hours + "h " + minutes + "m " + seconds + "s remaining"; // If the count down is over, write some text if (distance < 0) { clearInterval(x); document.getElementById("demo").innerHTML = "INFECTION COMPLETE"; } }, 1000); </script>''' import io from bokeh.embed import components from bokeh.resources import CDN from jinja2 import Template # template for entire document template = Template( '''<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Memonavirus Stats</title> {{ resources }} {{ script }} ---replace here--- <style> p { text-align: center; font-size: 20px; margin-top: 10px; color: white; width: 100%; } .embed-wrapper { display: flex; justify-content: space-evenly; } .warning { text-align: center; font-size: 60px; margin-top: 0px; color: red; } .demo { text-align: center; font-size: 40px; margin-top: 0px; color: white; width: 100%; } </style> </head> <body style="background-color:black"> <div class="embed-wrapper"> {{ div }} </div> </body> </html> '''.replace('---replace here---', countdown_script)) # make the other HTML elements from bokeh.layouts import column, row from bokeh.models import Div header = Div(text='''<div class="warning">{:,} users infected!</div>'''.format(infections.index.nunique())) countdown = Div(text='''<div width="100%"><p id="demo">Loading...</p><div>''', width=800) blank_space = Div(text="""""", height=30) disclaimer = Div(text='''<p>Last updated at: {}<p>'''.format(datetime.now().strftime('%I:%M %p EST, %b %d'))) # align everything into a single layout composite_layout = column(header, countdown, row(fig1, fig2), blank_space, tabs, disclaimer) # render everything together script_bokeh, div_bokeh = components(composite_layout) resources_bokeh = CDN.render() html = template.render(resources=resources_bokeh, script=script_bokeh, div=div_bokeh) # save to file out_file_path = "../html/index.html" with io.open(out_file_path, mode='w') as f: f.write(html)