散景:触发回调后,Hovertool 在旧实例上堆叠
Bokeh: Hovertool stacks on old instances after callback triggered
我有一个程序可以显示 google 的股票市场信息。我可以使用 DateRangeSlider 对其进行调整以增加我们正在查看的日期。我还在图表上设置了一个 hovertool 来显示每一天数据的各个方面。
我遇到的问题是回调。它不会在每次调用时刷新悬停数据,而是保留之前调用的内存,并将之前的悬停信息堆叠在新的之上。
这是我在 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{[=10=].00}"),
("Low","@Low{[=10=].00}"),
("Open","@Open{[=10=].00}"),
("Close","@Close{[=10=].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="[=10=],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)
让我知道我该如何推进这件事。我迷路了。
如果没有一些测试数据,我无法 运行 你的代码,但在我看来,问题出在对 stream
函数的调用上。流式数据不会删除旧数据,即使 X 坐标相同(数据源不知道也不关心坐标)。如果你想替换某个特定日期的数据,你应该使用 patch
。如果要替换整个数据,只需将新值分配给数据源的 data
属性即可。
我有一个程序可以显示 google 的股票市场信息。我可以使用 DateRangeSlider 对其进行调整以增加我们正在查看的日期。我还在图表上设置了一个 hovertool 来显示每一天数据的各个方面。
我遇到的问题是回调。它不会在每次调用时刷新悬停数据,而是保留之前调用的内存,并将之前的悬停信息堆叠在新的之上。
这是我在 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{[=10=].00}"),
("Low","@Low{[=10=].00}"),
("Open","@Open{[=10=].00}"),
("Close","@Close{[=10=].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="[=10=],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)
让我知道我该如何推进这件事。我迷路了。
如果没有一些测试数据,我无法 运行 你的代码,但在我看来,问题出在对 stream
函数的调用上。流式数据不会删除旧数据,即使 X 坐标相同(数据源不知道也不关心坐标)。如果你想替换某个特定日期的数据,你应该使用 patch
。如果要替换整个数据,只需将新值分配给数据源的 data
属性即可。