Я хочу отображать данные на тепловой карте в Plotly и включать ползунок для переключения между различными категориями определенного столбца ползунка.
В настоящее время я могу обновить текст в hovertemplate
, но я также хотел бы включить в поле дополнительную информацию, которая также должна обновляться при каждом изменении ползунка.
В качестве примера,
# create Pandas dataframe
df = pd.DataFrame({'x_label': [1,2,3,1,2,3,1,2,3], 'y_label': [3,4,5,3,4,5,3,4,5], 'z_label':[6,7,8,9,10,11,12,13,14],
'A': [10, 11, 12,13,14,15,16,17,18], 'B': [10, 12, 14,16,18,20,22,24,26], 'C': [12, 14, 16,18,20,22,24,26,28],
'slider':['a','a','a','b','b','b','c','c','c']})
# create list of dataframes where each dataframe is a filtered dataframe based on the selected slider category
multi_dfs = [df[df['slider'] == s] for s in df['slider'].unique()]
# create and name each frame in the heatmap based on the slider name
frames = [
go.Frame(data=go.Heatmap(z=df['z_label'],
x=df['x_label'],
y=df['y_label']),
name=df['slider'].iloc[0],
)
for i, df in enumerate(multi_dfs)
]
# plot the heatmap figure
fig = go.Figure(data=frames[0].data, frames=frames).update_layout(
# iterate over frames to generate slider steps
sliders=[{"active":1, "currentvalue":{"prefix": "slider: "},
"steps": [{"args": [[f.name],{"frame": {"duration": 0, "redraw": True},
"mode": "immediate",},],
"label": f.name, "method": "animate",}
for f in frames],}]
)
# update hovertemplate labels and information
fig.update_traces(
hovertemplate = "X Custom Label: %{x}"
"<br>Y Custom Label: %{y}"
"<br>Z Custom Label: %{z}<extra></extra>"
)
Я получаю следующую тепловую карту
Я хотел бы добавить в информационное окно при наведении данные, которые находятся в столбцах A
, B
и C
.
Я не уверен, как передавать и обновлять эти параметры, когда на графике есть ползунок.
я не уверен, возможно ли это только с сюжетом, но, поскольку вы указали plotly-dash
в качестве тега, не могли бы вы рассмотреть решение сюжетно-тире?
Я бы хотел, моя реализация на самом деле в тире, но я также не был уверен, как это сделать с тире
@finstats, значит, вы хотите изменить ползунок, чтобы добавить дополнительную информацию в hovertemplate? какая информация конкретно?
Я хочу, чтобы информация столбцов A, B и C также была включена в шаблон наведения, и при изменении ползунка эта информация должна обновляться для всех ячеек, поскольку она обновляется для пользовательской метки X, пользовательской метки Y и Пользовательская этикетка Z.
Основная проблема заключается в том, что когда вы передаете информацию о ползунке в аргумент sliders
в методе fig.update_layout
, нет простого способа получить значение ползунка, когда он используется в реальном времени. Это означает, что даже с обратным вызовом, в котором мы передаем фигуру в качестве входных данных, у нас нет простого способа обновить hovertemplate
, потому что состояние ползунка неизвестно, когда вы его используете.
Мы можем обойти это, используя вместо этого использование dcc.Slider
для рендеринга ползунка — это будет отдельно от рисунка, поэтому мы не столкнемся с проблемой получения значения ползунка из графического рисунка.
Однако из документации я считаю, что dcc.Slider допускает только числовые значения, поэтому, когда мы определяем ползунок, нам нужно предоставить числовые значения, такие как 0, 1, 2,...
, и создать словарь, чтобы сопоставить их с вашими категориями 'a', 'b', 'c',...,
. Затем, когда мы используем обратный вызов для получения значений ползунка, мы используем тот же словарь, чтобы знать, какая категория на ползунке была выбрана (аналогично примеру здесь). Затем в обратном вызове мы можем соответственно обновить как данные на рисунке, так и hovertemplate.
Чтобы получить несколько столбцов данных в hovertemplate, вам нужно передать несколько столбцов вашего df в параметр customdata, как описано в этом ответе.
Я знаю, что это выходит за рамки вашего вопроса, но я заметил, что масштаб тепловой карты меняется каждый раз, когда вы переключаете ползунок, потому что меняются входные данные — независимо от того, охватывают ли данные в тепловой карте значения от 10-12 или 20-22, цвета останутся неизменными. Поэтому я добавил аргументы zmin
и zmax
к go.Heatmap
на основе глобальных минимальных и максимальных значений в ваших данных, но если это не то, что вам нужно, не стесняйтесь отбрасывать эти аргументы.
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import dash
from dash import Input, Output, State, ctx, dcc, html
from dash.exceptions import PreventUpdate
# create Pandas dataframe
df = pd.DataFrame({'x_label': [1,2,3,1,2,3,1,2,3], 'y_label': [3,4,5,3,4,5,3,4,5], 'z_label':[6,7,8,9,10,11,12,13,14],
'A': [10, 11, 12,13,14,15,16,17,18], 'B': [10, 12, 14,16,18,20,22,24,26], 'C': [12, 14, 16,18,20,22,24,26,28],
'slider':['a','a','a','b','b','b','c','c','c']})
# create dictionary mapping slider category to dataframe filtered by slider category
slider_df_dict = {
slider_value:df_group for
slider_value, df_group in df.groupby("slider")
}
zmin, zmax = min(df['z_label']), max(df['z_label'])
customdata = np.stack((df['A'], df['B'], df['C']), axis=-1)
slider_values = list(slider_df_dict.keys())
# create and name each frame in the heatmap based on the slider name
frames = [
go.Frame(data=go.Heatmap(
z=df['z_label'],
x=df['x_label'],
y=df['y_label'],
customdata=customdata,
zmin=zmin,
zmax=zmax
),
name=slider_name,
)
for slider_name, df in slider_df_dict.items()
]
# plot the heatmap figure
fig = go.Figure(data=frames[0].data, frames=frames)
# update hovertemplate labels and information
fig.update_traces(
hovertemplate = "X Custom Label: %{x}"
"<br>Y Custom Label: %{y}"
"<br>Z Custom Label: %{z}"
"<br>Col A: %{customdata[0]}"
"<br>Col B: %{customdata[1]}"
"<br>Col C: %{customdata[2]}"
"<extra></extra>"
)
app = dash.Dash()
## if i am not mistaken, categorical dash slider has not been implemented yet
## so we still need to pass numbers and then display the marks as categories
slider_value_to_mark_dict = {i:s for i,s in enumerate(slider_values)}
app.layout = html.Div([
dcc.Graph(figure=fig, id = "fig-heatmap"),
dcc.Slider(
min=0,
max=len(slider_values)-1,
step=1,
marks=slider_value_to_mark_dict, id = "slider")
])
@app.callback(
Output("fig-heatmap", "figure"),
Input("slider", "value"),
prevent_initial_call=True,
)
def update_fig(slider_value):
slider_mark = slider_value_to_mark_dict[slider_value]
slider_df = slider_df_dict[slider_mark]
customdata = np.stack((slider_df['A'], slider_df['B'], slider_df['C']), axis=-1)
frames = [
go.Frame(
data=go.Heatmap(
z=slider_df['z_label'],
x=slider_df['x_label'],
y=slider_df['y_label'],
customdata=customdata,
zmin=zmin,
zmax=zmax
),
name=slider_mark,
)
]
fig = go.Figure(data=frames[0].data, frames=frames)
fig.update_traces(
hovertemplate = "X Custom Label: %{x}"
"<br>Y Custom Label: %{y}"
"<br>Z Custom Label: %{z}"
"<br>Col A: %{customdata[0]}"
"<br>Col B: %{customdata[1]}"
"<br>Col C: %{customdata[2]}"
"<extra></extra>"
)
return fig
app.run_server(debug=True, use_reloader=False)
Не за что, рад слышать, что мой ответ оказался полезным!
Мне было бы трудно обновлять значения ползунка, поскольку шаблон - это всего лишь шаблон.