Пример #1
0
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
Пример #3
0
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)])
Пример #4
0
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
Пример #5
0
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
Пример #6
0
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
Пример #7
0
    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
Пример #8
0

# 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):
Пример #9
0
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,
Пример #10
0
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)
Пример #11
0
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)
Пример #12
0
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)
Пример #13
0
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)
Пример #14
0
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)
Пример #15
0
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)
Пример #18
0
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
Пример #19
0
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)
Пример #20
0
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
Пример #21
0
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])
Пример #23
0
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)
Пример #24
0
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)