#python #plotly #plotly-dash #plotly-python
#python #plotly #сюжетно-тире #plotly-python #plotly-dash
Вопрос:
Я хочу использовать plotly для отображения графика только после нажатия кнопки, но не уверен, как заставить это работать. Мой рисунок хранится в следующем коде
fig1 = go.Figure(data=plot_data, layout=plot_layout)
Затем я определяю макет своего приложения с помощью следующего бита кода:
app.layout = html.Div([
#button
html.Div(className='submit', children=[
html.Button('Forecast', id='submit', n_clicks=0)
]),
#loading
dcc.Loading(
id="loading-1",
type="default",
children=html.Div(id="loading-output-1")
),
#graph
dcc.Graph(id= 'mpg-scatter',figure=fig),
#hoverdata
html.Div([
dcc.Markdown(id='hoverdata-text')
],style={'width':'50%','display':'inline-block'})
])
@app.callback(Output('hoverdata-text','children'),
[Input('mpg-scatter','hoverData')])
def callback_stats(hoverData):
return str(hoverData)
if __name__ == '__main__':
app.run_server()
Но проблема в том, что я хочу, чтобы сначала отображалась только кнопка. Затем, когда кто-то нажимает на кнопку прогноза, появляется функция загрузки, а через секунду отображается график. Я определил компонент dcc.loading, но не уверен, как определить обратный вызов для этой функции.
Ответ №1:
ПРЕДЛОЖЕНИЕ 3 — dcc.Store()
и dcc.Loading
В этом предложении используется компонент dcc.Store(), html.Кнопка() и компонент dcc.Loading для создания того, что, как я теперь понимаю, является желаемой настройкой:
- Запустите приложение, которое показывает только кнопку.
- Нажмите кнопку, чтобы отобразить значок загрузки, а затем
- отобразить фигуру.
- Нажмите еще раз, чтобы отобразить следующий рисунок в последовательности из трех рисунков.
- Начните снова, когда последовательность фигур будет исчерпана.
После запуска приложение будет выглядеть следующим образом:
Теперь вы можете нажать Figures
один раз, чтобы попасть Figure 1
ниже, но только после просмотра одного из следующих значков загрузки: ['graph', 'cube', 'circle', 'dot', or 'default']
один из которых 'dot'
вызовет посттравматическое расстройство и 'cube'
является моим любимым:
Загрузка…
Рисунок 1
Теперь вы не можете продолжать нажимать для Figure 2
и Figure 3
. Я установил время загрузки на Figure 1
не менее 5 секунд, а затем 2 секунды для Figure 2
и Figure 3
. Но вы можете легко это изменить.
Когда вы нажмете более трех раз, мы снова начнем с начала:
Надеюсь, я наконец нашел решение для того, что вы на самом деле искали. Настройка в приведенном ниже фрагменте кода основана на настройке, описанной здесь, но, надеюсь, была скорректирована в соответствии с вашими потребностями. Дайте мне знать, как это работает для вас!
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_table
from dash.exceptions import PreventUpdate
import dash_bootstrap_components as dbc
import time
time.sleep(5) # Delay for 5 seconds.
global_df = pd.DataFrame({'value1':[1,2,3,4],
'value2':[10,11,12,14]})
# app = JupyterDash(__name__)
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
df = pd.DataFrame({'Value 1': [1,2,3],
'Value 2':[10,11,12],
'Value 3':[14,12,9]})
df.set_index('Value 1', inplace = True)
app.layout = html.Div([
# The memory store reverts to the default on every page refresh
dcc.Store(id='memory'),
# The local store will take the initial data
# only the first time the page is loaded
# and keep it until it is cleared.
# Same as the local store but will lose the data
# when the browser/tab closes.
html.Table([
html.Thead([
html.Tr(html.Th('Click to launch figure:')),
html.Tr([
html.Th(html.Button('Figures', id='memory-button')),
]),
]),
]),
dcc.Loading(id = "loading-icon",
#'graph', 'cube', 'circle', 'dot', or 'default'
type = 'cube',
children=[html.Div(dcc.Graph(id='click_graph'))])
])
# Create two callbacks for every store.
# add a click to the appropriate store.
@app.callback(Output('memory', 'data'),
[Input('memory-button', 'n_clicks')],
[State('memory', 'data')])
def on_click(n_clicks, data):
if n_clicks is None:
# prevent the None callbacks is important with the store component.
# you don't want to update the store for nothing.
raise PreventUpdate
# Give a default data dict with 0 clicks if there's no data.
data = data or {'clicks': 0}
data['clicks'] = data['clicks'] 1
if data['clicks'] > 3: data['clicks'] = 0
return data
# output the stored clicks in the table cell.
@app.callback(Output('click_graph', 'figure'),
# Since we use the data prop in an output,
# we cannot get the initial data on load with the data prop.
# To counter this, you can use the modified_timestamp
# as Input and the data as State.
# This limitation is due to the initial None callbacks
# https://github.com/plotly/dash-renderer/pull/81
[Input('memory', 'modified_timestamp')],
[State('memory', 'data')])
def on_data(ts, data):
if ts is None:
#raise PreventUpdate
fig = go.Figure()
fig.update_layout(plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
yaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')),
xaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')))
return(fig)
data = data or {}
0
# plotly
y = 'Value 2'
y2 = 'Value 3'
fig = go.Figure()
fig.update_layout(plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
yaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')),
xaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')))
if data.get('clicks', 0) == 1:
fig = go.Figure(go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines'))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='plotly_dark',
title = 'Plot number ' str(data.get('clicks', 0)))
# delay only after first click
time.sleep(2)
if data.get('clicks', 0) == 2:
fig = go.Figure((go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines')))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='seaborn',
title = 'Plot number ' str(data.get('clicks', 0)))
if data.get('clicks', 0) == 3:
fig = go.Figure((go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines')))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='plotly_white',
title = 'Plot number ' str(data.get('clicks', 0)))
# Aesthetics
fig.update_layout(margin= {'t':30, 'b':0, 'r': 50, 'l': 50, 'pad': 0},
hovermode = 'x',
legend=dict(x=1,y=0.85),
uirevision='constant')
# delay for every figure
time.sleep(2)
return fig
app.run_server(mode='external', port = 8070, dev_tools_ui=True,
dev_tools_hot_reload =True, threaded=True)
ПРЕДЛОЖЕНИЕ 2
После небольшого общения мы теперь знаем, что вы хотели бы:
- сначала отображать только кнопку (вопрос)
- при однократном нажатии кнопки внизу отображается рис. 1, при 2-м нажатии отображается рис. 2, а при 3-м нажатии отображается рис. 3 (комментарий)
Я создал новую настройку, которая должна соответствовать всем вышеперечисленным критериям. Сначала отображаются только параметры управления. И затем вы можете выбрать, какой рисунок отображать: Fig1, Fig2 or Fig3
. Мне кажется, что это неоптимальный пользовательский интерфейс, если вам нужно циклически просматривать свои рисунки, чтобы выбрать, какой из них вы хотели бы отобразить. Итак, я выбрал переключатели, подобные этому:
Теперь вы можете свободно выбрать свою фигуру для отображения или вернуться к тому, чтобы снова ничего не показывать, вот так:
Отображение при запуске или при None
выборе:
Figure 1
выбрано
Вы все еще не предоставили образец данных, поэтому я все еще использую свои синтетические данные из Suggestion 1
и скорее позволяю различным макетам указывать, какой рисунок показан. Я надеюсь, что это соответствует вашим потребностям, поскольку казалось, что вы хотели бы иметь разные макеты для разных фигур.
Завершите код 2
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
pd.options.plotting.backend = "plotly"
from datetime import datetime
palette = px.colors.qualitative.Plotly
# sample data
df = pd.DataFrame({'Prices': [1,10,7,5, np.nan, np.nan, np.nan],
'Predicted_prices':[np.nan, np.nan, np.nan, 5, 8,6,9]})
# app setup
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
# controls
controls = dbc.Card(
[dbc.FormGroup(
[
dbc.Label("Options"),
dcc.RadioItems(id="display_figure",
options=[ {'label': 'None', 'value': 'Nope'},
{'label': 'Figure 1', 'value': 'Figure1'},
{'label': 'Figure 2', 'value': 'Figure2'},
{'label': 'Figure 3', 'value': 'Figure3'}
],
value='Nope',
labelStyle={'display': 'inline-block', 'width': '10em', 'line-height':'0.5em'}
)
],
),
dbc.FormGroup(
[dbc.Label(""),]
),
],
body=True,
style = {'font-size': 'large'})
app.layout = dbc.Container(
[
html.H1("Button for predictions"),
html.Hr(),
dbc.Row([
dbc.Col([controls],xs = 4),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id="predictions")),
])
]),
]),
html.Br(),
dbc.Row([
]),
],
fluid=True,
)
@app.callback(
Output("predictions", "figure"),
[Input("display_figure", "value"),
],
)
def make_graph(display_figure):
# main trace
y = 'Prices'
y2 = 'Predicted_prices'
# print(display_figure)
if 'Nope' in display_figure:
fig = go.Figure()
fig.update_layout(plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
yaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')),
xaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')))
return fig
if 'Figure1' in display_figure:
fig = go.Figure(go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines'))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='plotly_dark')
# prediction trace
if 'Figure2' in display_figure:
fig = go.Figure((go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines')))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='seaborn')
if 'Figure3' in display_figure:
fig = go.Figure((go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines')))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='plotly_white')
# Aesthetics
fig.update_layout(margin= {'t':30, 'b':0, 'r': 0, 'l': 0, 'pad': 0})
fig.update_layout(hovermode = 'x')
fig.update_layout(showlegend=True, legend=dict(x=1,y=0.85))
fig.update_layout(uirevision='constant')
fig.update_layout(title = "Prices and predictions")
return(fig)
app.run_server(mode='external', port = 8005)
ПРЕДЛОЖЕНИЕ 1
Это предложение будет сосредоточено непосредственно на:
Я хочу использовать plotly для отображения графика только после нажатия кнопки
Это означает, что я не предполагаю, что это dcc.Loading()
должно быть частью ответа.
Я считаю, что dcc.Checklist()
это чрезвычайно универсальный и удобный компонент. И при правильной настройке он будет отображаться как кнопка, на которую нужно нажать (или опция, которая должна быть отмечена), чтобы запустить определенные функции или визуализации.
Вот базовая настройка:
dcc.Checklist(
id="display_columns",
options=[{"label": col ' ', "value": col} for col in df.columns],
value=[df.columns[0]],
labelStyle={'display': 'inline-block', 'width': '12em', 'line-height':'0.5em'}
И вот как это будет выглядеть:
Наряду с, среди прочего, следующими несколькими строками, dcc.Checklist()
компонент позволит вам включать и выключать Prediction
трассировку по своему усмотрению.
# main trace
y = 'Prices'
fig = make_subplots(specs=[[{"secondary_y": True}]])
if 'Prices' in display_columns:
fig.add_trace(go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines'), secondary_y=False)
# prediction trace
if 'Predicted_prices' in display_columns:
fig.add_trace(go.Scatter(name = 'predictions', x=df.index, y=df['Predicted_prices'], mode = 'lines'), secondary_y=False
В дополнение к этому, эта настройка легко позволит вам обрабатывать несколько прогнозов для нескольких трасс, если вы захотите расширить этот пример дальше. Попробуйте и дайте мне знать, как это работает у вас. А если что-то непонятно, то мы можем углубиться в детали, когда вы найдете время.
Вот как будет выглядеть приложение с активированными прогнозами и без них:
ВЫКЛ
НА
Полный код:
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
pd.options.plotting.backend = "plotly"
from datetime import datetime
palette = px.colors.qualitative.Plotly
# sample data
df = pd.DataFrame({'Prices': [1,10,7,5, np.nan, np.nan, np.nan],
'Predicted_prices':[np.nan, np.nan, np.nan, 5, 8,6,9]})
# app setup
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
# input controls
controls = dbc.Card(
[dbc.FormGroup(
[
dbc.Label("Options"),
dcc.Checklist(
id="display_columns",
options=[{"label": col ' ', "value": col} for col in df.columns],
value=[df.columns[0]],
labelStyle={'display': 'inline-block', 'width': '12em', 'line-height':'0.5em'}
#clearable=False,
#multi = True
),
],
),
dbc.FormGroup(
[dbc.Label(""),]
),
],
body=True,
style = {'font-size': 'large'})
app.layout = dbc.Container(
[
html.H1("Button for predictions"),
html.Hr(),
dbc.Row([
dbc.Col([controls],xs = 4),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id="predictions")),
])
]),
]),
html.Br(),
dbc.Row([
]),
],
fluid=True,
)
@app.callback(
Output("predictions", "figure"),
[Input("display_columns", "value"),
],
)
def make_graph(display_columns):
# main trace
y = 'Prices'
fig = make_subplots(specs=[[{"secondary_y": True}]])
if 'Prices' in display_columns:
fig.add_trace(go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines'), secondary_y=False)
# prediction trace
if 'Predicted_prices' in display_columns:
fig.add_trace(go.Scatter(name = 'predictions', x=df.index, y=df['Predicted_prices'], mode = 'lines'), secondary_y=False)
# Aesthetics
fig.update_layout(margin= {'t':30, 'b':0, 'r': 0, 'l': 0, 'pad': 0})
fig.update_layout(hovermode = 'x')
fig.update_layout(showlegend=True, legend=dict(x=1,y=0.85))
fig.update_layout(uirevision='constant')
fig.update_layout(template='plotly_dark',
plot_bgcolor='#272B30',
paper_bgcolor='#272B30')
fig.update_layout(title = "Prices and predictions")
return(fig)
app.run_server(mode='external', port = 8005)
Комментарии:
1. Привет @vestland, у меня похожая проблема, и я опубликовал выше подробности. Пожалуйста, дайте мне знать, как отображать статические изображения (уже сгенерированные) в зависимости от нажатой кнопки. Спасибо