用于堆叠 vbar 的 Bokeh 滑块以增加段大小和 HoverTool

Bokeh Sliders for stacked vbar to increase segment size & HoverTool

我的目标是在下面的代码中制作一个带有散景的堆叠条形图,附加上滑块,这样我就可以增加或减少每个条形段的大小并依次移动其他条形图。

我现在的问题是 运行 从散景服务器中更新时它不会更新。我的猜测是在更新源后散景可能不会 运行 再次计算......或者我遇到了源冲突。 (到目前为止,我只为 "Engineering" 实施了它。希望在我解决其余问题之前让它发挥作用。

其他注意事项。我使用的是为每个字形提供底部/顶部数据以及源的折旧技术。这样做是因为这是我可以显示 hovertool 的唯一方法。

我让这个工作的唯一方法是完全重绘图表,我会接受这个选项,但它会将图表堆叠在一起。有没有办法清除 Bokeh 中所有以前的图表?显然,我更喜欢一种只改变数据而不完全重绘图形的解决方案。

from bokeh.plotting import figure, show, curdoc
from bokeh.models import NumeralTickFormatter
from bokeh.models import HoverTool
from bokeh.models import ColumnDataSource
from bokeh.layouts import widgetbox, column
from bokeh.models import CustomJS, Slider
from matplotlib import colors
import pandas as pd
import numpy as np

# Read Data
df=pd.read_csv('/home/mint/SAGD_Costs.csv')


# Master source
source = ColumnDataSource(df)


# Bar Tops Data
engtop = source.data['Engineering'][0]
equiptop = source.data['Engineering'][0] + source.data['Equipment'][0]
bulktop = source.data['Engineering'][0] + source.data['Equipment'][0] + source.data['Bulk_Materials'][0]
inditop = source.data['Engineering'][0] + source.data['Equipment'][0] + source.data['Bulk_Materials'][0] + source.data['Indirects'][0]
labtop = source.data['Engineering'][0] + source.data['Equipment'][0] + source.data['Bulk_Materials'][0] + source.data['Indirects'][0] + source.data['Labour'][0]


# Source for Stupid Hovertool
engsource = ColumnDataSource(data=dict(x=[0], y=[engtop], desc = ['Engineering']))
equipsource = ColumnDataSource(data=dict(x=[0], y=[equiptop-engtop], desc = ['Equipment']))
bulksource = ColumnDataSource(data=dict(x=[0], y=[bulktop-equiptop], desc = ['Bulk Materials']))
indisource = ColumnDataSource(data=dict(x=[0], y=[inditop-bulktop], desc = ['Indirects']))
labsource = ColumnDataSource(data=dict(x=[0], y=[labtop-inditop], desc = ['Labour']))

# HoverTool Label
hover = HoverTool(
    tooltips=[
('Item', '@desc'),
('Cost', '@y{$ 0.00 a}'),
    ]
)


# Other Tools
TOOLS = 'box_zoom, box_select, resize, reset'


# Figure
p = figure(title="Capital Costs Breakdown", title_location="above", plot_width=600, plot_height=600, x_range=(-2, 2), tools=[TOOLS, hover])


# Plots
engbar = p.vbar(x=source.data['Year'][0], width=2, bottom=0,
   top=engtop, alpha=0.75, color="darkslategrey", legend="Engineering", source=engsource)

equipbar = p.vbar(x=[source.data['Year'][0]], width=2, bottom=engtop, 
   top = equiptop, alpha=0.75, color="teal", legend="Equipment", source=equipsource)

bulkbar = p.vbar(x=[source.data['Year'][0]], width=2, bottom=equiptop, 
   top=bulktop, alpha=0.75, color="cyan", legend="Bulk Materials", source=bulksource)

indibar = p.vbar(x=[source.data['Year'][0]], width=2, bottom=bulktop, 
   top=inditop, alpha=0.75, color="powderblue", legend="Indirects", source=indisource)

labbar = p.vbar(x=[source.data['Year'][0]], width=2, bottom=inditop, 
   top=labtop, alpha=0.75, color="lavender", legend="Labour", source=labsource)


# Format
p.yaxis[0].formatter = NumeralTickFormatter(format="[=10=],000")


# Set up widgets
eng_slider = Slider(start=5000000, end=100000000, value=40000000, step=5000000, title="Engineering")


def update_data(attrname, old, new):

    # Get the current slider values
    a = eng_slider.value


    # Generate the new curve
    df['Engineering'][0] = a


    source = ColumnDataSource(df)
    #source.data = dict(x=x, y=y)


for w in [eng_slider]:
    w.on_change('value', update_data)


# Set up layouts and add to document
inputs = widgetbox(eng_slider)


# Show!
curdoc().add_root(column(inputs, p))
curdoc().title = "Sliders"

Picture of Current Graph

Dataset

不确定回答您自己的问题时的礼仪...它大部分已修复,但 Hovertools 无法正常工作。由于 Hovertool 是 @y 它显示了每个项目的堆栈总数。我想让它显示出不同之处。是否可以计算 HoverTool 的值?

万一我的情况对某人有帮助,我在上面犯的错误是我用滑块更改了一个值,然后必须在将其传递到字形之前通过计算传递。

正确的方法是在更新函数中进行任何计算

如果您像我一样来自 Pandas 和 Matplotlib,您可能最终会在图表中构建 df 列调用,例如x = df['Column_name'][0]。在散景中使用 Glyphs 绘图时,我认为正确的方法是使用所需数据创建源,这样您就可以将 x 和 y 传递到 Glyph 中。请参阅:主源、获取源数据、计算顶部和底部以及下面我的代码中的新源。

# Read Data
df=pd.read_csv('/home/mint/SAGD_Costs.csv')


# Master source
source = ColumnDataSource(df)


# Get source data
a = source.data['Engineering'][0]
b = source.data['Equipment'][0]
c = source.data['Bulk_Materials'][0]
d = source.data['Indirects'][0]
e = source.data['Labour'][0]

# Calculate Top & Bottom
ab = 0
at = a
bb = a
bt = a + b
cb = a + b
ct = a + b + c
db = a + b + c
dt = a + b + c + d
eb = a + b + c + d
et = a + b + c + d + e


# New sources
engsource = ColumnDataSource(data=dict(x=[ab], y=[at], desc = ['Engineering']))
equipsource = ColumnDataSource(data=dict(x=[bb], y=[bt], desc = ['Equipment']))
bulksource = ColumnDataSource(data=dict(x=[cb], y=[ct], desc = ['Bulk Materials']))
indisource = ColumnDataSource(data=dict(x=[db], y=[dt], desc = ['Indirects']))
labsource = ColumnDataSource(data=dict(x=[eb], y=[et], desc = ['Labour']))


# HoverTool Label
hover = HoverTool(
        tooltips=[
    ('Item', '@desc'),
    ('Cost', '@y{$ 0.00 a}'),
        ]
    )


# Other Tools
TOOLS = 'box_zoom, box_select, resize, reset'


# Figure
p = figure(title="Capital Costs Breakdown", title_location="above", plot_width=600, plot_height=600, x_range=(-2, 2), tools=[TOOLS, hover])


# Plots
engbar = p.vbar(x=0, width=2, bottom = 'x',
       top ='y', alpha=0.75, color="darkslategrey", legend="Engineering", source=engsource)

equipbar = p.vbar(x=0, width=2, bottom = 'x', 
       top = 'y', alpha=0.75, color="teal", legend="Equipment", source=equipsource)

bulkbar = p.vbar(x=0, width=2, bottom = 'x', 
       top ='y', alpha=0.75, color="cyan", legend="Bulk Materials", source=bulksource)

indibar = p.vbar(x=0, width=2, bottom = 'x', 
       top ='y', alpha=0.75, color="powderblue", legend="Indirects", source=indisource)

labbar = p.vbar(x=0, width=2, bottom = 'x', 
       top = 'y', alpha=0.75, color="lavender", legend="Labour", source=labsource)


# Format
p.yaxis[0].formatter = NumeralTickFormatter(format="[=10=],000")


# Set up widgets
eng_slider = Slider(start=5000000, end=100000000, value=40000000, step=5000000, title="Engineering")
equip_slider = Slider(start=5000000, end=100000000, value=40000000, step=5000000, title="Equipment")
bulk_slider = Slider(start=5000000, end=100000000, value=40000000, step=5000000, title="Bulk_Materials")
indi_slider = Slider(start=5000000, end=100000000, value=40000000, step=5000000, title="Indirects")
lab_slider = Slider(start=5000000, end=100000000, value=40000000, step=5000000, title="Labour")

def update_data(attrname, old, new):

    # Get the current slider values
    a = eng_slider.value
    b = equip_slider.value
    c = bulk_slider.value
    d = indi_slider.value
    e = lab_slider.value

    # Calculate Top & Bottom
    ab = 0
    at = a
    bb = a
    bt = a + b
    cb = a + b
    ct = a + b + c
    db = a + b + c
    dt = a + b + c + d
    eb = a + b + c + d
    et = a + b + c + d + e


    # New sources
    engsource.data=dict(x=[ab], y=[at], desc = ['Engineering'])
    equipsource.data=dict(x=[bb], y=[bt], desc = ['Equipment'])
    bulksource.data=dict(x=[cb], y=[ct], desc = ['Bulk Materials'])
    indisource.data=dict(x=[db], y=[dt], desc = ['Indirects'])
    labsource.data=dict(x=[eb], y=[et], desc = ['Labour'])



for w in [eng_slider, equip_slider, bulk_slider, indi_slider, lab_slider]:
    w.on_change('value', update_data)


# Set up layouts and add to document
inputs = widgetbox(eng_slider, equip_slider, bulk_slider, indi_slider, lab_slider)


# Show!
curdoc().add_root(column(inputs, p))
curdoc().title = "Sliders"