Python 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')

Кажется, это работает, как ожидалось. Ниже показан сюжет, который я получаю при запуске сервера боке.

Python bokeh: обновлять цвета точечной диаграммы при обратном вызове

Затем у меня есть функция обратного вызова Обновить(), запускаемая при изменении значения в некотором виджете (выбор или средство выбора времени).

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, как на карте первого графика перед обратным вызовом).

Python bokeh: обновлять цвета точечной диаграммы при обратном вызове

Должен ли я просто добавить во фрейм данных столбец «цвет»? Я считаю, что есть более простой / удобный способ сделать это.

РЕДАКТИРОВАТЬ: Вот минимальный рабочий пример с использованием ответа 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 кружков на вершинах, которые должны быть окрашены в зеленый, желтый и красный цвета соответственно.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
2 408
1

Ответы 1

Возможно, это ошибка, не стесняйтесь открывать проблему с 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))

Вот оригинальный сюжет:

А вот график обновления после нажатия кнопки

Спасибо за ответ. Думаю, проблема в том, что масштаб на вашей цветовой карте не меняется. В этом случае у меня тоже нет проблем. Я имею в виду, что если моя шкала изначально составляет 10-100, а на следующем графике она становится 1-10, маркеры с количеством 10 будут окрашены, скажем, в синий, а не в красный цвет, поскольку согласно первой карте они были бы синими. . Постараюсь привести рабочий пример.

Tommy 18.12.2018 04:23

Чтобы быть более ясным, после обратного вызова порядок величины может значительно измениться. Предположим, в вашем примере вы берете вместо обратного (z), exp (z). Тогда почти все круги будут красными, что может быть не очень информативным, поскольку (10 и 1000 будут красными, но совершенно разными). Мне нужно, чтобы цветовая полоса масштабировалась соответствующим образом, например, с шагом вместо 2-4 -... 10, 2-16, ... 100. Надеюсь, это более понятно.

Tommy 18.12.2018 04:30

Добавлена ​​правка с измененным вашим рабочим примером, чтобы показать, что я имею в виду. Еще раз спасибо.

Tommy 18.12.2018 04:45

Хорошо, похоже, это ошибка, но есть способ, который работает, и в любом случае это лучшая практика. Я обновил ответ.

bigreddot 18.12.2018 05:35

О, похоже, это работает. Спасибо, а также за совет по хорошей практике. Замена цветового преобразования действительно казалась неправильной. Я еще недостаточно хорошо знаю боке, и в данном случае я упустил свойства мапперов в целом.

Tommy 18.12.2018 06:05

Другие вопросы по теме