【发布时间】: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)
提前谢谢你。
【问题讨论】:
-
你应该一次问一个问题