Plotly-Dash: How to show the same selected area of a figure between callbacks?

落花浮王杯 提交于 2021-01-01 10:59:36

问题


Consider a plotly figure where you can select polynomial features for a line fit using JupyterDash:

If you select an area and then choose another number for polynomial features, the figure goes from this:

... and back to this again:

So, how can you set things up so that the figure displays the same area of the figure every time you select another number of features and trigger another callback?

Complete code:

import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

from sklearn.preprocessing import PolynomialFeatures 
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline

from IPython.core.debugger import set_trace

# Load Data
df = px.data.tips()
# Build App
app = JupyterDash(__name__)
app.layout = html.Div([
    html.H1("ScikitLearn: Polynomial features"),
    dcc.Graph(id='graph'),
    html.Label([
        "Set number of features",
        dcc.Slider(id='PolyFeat',
    min=1,
    max=6,
    marks={i: '{}'.format(i) for i in range(10)},
    value=1,
) 
    ]),
])

# Define callback to update graph
@app.callback(
    Output('graph', 'figure'),
    [Input("PolyFeat", "value")]
)

def update_figure(nFeatures):
    
    global model

    # data
    df = px.data.tips()
    x=df['total_bill']
    y=df['tip']

    # model
    model = make_pipeline(PolynomialFeatures(nFeatures), LinearRegression())
    model.fit(np.array(x).reshape(-1, 1), y)
    x_reg = x.values
    y_reg = model.predict(x_reg.reshape(-1, 1))
    df['model']=y_reg

    # figure setup and trace for observations
    fig = go.Figure()
    fig.add_traces(go.Scatter(x=df['total_bill'], y=df['tip'], mode='markers', name = 'observations'))

    # trace for polynomial model
    df=df.sort_values(by=['model'])
    fig.add_traces(go.Scatter(x=df['total_bill'], y=df['model'], mode='lines', name = 'model'))
    
    # figure layout adjustments
    fig.update_layout(yaxis=dict(range=[0,12]))
    fig.update_layout(xaxis=dict(range=[0,60]))
    #print(df['model'].tail())
    fig.update_layout(template = 'plotly_dark')
    
    return(fig)

# Run app and display result inline in the notebook
app.enable_dev_tools(dev_tools_hot_reload =True)
app.run_server(mode='inline', port = 8040, dev_tools_ui=True, #debug=True,
              dev_tools_hot_reload =True, threaded=True)

回答1:


This is surprisingly easy and just adds to the power and flexibility of Plotly and Dash. Just add

fig.update_layout(uirevision='constant')

to your code and you're good to go:

For even more control you can set axis properties directly. From the documentation:

For finer control you can set these sub-attributes directly. For example, if your app separately controls the data on the x and y axes you might set xaxis.uirevision=*time* and yaxis.uirevision=*cost*. Then if only the y data is changed, you can update yaxis.uirevision=*quantity* and the y axis range will reset but the x axis range will retain any user-driven zoom.

Complete code:

import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

from sklearn.preprocessing import PolynomialFeatures 
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline

from IPython.core.debugger import set_trace

# Load Data
df = px.data.tips()
# Build App
app = JupyterDash(__name__)
app.layout = html.Div([
    html.H1("ScikitLearn: Polynomial features"),
    dcc.Graph(id='graph'),
    html.Label([
        "Set number of features",
        dcc.Slider(id='PolyFeat',
    min=1,
    max=6,
    marks={i: '{}'.format(i) for i in range(10)},
    value=1,
) 
    ]),
])

# Define callback to update graph
@app.callback(
    Output('graph', 'figure'),
    [Input("PolyFeat", "value")]
)

def update_figure(nFeatures):
    
    global model

    # data
    df = px.data.tips()
    x=df['total_bill']
    y=df['tip']

    # model
    model = make_pipeline(PolynomialFeatures(nFeatures), LinearRegression())
    model.fit(np.array(x).reshape(-1, 1), y)
    x_reg = x.values
    y_reg = model.predict(x_reg.reshape(-1, 1))
    df['model']=y_reg

    # figure setup and trace for observations
    fig = go.Figure()
    fig.add_traces(go.Scatter(x=df['total_bill'], y=df['tip'], mode='markers', name = 'observations'))

    # trace for polynomial model
    df=df.sort_values(by=['model'])
    fig.add_traces(go.Scatter(x=df['total_bill'], y=df['model'], mode='lines', name = 'model'))
    
    # figure layout adjustments
    fig.update_layout(yaxis=dict(range=[0,12]))
    fig.update_layout(xaxis=dict(range=[0,60]))
    #print(df['model'].tail())
    fig.update_layout(template = 'plotly_dark')
    fig.update_layout(uirevision='constant')
    return(fig)

# Run app and display result inline in the notebook
app.enable_dev_tools(dev_tools_hot_reload =True)
app.run_server(mode='inline', port = 8040, dev_tools_ui=True, #debug=True,
              dev_tools_hot_reload =True, threaded=True)


来源:https://stackoverflow.com/questions/63876187/plotly-dash-how-to-show-the-same-selected-area-of-a-figure-between-callbacks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!