Plotly Python — Heatmap — включение и обновление дополнительных параметров метки в Hovertext при использовании ползунка

Я хочу отображать данные на тепловой карте в 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.

Я не уверен, как передавать и обновлять эти параметры, когда на графике есть ползунок.

Мне было бы трудно обновлять значения ползунка, поскольку шаблон - это всего лишь шаблон.

r-beginners 24.12.2022 03:34

я не уверен, возможно ли это только с сюжетом, но, поскольку вы указали plotly-dash в качестве тега, не могли бы вы рассмотреть решение сюжетно-тире?

Derek O 24.12.2022 06:49

Я бы хотел, моя реализация на самом деле в тире, но я также не был уверен, как это сделать с тире

finstats 24.12.2022 06:59

@finstats, значит, вы хотите изменить ползунок, чтобы добавить дополнительную информацию в hovertemplate? какая информация конкретно?

Derek O 24.12.2022 07:44

Я хочу, чтобы информация столбцов A, B и C также была включена в шаблон наведения, и при изменении ползунка эта информация должна обновляться для всех ячеек, поскольку она обновляется для пользовательской метки X, пользовательской метки Y и Пользовательская этикетка Z.

finstats 24.12.2022 14:32
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
5
132
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Основная проблема заключается в том, что когда вы передаете информацию о ползунке в аргумент 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)

Не за что, рад слышать, что мой ответ оказался полезным!

Derek O 27.12.2022 17:27

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