Я только недавно начал использовать боке. У меня есть диаграмма рассеяния, на которой я хотел бы раскрасить каждый маркер в соответствии с определенным третьим свойством (скажем, количеством, в то время как ось 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')
Кажется, это работает, как ожидалось. Ниже показан сюжет, который я получаю при запуске сервера боке.
Затем у меня есть функция обратного вызова Обновить(), запускаемая при изменении значения в некотором виджете (выбор или средство выбора времени).
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, как на карте первого графика перед обратным вызовом).
Должен ли я просто добавить во фрейм данных столбец «цвет»? Я считаю, что есть более простой / удобный способ сделать это.
РЕДАКТИРОВАТЬ: Вот минимальный рабочий пример с использованием ответа 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 кружков на вершинах, которые должны быть окрашены в зеленый, желтый и красный цвета соответственно.
Возможно, это ошибка, не стесняйтесь открывать проблему с GitHub.
Тем не менее, приведенный выше код не отражает лучших практик использования боке, а именно: всегда делайте минимально возможное обновление. В данном случае это означает установку новых значений свойств для существующего преобразования цвета, а не замену существующего преобразования цвета.
Вот полный рабочий пример (созданный с помощью Bokeh 1.0.2), демонстрирующий обновление цветов глифа в ответ на изменение столбца данных:
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))
Вот оригинальный сюжет:
А вот график обновления после нажатия кнопки
Чтобы быть более ясным, после обратного вызова порядок величины может значительно измениться. Предположим, в вашем примере вы берете вместо обратного (z), exp (z). Тогда почти все круги будут красными, что может быть не очень информативным, поскольку (10 и 1000 будут красными, но совершенно разными). Мне нужно, чтобы цветовая полоса масштабировалась соответствующим образом, например, с шагом вместо 2-4 -... 10, 2-16, ... 100. Надеюсь, это более понятно.
Добавлена правка с измененным вашим рабочим примером, чтобы показать, что я имею в виду. Еще раз спасибо.
Хорошо, похоже, это ошибка, но есть способ, который работает, и в любом случае это лучшая практика. Я обновил ответ.
О, похоже, это работает. Спасибо, а также за совет по хорошей практике. Замена цветового преобразования действительно казалась неправильной. Я еще недостаточно хорошо знаю боке, и в данном случае я упустил свойства мапперов в целом.
Спасибо за ответ. Думаю, проблема в том, что масштаб на вашей цветовой карте не меняется. В этом случае у меня тоже нет проблем. Я имею в виду, что если моя шкала изначально составляет 10-100, а на следующем графике она становится 1-10, маркеры с количеством 10 будут окрашены, скажем, в синий, а не в красный цвет, поскольку согласно первой карте они были бы синими. . Постараюсь привести рабочий пример.