Исходный вопрос
У меня есть график, определенный функцией y / 100 = (x / 100) ^ n
Я хочу определить x с помощью ползунка на bokeh
и ( i) пометьте точку пунктирными линиями на осях x и y и (ii) найдите соответствующую координату y в таблице под ползунком.
Вот то, что я пробовал, но без успех (ползунок, таблица и график все есть, но при перемещении ползунка ничего не происходит).
### Slider:: Interactive Graph with Python Bokeh
### Source:: https://docs.bokeh.org/en/latest/docs/gallery/slider.html
import numpy as np
import pandas as pd
from bokeh.layouts import row, column
from bokeh.models import CustomJS, Slider, Title
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models.widgets import DataTable, TableColumn
#%% Read data from graph
## Define parameters and function
n = 0.333333333333
w1, h1 = 500, 400
w2, h2 = 200, 80
def x_to_y(x):
y = 100*((x*0.01)**n)
y = np.round(y, 2)
return y
## Initialize the graph
x = np.linspace(0, 100, 1001)
y = x_to_y(x)
cust_slider = Slider(start=0, end=100, step=0.1,
value=30,
width=w2, height=h2,
title="% Customers")
## Create a table with (x, y) values of the graph...
## read x from slider
tabl = dict(cust=[cust_slider.value],
potn=[x_to_y(cust_slider.value)])
values = ColumnDataSource(tabl)
columns = [TableColumn(field="cust", title="% Customers"),
TableColumn(field="potn", title="% Potential")]
data_table = DataTable(source=values,
columns=columns,
width=w2, height=h2,
editable=True)
## Plot the graph from function (later to be read from source)
source = ColumnDataSource(data=dict(x=x, y=y))
TOOLTIPS = [("Customers: ", "$x"),
("Potential: ", "$y")
]
plot = figure(x_range=(0,100),
y_range=(0, 100),
tooltips=TOOLTIPS,
plot_width=w1,
plot_height=h1)
plot.line('x', 'y', source=source)
plot.line([0,cust_slider.value,cust_slider.value],
[x_to_y(cust_slider.value),x_to_y(cust_slider.value),0],
line_dash="dashed"
)
plot.add_layout(Title(text="Customers (%)", align="center"), "below")
plot.add_layout(Title(text="Potential (%)", align="center"), "left")
## Try to make table and plot interactive (...table not interacting)
callback = CustomJS(args=dict(source=tabl,
slider=cust_slider),
code="""const source = source.data;
const xx = source['cust'];
const yy = source['potn'];
xx = slider.value;
yy = x_to_y(xx);
source.change.emit();"""
)
cust_slider.js_on_change('value', callback)
layout = row(plot,
column(cust_slider,
data_table)
)
output_file("slider_mwe.html", title="Graph")
show(layout)
Вот снимок полученной мной фигуры:
Редактировать: Решение, вдохновленное bigreddot
### Slider:: Interactive Graph with Python Bokeh
### Source:: https://docs.bokeh.org/en/latest/docs/gallery/slider.html
import numpy as np
import pandas as pd
from bokeh.layouts import row, column
from bokeh.models import CustomJS, Slider, Title
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models.widgets import DataTable, TableColumn
#%% Read data from graph
## Define parameters and function
n = 0.333333333333
w1, h1 = 500, 400
w2, h2 = 200, 80
def x_to_y(x):
y = 100*((x*0.01)**n)
y = np.round(y, 2)
return y
## Initialize the graph
x = np.linspace(0, 100, 1001)
y = x_to_y(x)
## ...and the data-point in focus
x0 = np.linspace(30, 30, 1)
y0 = x_to_y(x0)
cust_slider = Slider(start=0, end=100, step=0.1,
value=30,
width=w2, height=h2,
title="% Customers")
## Create a table with (x, y) values of the graph...
## read x from slider
tabl = dict(cust=[cust_slider.value],
potn=[x_to_y(cust_slider.value)])
tlin = ColumnDataSource(data=dict(lin_cust=np.insert(x0, 0, [0, x0[0]]),
lin_potn=np.insert(y0, 1, [y0[0], 0])))
values = ColumnDataSource(tabl)
columns = [TableColumn(field="cust", title="% Customers"),
TableColumn(field="potn", title="% Potential")]
data_table = DataTable(source=values,
columns=columns,
width=w2, height=h2,
editable=True)
## Plot the graph from function (later to be read from source)
source = ColumnDataSource(data=dict(x=x, y=y))
TOOLTIPS = [("Customers: ", "$x"),
("Potential: ", "$y")
]
plot = figure(x_range=(0,100),
y_range=(0, 100),
tooltips=TOOLTIPS,
plot_width=w1,
plot_height=h1)
plot.line('x', 'y', source=source)
plot.line('lin_cust', 'lin_potn', source=tlin, line_dash="dashed")
plot.add_layout(Title(text="Customers (%)", align="center"), "below")
plot.add_layout(Title(text="Potential (%)", align="center"), "left")
## Plot made interactive by writing to values & tlin
## (rather than declared constants)
callback = CustomJS(args=dict(source=source,
values=values,
tlin=tlin,
slider=cust_slider),
code="""values.data['cust'][0] = slider.value;
for (var i = 0; i < source.data['x'].length; i++)
{
if (source.data['x'][i] == slider.value)
{
values.data['potn'][0] = source.data['y'][i];
}
}
tlin.data['lin_cust'][1] = values.data['cust'][0]
tlin.data['lin_cust'][2] = values.data['cust'][0]
tlin.data['lin_potn'][0] = values.data['potn'][0]
tlin.data['lin_potn'][1] = values.data['potn'][0]
values.change.emit();
tlin.change.emit();"""
)
cust_slider.js_on_change('value', callback)
layout = row(plot,
column(cust_slider,
data_table)
)
output_file("slider_mwe.html", title="Graph")
show(layout)
, которое дает все на месте:
... за исключением того, что время от времени угол пунктирной линии выпадает из кривой.
Ну, я подумал, что нужно дать эту небольшую свободу ради того, что все это имеет сделано для меня!