【问题标题】:Bokeh HoverTool with multiple plots具有多个绘图的 Bokeh HoverTool
【发布时间】:2021-05-10 14:06:03
【问题描述】:

我尝试使用散景制作仪表板,但我的悬停工具坏了,因为我试图在单个图表中绘制数据。我找到了这个问题的一些答案,但不幸的是我无法将它调整到我的代码中。 我希望能够关闭和打开数据的可见性。 (另一个问题是,如果将日期范围更改为不存在任何值的区域,则整个图消失了,也许有人对此有解决方法。在旧版本中,当范围在数据上时,我尝试仅更改源发射,但这会导致其他问题。也许有人对此也有想法。) 这是我的代码的简化最小工作版本:

import pandas as pd
import numpy as np
from datetime import datetime

from bokeh.models import Button, CheckboxGroup, ColumnDataSource, CustomJS, DatetimeTickFormatter, HoverTool
from bokeh.models.widgets import DateRangeSlider
from bokeh.layouts import layout, column, row
from bokeh.palettes import Category20
from bokeh.plotting import figure, output_file, show

datesX = pd.date_range(start='1/1/2018', periods=100) #incl. '2018-01-01' to '2018-04-10'
numbof = 3
datesX = datesX[0:10].union(datesX[20:100])
valuesY = pd.DataFrame(np.random.randint(0,25,size=(90, numbof)), columns=list((f'V{i}' for i in range(numbof))))
movingY = pd.DataFrame(np.random.randint(0,10,size=(90, numbof)), columns=list((f'M{i}' for i in range(numbof))))

preCDS ={}

for i in range(numbof):
    preCDS.update({f'x{i}': datesX})
    preCDS.update({f'y{i}': valuesY[f'V{i}']})
    preCDS.update({f'm{i}': movingY[f'M{i}']})
    
source = ColumnDataSource(preCDS) 
source2 = ColumnDataSource(preCDS)

# output to static HTML file
output_file('file.html')

# create a new plot with a title and axis labels
p = figure(
    title='file1', x_axis_label='Date', y_axis_label='yValue',
    y_range=(0, 30), x_axis_type='datetime',
    tools="pan, wheel_zoom, box_zoom, reset, save",
    plot_width=1800, plot_height=480)

ypsilon = [f'y{i}' for i in range(numbof)]
em = [f'm{i}' for i in range(numbof)]

hover = HoverTool(line_policy='nearest', names=ypsilon + em,
    tooltips=[('Timestamp', '@x{%Y-%m-%d %H:%M:%S}'), ('Wert', '@y')], ###what to do with @x and @y to make it iterable: x0, x1, x2, y0, ...
    formatters={'@x': 'datetime'})

p.add_tools(hover)

date_range_slider = DateRangeSlider(
    title="DateRange", start=datesX[0], end=datesX[89],
    value=(datesX[0], datesX[89]), width=800, bar_color = (32,120,180))

checkbox_vals = CheckboxGroup(labels=[f'Plot {i+1}' for i in range(numbof)], active=[i for i in range(numbof)])
checkbox_mova = CheckboxGroup(labels=[f'Plot {i+1}' for i in range(numbof)], active=[i for i in range(numbof)])

vals=[]
mova=[]

for i in range(numbof):
    vals.append(p.circle(x=f'x{i}', y=f'y{i}', source=source, color = Category20[20][2*i], size=3, name=f'y{i}'))
    mova.append(p.line(x=f'x{i}', y=f'm{i}', source=source, line_color = Category20[20][2*i+1], line_width=2, name=f'm{i}'))


rangeCallback = CustomJS(args=dict(source=source, ref_source=source2, dRs=date_range_slider), code="""
        
    // print out array of date from, date to
    //console.log(dRs.value); 

    // dates returned from slider are not at round intervals and include time
    const date_from = Date.parse(new Date(dRs.value[0]).toDateString());
    const date_to = Date.parse(new Date(dRs.value[1]).toDateString());
    //console.log(date_from, date_to)

    // Creating the Data Sources
    const data = source.data;
    const ref = ref_source.data;

    for (const [key, value] of Object.entries(data)) {
        console.log(key)
        // Creating new Array and appending correctly parsed dates
        if(key.indexOf("x") == 0){
            let new_ref = [];
            let from_pos = [];
            let to_pos = [];
            
            ref[key].forEach(elem => {
                elem = Date.parse(new Date(elem).toDateString()); 
                new_ref.push(elem);
            })
            
            // Creating Indices with new Array
            from_pos[key] = new_ref.indexOf(date_from);
            to_pos[key] = new_ref.indexOf(date_to) + 1;

            // re-create the source data from "reference"
            const dataKeyM = key.replace('x', 'm');
            const dataKeyY = key.replace('x', 'y');
            
            data[dataKeyM] = ref[dataKeyM].slice(from_pos[key], to_pos[key]);
            data[key] = ref[key].slice(from_pos[key], to_pos[key]);
            data[dataKeyY] = ref[dataKeyY].slice(from_pos[key], to_pos[key]);
        }
    }
    /*if (new_ref.includes(date_from) && new_ref.includes(date_to)) {
        source.change.emit();
    }*/
    source.change.emit();  
    
    """)

checkboxValsCallback = CustomJS(args=dict(vals=vals, checkboxVals=checkbox_vals), code="""

    var indexOf = [].indexOf || function(item) { 
        for (var i = 0, l = this.length; i < l; i++) { 
            if (i in this && this[i] === item) 
                return i; 
            } 
        return -1; 
    };
    
    //vals.visible = indexOf.call(checkboxVals.active,0)>=0;
    for (let i=0;i<vals.length;i++) {
        vals[i].visible = indexOf.call(checkboxVals.active,i)>=0;
    }
    
    """)

checkboxMovaCallback = CustomJS(args=dict(mova=mova, checkboxMova=checkbox_mova), code="""

    var indexOf = [].indexOf || function(item) { 
        for (var i = 0, l = this.length; i < l; i++) { 
            if (i in this && this[i] === item) 
                return i; 
            } 
        return -1; 
    };
    
    //mova.visible = indexOf.call(checkboxMova.active,0)>=0;
    for (let i=0;i<mova.length;i++) {
        mova[i].visible = indexOf.call(checkboxMova.active,i)>=0;
    }
    
    """)

checkbox_vals.js_on_change('active', checkboxValsCallback)
checkbox_mova.js_on_change('active', checkboxMovaCallback)
date_range_slider.js_on_change('value', rangeCallback)

b1 = Button(label="select all", max_width = 300, button_type="primary")
b1.js_on_click(CustomJS(args=dict(s=checkbox_vals, t=checkbox_mova), code="""
    s.active = [0,1,2]
    t.active = [0,1,2]
    """))

b2 = Button(label="unselect all", max_width = 300)
b2.js_on_click(CustomJS(args=dict(s=checkbox_vals, t=checkbox_mova), code="""
    s.active = []
    t.active = []
    """))

layout = column(p, row(checkbox_vals, checkbox_mova, date_range_slider), row(b1, b2))
show(layout)

提前谢谢你。

【问题讨论】:

  • 你应该一次问一个问题

标签: python hover bokeh


【解决方案1】:

对于工具提示:您为悬停指定了 xy,但是当您查看例如数据源时您存储在mova 中的行字形您会看到没有xy,但有许多不同的字段。我将工具提示更改为x1y0,但我不确定这是否是您想要的。只需在某处放置一个断点并查看例如在mova[0].data_source.data 中,您将看到所有可用字段并选择您想要的工具提示。

hover = HoverTool(line_policy='nearest', names=ypsilon + em,
                  tooltips=[('Timestamp', '@x1{%Y-%m-%d %H:%M:%S}'), ('Wert', '@y0')],
                  formatters={'@x1': 'datetime'})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多