散景:在图像图中实现自定义 javascript
Bokeh: implementing custom javascript in an image plot
我正在尝试在 Bokeh 中结合这两个示例:
http://docs.bokeh.org/en/latest/docs/gallery/image.html
http://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-widgets
这个想法似乎很简单。我想绘制第一个 link 中显示的图像,然后使用交互式滑块改变正弦函数的频率:
import numpy as np
from bokeh.plotting import figure, show, output_file
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.io import vform
N = 10
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
output_file("image.html", title="image.py example")
source = ColumnDataSource(data={'d': d, 'x': x, 'y': y})
p = figure(x_range=[0, 10], y_range=[0, 10])
p.image([source.data['d']], x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11")
callback = CustomJS(args=dict(source=source), code="""
var data = source.get('data');
var f = cb_obj.get('value')
x = data['x']
y = data['y']
d = data['d']
for (i = 0; i < x.length; i++) {
for (i = 0; i < x.length; i++){
d[i][j] = Math.sin(f*x[i])*Math.cos(y[j])
}
source.trigger('change');
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="angular frequency", callback=callback)
layout = vform(slider, p)
show(layout)
图表绘制正确,但图像永远不会更新。问题几乎肯定存在于我如何绘制图像:
p.image([source.data['d']], x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11")
我不认为这是将绘图正确附加到源对象的方式。我只是传入一个数组,这解释了为什么当源更改时绘图没有更新,但我不确定图像函数的正确方法是什么。如果我将语句更改为:
p.image(['d'], x=[0], y=[0], dw=[10], dh=[10], source=source, palette="Spectral11")
它不会正确绘制。我不确定这只是语法问题还是更深层次的问题。任何指针将不胜感激。提前致谢。
几天来我一直在处理类似的问题。最后我成功了。首先,请注意 ColumnDataSource 函数中的方括号 []。数据允许多个图像。因此,在回调函数中,您应该使用 [0] 来获取一张图像的数据。此外,在图像源中使用 'x' 和 'y' 与图像 x=[0] 和 y[0] 的位置冲突,因此我使用了 xx 和 yy。我想提一下,我从 tobyhodges color_sliders.py 的示例中借用了代码:一种将滑块信息推送到已定义的回调函数的方法。这里的代码:
import numpy as np
from bokeh.plotting import figure, show, output_file
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.io import vform
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
output_file("image.html", title="image.py example")
source = ColumnDataSource(data={'d': [d], 'xx': [x], 'yy': [y]})
p = figure(x_range=[0, 10], y_range=[0, 10])
p.image(image="d", x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11",source=source)
callback = CustomJS(args=dict(source=source), code="""
var xx = source.get('data')['xx'][0];
var yy = source.get('data')['yy'][0];
var d = source.get('data')['d'][0];
var f = slider.get('value');
for (var i = 0; i < xx.length; i++) {
for (var j = 0; j < yy.length; j++){
d[i][j] = Math.sin(f * xx[i]) * Math.cos(yy[j]);
}
}
source.trigger('change');
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="angular frequency", callback=callback)
callback.args['slider'] = slider
layout = vform(slider, p)
show(layout)
为了避免在最新版本的bokeh(我刚刚安装的版本是0.12.3)中出现弃用警告,我将这段代码修改如下。在此代码中,我没有为图像使用数据源。在 CustomJS
中,我传递图像句柄“im”并将数据源获取为 im.data_source
。
import numpy as np
import bokeh
import bokeh.plotting
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
source = bokeh.models.ColumnDataSource(data={'x': [x], 'y': [y]})
p = bokeh.plotting.figure(x_range=[0, 10], y_range=[0, 10])
im = p.image(image=[d], x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11")
callback = bokeh.models.CustomJS(args=dict(source=source,im=im), code="""
var x = source.data['x'][0];
var y = source.data['y'][0];
var image_source = im.data_source;
var d = image_source.data['image'][0];
var f = slider.value;
for (var i = 0; i < x.length; i++) {
for (var j = 0; j < y.length; j++){
d[i][j] = Math.sin(f * x[i]) * Math.cos(f * y[j]);
}
}
image_source.trigger('change');
""")
slider = bokeh.models.Slider(start=0.1, end=4, value=1, step=.1,
title="angular frequency", callback=callback)
callback.args['slider'] = slider
layout = bokeh.models.layouts.Column(slider, p)
bokeh.io.output_file("image.html", title="image.py example")
bokeh.io.save(layout)
散景版本 0.12.4 更新:
更改:现在的输出是 Jupyter
笔记本。要获取html页面,按照之前的版本即可。此散景版本的新功能:JavaScript
上的数组现在是一维数组。
import numpy as np
import bokeh
import bokeh.plotting
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
source = bokeh.models.ColumnDataSource(data={'x': [x], 'y': [y]})
p = bokeh.plotting.figure(plot_width=300, plot_height=300,x_range=[0, 10], y_range=[0, 10])
im = p.image(image=[d], x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11")
callback = bokeh.models.CustomJS(args=dict(source=source,im=im), code="""
var x = source.data['x'][0];
var y = source.data['y'][0];
var image_source = im.data_source;
var d = image_source.data['image'][0];
var f = slider.value;
for (var j = 0; j < y.length; j++){
for (var i = 0; i < x.length; i++) {
d[j*y.length + i] = Math.sin(f * x[i]) * Math.cos(f * y[j]);
}
}
image_source.trigger('change');
""")
slider = bokeh.models.Slider(start=0.1, end=4, value=1, step=.1,
title="angular frequency",callback=callback)
callback.args['slider'] = slider
layout = bokeh.models.layouts.Row(p,slider)
bokeh.io.output_notebook()
bokeh.io.show(layout)
散景版本 0.12.15 更新:
import numpy as np
import bokeh
import bokeh.plotting
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
source = bokeh.models.ColumnDataSource(
data=dict(values=[d],x_vals=[x],y_vals=[y]))
p = bokeh.plotting.figure(plot_width=300, plot_height=300,
x_range=[0, 10], y_range=[0, 10])
p.image(image='values', x=0, y=0, dw=10, dh=10,
palette="Spectral11", source=source)
callback = bokeh.models.CustomJS(args=dict(source=source), code="""
var f = slider.value;
var x = source.data['x_vals'][0];
var y = source.data['y_vals'][0];
var d = source.data['values'][0];
for (var j = 0; j < y.length; j++){
for (var i = 0; i < x.length; i++) {
d[j*y.length + i] = Math.sin(f*x[i]) * Math.cos(f*y[j]);
}
}
source.change.emit();
""")
slider = bokeh.models.Slider(start=0.1, end=4, value=1, step=.1,
title="angular frequency",callback=callback)
callback.args['slider'] = slider
layout = bokeh.models.layouts.Row(p,slider)
bokeh.io.output_notebook()
bokeh.io.show(layout)
散景版本 2.4.2 更新:
最近的版本有些变化。适用于版本 2.4.2(最新版本为 2022 年 2 月)的代码如下。
import numpy as np
import bokeh
import bokeh.plotting
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
source = bokeh.models.ColumnDataSource(data=dict(
x_values=x, # needed in CustomJS for calculating d
y_values=y, # needed in CustomJS for calculating d
))
source_im = bokeh.models.ColumnDataSource(data=dict(
image=[d], # needed for p.image
x=[0], # needed for p.image
y=[0], # needed for p.image
dw = [10], # needed for p.image
dh=[10] # needed for p.image
))
p = bokeh.plotting.figure(plot_width=300, plot_height=300,
x_range=[0, 10], y_range=[0, 10])
im = p.image(source=source_im, palette="Spectral11")
slider_callback = bokeh.models.CustomJS(args=dict(
source=source,source_im=source_im), code="""
var x = source.data['x_values'];
var y = source.data['y_values'];
var d = source_im.data['image'][0];
var f = slider.value;
for (var j = 0; j < y.length; j++){
for (var i = 0; i < x.length; i++) {
d[j*y.length + i] = Math.sin(f * x[i]) * Math.cos(f * y[j]);
}
}
source_im.change.emit();
""")
slider = bokeh.models.Slider(start=0.1, end=4, value=1, step=.1,
title="angular frequency")
slider.js_on_change('value', slider_callback)
slider_callback.args['slider'] = slider
layout = bokeh.models.layouts.Row(p,slider)
bokeh.io.output_notebook()
bokeh.io.show(layout)
我正在尝试在 Bokeh 中结合这两个示例:
http://docs.bokeh.org/en/latest/docs/gallery/image.html http://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html#customjs-for-widgets
这个想法似乎很简单。我想绘制第一个 link 中显示的图像,然后使用交互式滑块改变正弦函数的频率:
import numpy as np
from bokeh.plotting import figure, show, output_file
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.io import vform
N = 10
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
output_file("image.html", title="image.py example")
source = ColumnDataSource(data={'d': d, 'x': x, 'y': y})
p = figure(x_range=[0, 10], y_range=[0, 10])
p.image([source.data['d']], x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11")
callback = CustomJS(args=dict(source=source), code="""
var data = source.get('data');
var f = cb_obj.get('value')
x = data['x']
y = data['y']
d = data['d']
for (i = 0; i < x.length; i++) {
for (i = 0; i < x.length; i++){
d[i][j] = Math.sin(f*x[i])*Math.cos(y[j])
}
source.trigger('change');
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="angular frequency", callback=callback)
layout = vform(slider, p)
show(layout)
图表绘制正确,但图像永远不会更新。问题几乎肯定存在于我如何绘制图像:
p.image([source.data['d']], x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11")
我不认为这是将绘图正确附加到源对象的方式。我只是传入一个数组,这解释了为什么当源更改时绘图没有更新,但我不确定图像函数的正确方法是什么。如果我将语句更改为:
p.image(['d'], x=[0], y=[0], dw=[10], dh=[10], source=source, palette="Spectral11")
它不会正确绘制。我不确定这只是语法问题还是更深层次的问题。任何指针将不胜感激。提前致谢。
几天来我一直在处理类似的问题。最后我成功了。首先,请注意 ColumnDataSource 函数中的方括号 []。数据允许多个图像。因此,在回调函数中,您应该使用 [0] 来获取一张图像的数据。此外,在图像源中使用 'x' 和 'y' 与图像 x=[0] 和 y[0] 的位置冲突,因此我使用了 xx 和 yy。我想提一下,我从 tobyhodges color_sliders.py 的示例中借用了代码:一种将滑块信息推送到已定义的回调函数的方法。这里的代码:
import numpy as np
from bokeh.plotting import figure, show, output_file
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.io import vform
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
output_file("image.html", title="image.py example")
source = ColumnDataSource(data={'d': [d], 'xx': [x], 'yy': [y]})
p = figure(x_range=[0, 10], y_range=[0, 10])
p.image(image="d", x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11",source=source)
callback = CustomJS(args=dict(source=source), code="""
var xx = source.get('data')['xx'][0];
var yy = source.get('data')['yy'][0];
var d = source.get('data')['d'][0];
var f = slider.get('value');
for (var i = 0; i < xx.length; i++) {
for (var j = 0; j < yy.length; j++){
d[i][j] = Math.sin(f * xx[i]) * Math.cos(yy[j]);
}
}
source.trigger('change');
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="angular frequency", callback=callback)
callback.args['slider'] = slider
layout = vform(slider, p)
show(layout)
为了避免在最新版本的bokeh(我刚刚安装的版本是0.12.3)中出现弃用警告,我将这段代码修改如下。在此代码中,我没有为图像使用数据源。在 CustomJS
中,我传递图像句柄“im”并将数据源获取为 im.data_source
。
import numpy as np
import bokeh
import bokeh.plotting
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
source = bokeh.models.ColumnDataSource(data={'x': [x], 'y': [y]})
p = bokeh.plotting.figure(x_range=[0, 10], y_range=[0, 10])
im = p.image(image=[d], x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11")
callback = bokeh.models.CustomJS(args=dict(source=source,im=im), code="""
var x = source.data['x'][0];
var y = source.data['y'][0];
var image_source = im.data_source;
var d = image_source.data['image'][0];
var f = slider.value;
for (var i = 0; i < x.length; i++) {
for (var j = 0; j < y.length; j++){
d[i][j] = Math.sin(f * x[i]) * Math.cos(f * y[j]);
}
}
image_source.trigger('change');
""")
slider = bokeh.models.Slider(start=0.1, end=4, value=1, step=.1,
title="angular frequency", callback=callback)
callback.args['slider'] = slider
layout = bokeh.models.layouts.Column(slider, p)
bokeh.io.output_file("image.html", title="image.py example")
bokeh.io.save(layout)
散景版本 0.12.4 更新:
更改:现在的输出是 Jupyter
笔记本。要获取html页面,按照之前的版本即可。此散景版本的新功能:JavaScript
上的数组现在是一维数组。
import numpy as np
import bokeh
import bokeh.plotting
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
source = bokeh.models.ColumnDataSource(data={'x': [x], 'y': [y]})
p = bokeh.plotting.figure(plot_width=300, plot_height=300,x_range=[0, 10], y_range=[0, 10])
im = p.image(image=[d], x=[0], y=[0], dw=[10], dh=[10], palette="Spectral11")
callback = bokeh.models.CustomJS(args=dict(source=source,im=im), code="""
var x = source.data['x'][0];
var y = source.data['y'][0];
var image_source = im.data_source;
var d = image_source.data['image'][0];
var f = slider.value;
for (var j = 0; j < y.length; j++){
for (var i = 0; i < x.length; i++) {
d[j*y.length + i] = Math.sin(f * x[i]) * Math.cos(f * y[j]);
}
}
image_source.trigger('change');
""")
slider = bokeh.models.Slider(start=0.1, end=4, value=1, step=.1,
title="angular frequency",callback=callback)
callback.args['slider'] = slider
layout = bokeh.models.layouts.Row(p,slider)
bokeh.io.output_notebook()
bokeh.io.show(layout)
散景版本 0.12.15 更新:
import numpy as np
import bokeh
import bokeh.plotting
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
source = bokeh.models.ColumnDataSource(
data=dict(values=[d],x_vals=[x],y_vals=[y]))
p = bokeh.plotting.figure(plot_width=300, plot_height=300,
x_range=[0, 10], y_range=[0, 10])
p.image(image='values', x=0, y=0, dw=10, dh=10,
palette="Spectral11", source=source)
callback = bokeh.models.CustomJS(args=dict(source=source), code="""
var f = slider.value;
var x = source.data['x_vals'][0];
var y = source.data['y_vals'][0];
var d = source.data['values'][0];
for (var j = 0; j < y.length; j++){
for (var i = 0; i < x.length; i++) {
d[j*y.length + i] = Math.sin(f*x[i]) * Math.cos(f*y[j]);
}
}
source.change.emit();
""")
slider = bokeh.models.Slider(start=0.1, end=4, value=1, step=.1,
title="angular frequency",callback=callback)
callback.args['slider'] = slider
layout = bokeh.models.layouts.Row(p,slider)
bokeh.io.output_notebook()
bokeh.io.show(layout)
散景版本 2.4.2 更新:
最近的版本有些变化。适用于版本 2.4.2(最新版本为 2022 年 2 月)的代码如下。
import numpy as np
import bokeh
import bokeh.plotting
N = 100
x = np.linspace(0, 10, N)
y = np.linspace(0, 10, N)
xx, yy = np.meshgrid(x, y)
d = np.sin(xx)*np.cos(yy)
source = bokeh.models.ColumnDataSource(data=dict(
x_values=x, # needed in CustomJS for calculating d
y_values=y, # needed in CustomJS for calculating d
))
source_im = bokeh.models.ColumnDataSource(data=dict(
image=[d], # needed for p.image
x=[0], # needed for p.image
y=[0], # needed for p.image
dw = [10], # needed for p.image
dh=[10] # needed for p.image
))
p = bokeh.plotting.figure(plot_width=300, plot_height=300,
x_range=[0, 10], y_range=[0, 10])
im = p.image(source=source_im, palette="Spectral11")
slider_callback = bokeh.models.CustomJS(args=dict(
source=source,source_im=source_im), code="""
var x = source.data['x_values'];
var y = source.data['y_values'];
var d = source_im.data['image'][0];
var f = slider.value;
for (var j = 0; j < y.length; j++){
for (var i = 0; i < x.length; i++) {
d[j*y.length + i] = Math.sin(f * x[i]) * Math.cos(f * y[j]);
}
}
source_im.change.emit();
""")
slider = bokeh.models.Slider(start=0.1, end=4, value=1, step=.1,
title="angular frequency")
slider.js_on_change('value', slider_callback)
slider_callback.args['slider'] = slider
layout = bokeh.models.layouts.Row(p,slider)
bokeh.io.output_notebook()
bokeh.io.show(layout)