Python Bokeh:在回调中更新散点图颜色

3
我最近才开始使用Bokeh。我有一个散点图,我想根据第三个属性(比如数量,其中x轴是日期,y轴是该时间点的给定值)为每个标记着色。
假设我的数据在一个数据框中,我用线性颜色映射实现了这一点,如下所示:
min_q = df.quantity.min()
max_q = df.quantity.max()
mapper = linear_cmap(field_name='quantity', palette=palettes.Spectral6, low=min_q, high=max_q)
source = ColumnDataSource(data=get_data(df))

p = figure(x_axis_type="datetime")
p.scatter(x="date_column", y="value", marker="triangle", fill_color=mapper, line_color=None, source=source)
color_bar = ColorBar(color_mapper=mapper['transform'], width=8,  location=(0,0))
p.add_layout(color_bar, 'right')

这似乎按预期工作。下面是我启动bokeh服务器后得到的图表。

enter image description here

然后我有一个回调函数update(),在某个小部件(选择器或时间选择器)的值改变时触发。

def update():
    # get new df (according to new date/select)
    df = get_df()
    # update min/max for colormap
    min_q = df.quantity.min()
    max_q = df.quantity.max()
    # I think I should not create a new mapper but doing so I get closer
    mapper = linear_cmap(field_name='quantity', palette=palettes.Spectral6 ,low=min_q, high=max_q)
    color_bar.color_mapper=mapper['transform'] 
    source.data = get_data(df)
    # etc

这是我能找到的最接近的。颜色图随着新值的更新而更新,但似乎标记的颜色仍然遵循原始模式。请参见下面的图片(鉴于数量,我希望是绿色,但它是蓝色的,因为它仍然被视为小于4000,就像在回调之前的第一个情节的地图中一样)。

enter image description here

我应该只需在数据框中添加一个“颜色”列吗?我感觉有一种更简单/更方便的方法可以做到这一点。

编辑: 这里是使用bigreddot回答的最小工作示例:

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.plotting import figure
from bokeh.models import Button, ColumnDataSource, ColorBar, HoverTool
from bokeh.palettes import Spectral6
from bokeh.transform import linear_cmap
import numpy as np

x = [1,2,3,4,5,7,8,9,10]
y = [1,2,3,4,5,7,8,9,10]
z = [1,2,3,4,5,7,8,9,10]



source = ColumnDataSource(dict(x=x, y=y, z=z))

#Use the field name of the column source
mapper = linear_cmap(field_name='z', palette=Spectral6 ,low=min(y) ,high=max(y))

p = figure(plot_width=300, plot_height=300, title="Linear Color Map Based on Y")
p.circle(x='x', y='y', line_color=mapper,color=mapper, fill_alpha=1, size=12, source=source)

color_bar = ColorBar(color_mapper=mapper['transform'], width=8,  location=(0,0))
p.add_tools(HoverTool(tooltips="@z", show_arrow=False, point_policy='follow_mouse'))
p.add_layout(color_bar, 'right')

b = Button()

def update():
    new_z = np.exp2(z)
    mapper = linear_cmap(field_name='z', palette=Spectral6 ,low=min(new_z), high=max(new_z))
    color_bar.color_mapper=mapper['transform'] 
    source.data = dict(x=x, y=y, z=new_z)

b.on_click(update)

curdoc().add_root(column(b, p))

更新后,圆圈的颜色将根据原始比例进行着色:大于10的所有内容将变为红色。相反,我希望在最后3个圆圈上方之前一切都是蓝色,最后3个圆圈应分别着以绿色、黄色和红色。
1个回答

4

可能是一个bug,欢迎您开启一个GitHub问题。

即便如此,上述代码并不代表Bokeh使用的最佳实践,最好的方法是:尽可能地进行最小化更新。在这种情况下,这意味着在现有的颜色转换上设置新的属性值,而不是替换现有的颜色转换。

以下是完整的工作示例(使用Bokeh 1.0.2制作),演示了数据列变化时glyph的颜色映射颜色的更新:

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.plotting import figure
from bokeh.models import Button, ColumnDataSource, ColorBar
from bokeh.palettes import Spectral6
from bokeh.transform import linear_cmap

x = [1,2,3,4,5,7,8,9,10]
y = [1,2,3,4,5,7,8,9,10]
z = [1,2,3,4,5,7,8,9,10]

#Use the field name of the column source
mapper = linear_cmap(field_name='z', palette=Spectral6 ,low=min(y) ,high=max(y))

source = ColumnDataSource(dict(x=x, y=y, z=z))

p = figure(plot_width=300, plot_height=300, title="Linear Color Map Based on Y")
p.circle(x='x', y='y', line_color=mapper,color=mapper, fill_alpha=1, size=12, source=source)

color_bar = ColorBar(color_mapper=mapper['transform'], width=8,  location=(0,0))
p.add_layout(color_bar, 'right')

b = Button()

def update():
    new_z = np.exp2(z)

    # update the existing transform
    mapper['transform'].low=min(new_z)
    mapper['transform'].high=max(new_z)

    source.data = dict(x=x, y=y, z=new_z)

b.on_click(update)

curdoc().add_root(column(b, p))

以下是原始绘图:

输入图像描述

在点击按钮后,以下是更新的绘图:

输入图像描述


谢谢您的回答。我认为这里的问题是你的颜色图中的比例尺没有改变。在这种情况下,我也没有问题。我的意思是,如果我的比例尺最初是10-100,在下一个图中它变成了1-10,那么数量为10的标记将被着色为蓝色,而不是红色,因为根据第一张图,它们本来应该是蓝色的。我会尝试提供一个有效的示例。 - Tommy
更明确地说,在回调时,数量的数量级可能会发生相当大的变化。假设在您的示例中,您采用exp(z)而不是reversed(z)。那么几乎所有的圆圈都将是红色的,这可能并不是非常有信息量的(10和1000都将是红色的,但相差很大)。我需要颜色条相应地进行缩放,例如步骤应为2-16-...100,而不是2-4-...10。希望现在更清楚了。 - Tommy
添加了一个编辑,修改了您的工作示例以展示我的意思。再次感谢。 - Tommy
好的,那似乎是一个bug,但有一种方法可以解决,并且在任何情况下都是更好的做法。我已经更新了答案。 - bigreddot
哦,这似乎有效。谢谢,还有关于良好实践的提示。替换颜色转换确实感觉不对。我还不太了解 Bokeh,在这种情况下我错过了映射器的属性。 - Tommy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接