Боке: Hovertool складывается на старых экземплярах после срабатывания обратного вызова - PullRequest
0 голосов
/ 29 мая 2020

У меня есть программа, которая отображает информацию о фондовом рынке для Google. Я могу настроить его с помощью DateRangeSlider, чтобы увеличить даты, на которые мы смотрим. У меня также есть набор инструментов наведения на диаграмме для отображения различных аспектов данных за каждый соответствующий день.

Проблема, с которой я столкнулся, связана с обратным вызовом. Вместо того, чтобы обновлять данные при наведении при каждом вызове, он сохраняет память о предыдущих вызовах и просто складывает предыдущую информацию о наведении поверх новой.

Вот мой код для работы в Jupyter:

from pandas_datareader import data
from pandas import Timedelta
from datetime import datetime, date
from bokeh.plotting import figure, show
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, HoverTool, NumeralTickFormatter, DatetimeTickFormatter, TextInput, DateRangeSlider
from bokeh.io import output_notebook, curdoc

output_notebook()

def inc_dec(c, o):
"""Determines if the financial day with and Increase or Decrease"""
    if c > o:
        return "Increase"
    elif c == 0:
        return "Equal"
    else:
        return "Decrease"

def get_sources(start = date(2016, 3, 1), end = date(2016, 3, 10), company = "GOOG"):
"""Gets all my dataframes for each glyph below"""

    #Gather data for df's
    df = data.DataReader(company, data_source="yahoo", start=start, end=end)
    df["Status"] = [inc_dec(c, o) for c, o in zip(df.Close, df.Open)]
    df["Mid"] = (df.Open + df.Close) / 2
    df["Height"] = abs(df.Open - df.Close)
    df["Width"] = 12*60*60*1000
    df["Segment, Left"] = df.index - Timedelta(hours=2)
    df["Segment, Right"] = df.index + Timedelta(hours=2)

    #my df is complete. Split them
    inc_df = df[df.Status == "Increase"]
    dec_df = df[df.Status == "Decrease"]

    #inc_s = ColumnDataSource(inc_df)
    #dec_s = ColumnDataSource(dec_df)
    #tot_s = ColumnDataSource(df)
    #Dictionary of dataframes, Increasing, Decreasing, and Total
    dfs = {"I": inc_df, "D": dec_df, "T": df}

    return dfs

def fin(doc):
"""Initialize the graphs and make an application"""

    #Get df's and convert to CDS
    dfs = get_sources()
    inc_s = ColumnDataSource(dfs["I"])
    dec_s = ColumnDataSource(dfs["D"])
    tot_s = ColumnDataSource(dfs["T"])

    hover = HoverTool(names=["inc","dec"],
                    tooltips = [("Date", "@Date{%m/%d/%Y}"),
                        ("High", "@High{$0.00}"),
                        ("Low","@Low{$0.00}"),
                        ("Open","@Open{$0.00}"),
                        ("Close","@Close{$0.00}"),
                        ("Volume", "@Volume{0,0}")],
                    formatters = {"@Date" : "datetime"})

    #Formatting the plot, p
    p = figure(x_axis_type="datetime", width=1000, height=300, sizing_mode="scale_width", tools="crosshair,pan,wheel_zoom,box_zoom,reset")
    #p.title.text = "{}: Finacial Data".format(company)       #Change this with company later
    p.title.text = "GOOG: Financial Data"
    p.title.align = "center"
    p.xaxis.axis_label="Date"
    p.yaxis.axis_label="Price"   
    p.yaxis.formatter = NumeralTickFormatter(format="$0,0.00")
    p.grid.grid_line_alpha = 0.3

    #Add glyphs
    p.segment(x0='Date', y0='High', x1='Date', y1='Low', source=tot_s) #vertical
    p.segment(x0='Segment, Left', y0='High', x1='Segment, Right', y1='High', source=tot_s) #high
    p.segment(x0='Segment, Left', y0='Low', x1='Segment, Right', y1='Low', source=tot_s) #low
    p.rect(x="Date", y="Mid", width="Width", height="Height", name="inc",
        fill_color="#00FFFF", line_color="black", source=inc_s)
    p.rect(x="Date", y="Mid", width="Width", height="Height", name="dec",
        fill_color="#B22222", line_color="black", source=dec_s)

    p.add_tools(hover)

    #the callbacks
    def cb_date(attr, new, old):
    """Callback for the DateRangeSlider"""

        if not isinstance(new[0], int):
            return None
        else:
            #Get new dates, pass them to get_sources to get df's, add new CDS's
            start = date.fromtimestamp(new[0]/1000)
            end = date.fromtimestamp(new[1]/1000)
            dfs = get_sources(start=start, end=end)
            inc_s.stream(dfs["I"])
            dec_s.stream(dfs["D"])
            tot_s.stream(dfs["T"])

    #def cb_com(attr, new, old):
        #Changes the company name, do later

    #add widgets
    slider = DateRangeSlider(start=date(2016, 1, 1), 
                                     end=date(2016, 5, 1), 
                                     value=(date(2016, 3, 1), date(2016, 3, 10)), 
                                     step=1, 
                                     title="Change Dates:")

    slider.on_change('value', cb_date)

    doc.add_root(column(slider,p))

show(fin)

Дайте мне знать, как я могу go передать это. Я очень потерян.

1 Ответ

1 голос
/ 29 мая 2020

Я не могу запустить ваш код без некоторых тестовых данных, но мне кажется, что проблема связана с вызовами функции stream. Потоковая передача данных не удаляет старые данные, даже если координата X такая же (источники данных не знают и не заботятся о координатах). Если вы хотите заменить данные для какой-то конкретной даты, вы должны использовать patch. Если вы хотите заменить все данные, вы должны просто присвоить новое значение атрибуту data источника данных.

...