MartyMcFly
MartyMcFly

Reputation: 63

Dynamic & changing X and Y titles on plotly graph

I am trying to make a plotly scatter plot with a OLS trendline and provide a dropdown menu what let's the user pick from a different of X's and Y's. The plot almost works. The code below updates the data but does not update the X and Y axes titles or the labels for the data. So when you hover over a given point, it always show the X variables as CRTTOTAL and Y variable as AVG_MISPERCEPTION.

The code:


    import plotly.graph_objects as go
    import plotly.express as px
    #making a figure
    fig = go.Figure()
    x1 = df['crttotal']
    x2 = df['nfcc_mean']
    x3 = df['bficonmean']
    x4 = df['bfiopmean']
    
    y1 = df['avg_misperception_score']
    y2 = df['avg_ambiguous_score']
    
    
    
    # make figure
    fig = px.scatter(df, x=x1, y=y1, trendline="ols", trendline_scope="overall", labels = {"crttotal":"crttotal", "avg_misperception_score":"avg_misperception_score"})
    
    
    #making the dropdown
    fig.update_layout(
        updatemenus=[
            go.layout.Updatemenu(
                type="dropdown",
                buttons=list([
                    
                    dict(label="CRT vs Misperception",
                         method="update",
                         args=[{"x": [x1, px.scatter(x=x1, y=y1, trendline='ols').data[1].x], 
                                "y": [y1, px.scatter(x=x1, y=y1, trendline='ols').data[1].y], 
                                "trendline":["ols"], 
                                "trendline_scope":["overall"]},
                               {"title": "CRT vs Misperception"}]),
                    
                    dict(label="NFCC vs Misperception",
                         method="update",
                         args=[{"x": [x2, px.scatter(x=x2, y=y1, trendline='ols').data[1].x], 
                                "y": [y1, px.scatter(x=x2, y=y1, trendline='ols').data[1].y], 
                                "trendline":["ols"], 
                                "trendline_scope":["overall"]},
                               {"title": "NFCC vs Misperception"}]),
                    
                    dict(label="bficonmean vs Misperception",
                         method="update",
                         args=[{"x": [x3, px.scatter(x=x3, y=y1, trendline='ols').data[1].x], 
                                "y": [y1, px.scatter(x=x3, y=y1, trendline='ols').data[1].y], 
                                "trendline":["ols"], "trendline_scope":["overall"]},
                               {"title": "bficonmean vs Misperception"}]),
                    
                    dict(label="bfiopmean vs Misperception",
                         method="update",
                         args=[{"x": [x4, px.scatter(x=x4, y=y1, trendline='ols').data[1].x], 
                                "y": [y1, px.scatter(x=x4, y=y1, trendline='ols').data[1].y],
                                "trendline":["ols"], 
                                "trendline_scope":["overall"]},
                               {"title": "bfiopmean vs Misperception"}]),
    
                    ### changing the y variable also
                    dict(label="CRT vs Ambiguity",
                         method="update",
                         args=[{"x": [x1, px.scatter(x=x1, y=y2, trendline='ols').data[1].x], 
                                "y": [y2, px.scatter(x=x1, y=y2, trendline='ols').data[1].y], 
                                "trendline":["ols"],
                                "trendline_scope":["overall"]},
                               {"title": "CRT vs Ambiguity"}]),
                    
                    dict(label="NFCC vs Ambiguity",
                         method="update",
                         args=[{"x": [x2, px.scatter(x=x2, y=y2, trendline='ols').data[1].x],
                                "y": [y2, px.scatter(x=x2, y=y2, trendline='ols').data[1].y], 
                                "trendline":["ols"],
                                "trendline_scope":["overall"]},
                               {"title": "NFCC vs Ambiguity"}]),
                    
                    dict(label="bficonmean vs Ambiguity",
                         method="update",
                         args=[{"x": [x3, px.scatter(x=x3, y=y2, trendline='ols').data[1].x], 
                                "y": [y2, px.scatter(x=x3, y=y2, trendline='ols').data[1].y], 
                                "trendline":["ols"], 
                                "trendline_scope":["overall"]},
                               {"title": "bficonmean vs Ambiguity"}]),
                    
                    dict(label="bfiopmean vs Ambiguity",
                         method="update",
                         args=[{"x": [x4, px.scatter(x=x4, y=y2, trendline='ols').data[1].x], 
                                "y": [y2, px.scatter(x=x4, y=y2, trendline='ols').data[1].y],
                                "trendline":["ols"], 
                                "trendline_scope":["overall"]},
                               {"title": "bfiopmean vs Ambiguity"}])
    
                ])
            )
        ]
    )
    
    #set the title
    fig.update_layout(title="Dropdown")
    
    fig.show()

The Data


       crttotal  nfcc_mean  bficonmean  bfiopmean  avg_misperception_score  \
    0         3       2.87       3.875      3.000                   -0.062   
    1         0       3.53       3.625      3.125                   -0.235   
    2         0       3.80       4.000      3.000                    0.077   
    3         0       3.73       3.750      3.500                    0.067   
    4         2       3.87       3.125      3.000                    0.368   
    5         0       3.47       2.750      3.500                   -0.200   
    6         0       4.33       3.625      3.625                   -0.200   
    7         0       4.13       3.250      3.125                   -0.500   
    8         0       4.73       3.250      3.250                   -0.643   
    9         3       5.20       3.750      2.750                    0.000   
    
       avg_ambiguous_score  
    0                 2.60  
    1                 2.10  
    2                 3.35  
    3                 2.55  
    4                 2.90  
    5                 2.80  
    6                 2.85  
    7                 3.30  
    8                 3.15  
    9                 2.70  

** What I've tried** Adding arguments to the buttons so that it updates the px.scatter funtion:


    fig.update_layout(
        updatemenus=[
            go.layout.Updatemenu(
                type="dropdown",
                buttons=list([
                    
                    dict(label="CRT vs Misperception", # button label
                         method="update",
                         args=[{"x": [x1, px.scatter(x=x1, y=y1, trendline='ols').data[1].x], 
                                "y": [y1, px.scatter(x=x1, y=y1, trendline='ols').data[1].y], 
                                "trendline":["ols"], 
                                "trendline_scope":["overall"],
                            **  "labels":{"crttotal":"CRT",
                            **  "avg_misperception_score":"Misperception"}},
                               {"title": "CRT vs Misperception"}]),
                    
                    dict(label="NFCC vs Misperception",
                         method="update",
                         args=[{"x": [x2, px.scatter(x=x2, y=y1, trendline='ols').data[1].x], 
                                "y": [y1, px.scatter(x=x2, y=y1, trendline='ols').data[1].y], 
                                "trendline":["ols"], 
                                "trendline_scope":["overall"],
                                },
                               {"title": "NFCC vs Misperception",
                               ... ETC. 

Upvotes: 1

Views: 1031

Answers (2)

vestland
vestland

Reputation: 61084

I'm regarding this as a follow-up question to How to add a OLS trendline to a plotly scatter plot graph object that uses updatemenus to display subsets of data?. What you were aiming to achieve there turned out to be pretty much impossible if you were to accomplish all the details - such as a correct hovertemplate for each subplot. Getting dynamic and changing X and Y titles would be very much doable though. But my following suggestion, either you're using JupyterDash or Dash, is much easier with the setup in the code below. No updatemenus with complicated args or anything like that, just simply:

fig = px.scatter(df, x=x, y=y, trendline="ols",
                 trendline_scope="overall")

...where values for x and y are determined through dropdowns using dcc.Dropdown in a callback function like this:

@ app.callback(Output('fig1', 'figure'),
               [Input('y_val', 'value'),
               Input('x_val', 'value')])
def trends(y, x):

    fig = px.scatter(df, x=x, y=y, trendline="ols",
                     trendline_scope="overall")
    fig.update_layout(paper_bgcolor='rgba(0,0,0,0)',
                      plot_bgcolor='rgba(0,0,0,0)')
    return fig

Looking at the complete code snippet below, you'll see that there's a bunch of other stuff going on, like defining the structure and layout of the Dash app, but if you take the time to read just a little but more about Dash, you'll quickly find that this is the way to go. Not just with this example, but for all other analytics or data science challenges that should come your way. That's a bold statement on behalf of the Plotly team, but I'll stand by it!

Anyway, here's a gif of the resulting app:

enter image description here

And here's the complete code for a JupyterLab version. If you decide to go with pure Dash, just make the few edits described in Plotly: How to rewrite a standard dash app to launch it in JupyterLab?

from jupyter_dash import JupyterDash
from dash import Dash, html, dcc, Input, Output
import dash_bootstrap_components as dbc

import plotly.graph_objects as go
import plotly.express as px
import numpy as np
import pandas as pd


# data

df = pd.DataFrame({'crttotal': np.random.random(8),
                   'nfcc_mean': np.random.random(8),
                   'bficonmean': np.random.random(8),
                   'bfiopmean': np.random.random(8),
                   'avg_misperception_score': np.random.random(8),
                   'avg_ambiguous_score': np.random.random(8)})

# app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app = JupyterDash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

y_variables = ['avg_misperception_score', 'avg_ambiguous_score']
x_variables = ['crttotal', 'nfcc_mean', 'bficonmean', 'bfiopmean']
# dd_options = options.remove(target_variable)


app.layout = dbc.Container(
    [
        dbc.Row(
            [
                dbc.Col(
                    [
                        dcc.Markdown(
                            "#### Plotly Express line chart with trendlines and dropdown",
                            className="text-white",

                        )
                    ],  # style={"textalign": "center"},
                    width=8
                )
            ],
            className="bg-secondary rounded-3 mt-2",
            # style={"textalign": "center"},

            justify="center"

        ),
        dbc.Row(

            [

                dbc.Col([dbc.Label(
                    "Target variable:",
                    # className="bg-info bg-opacity-50 mt-2 p-2",
                    className="mt-2 p-2",
                    style={"width": "100%"},
                ),
                    dcc.Dropdown(y_variables,
                                 y_variables[0], id='y_val'),
                    dbc.Label(
                    "Explanatory variable:",
                    # className="bg-info bg-opacity-50 mt-2 p-2",
                    className="mt-2 p-2",
                    style={"width": "100%"},
                ),
                    dcc.Dropdown(x_variables,
                                 x_variables[0], id='x_val'),

                ], width=4),
                dbc.Col(
                    [

                        dcc.Graph(id='fig1')
                    ]
                )
            ],
            className="bg-secondary bg-opacity-25 rounded-3 p-2 mt-2",
        ),
    ]
)


@ app.callback(Output('fig1', 'figure'),
               [Input('y_val', 'value'),
               Input('x_val', 'value')])
def trends(y, x):

    fig = px.scatter(df, x=x, y=y, trendline="ols",
                     trendline_scope="overall")
    fig.update_layout(paper_bgcolor='rgba(0,0,0,0)',
                      plot_bgcolor='rgba(0,0,0,0)')
    return fig


# app.run_server(mode='inline', port=8070, dev_tools_ui=True, use_reloader = False,
#                dev_tools_hot_reload=False, threaded=    True)

app.run_server(debug=True, use_reloader=False, mode='inline')

Upvotes: 1

hoa tran
hoa tran

Reputation: 1679

If you use dash, you can refer below code:

from dash import html
import dash_bootstrap_components as dbc
from dash import dcc
from dash import html,callback
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd
import dash

df = pd.read_csv('Data_test.csv')
x1 = df['crttotal']
x2 = df['nfcc_mean']
x3 = df['bficonmean']
x4 = df['bfiopmean']
    
y1 = df['avg_misperception_score']
y2 = df['avg_ambiguous_score']

app = dash.Dash(__name__)

app.layout = html.Div([
    dbc.Row([
        dbc.Col([
            html.H5('Dropdown',className='text-center'),
            dcc.Dropdown(id='selection',placeholder="Please select dropdown",
                         options=[{'label':'CRT vs Misperception','value':'CRT vs Misperception'},
                                  {'label':'NFCC vs Misperception','value':'NFCC vs Misperception'},
                                  {'label':'bficonmean vs Misperception','value':'bficonmean vs Misperception'},
                                  {'label':'bfiopmean vs Misperception','value':'bfiopmean vs Misperception'},
                                  {'label':'CRT vs Ambiguity','value':'CRT vs Ambiguity'},
                                  {'label':'NFCC vs Ambiguity','value':'NFCC vs Ambiguity'},
                                  {'label':'bficonmean vs Ambiguity','value':'bficonmean vs Ambiguity'},
                                  {'label':'bfiopmean vs Ambiguity','value':'bfiopmean vs Ambiguity'}],
                         value='CRT vs Misperception',
                         multi=False,
                         disabled=False,
                         clearable=False,
                         searchable=True),
        ],width={'size':12,"offset":0,'order':1}),
    ], className='p-2 align-items-stretch'),
    
    dbc.Row([
        dbc.Col([
            dcc.Graph(id="graph",figure={},style={'height':450})
        ],width={'size':12,'offset':0,'order':2}),
    ], className='p-2 align-items-stretch'),
])

@app.callback(Output('graph','figure'), 
              [Input('selection', 'value')])
def data_picker(selection):
    if selection == 'CRT vs Misperception':
        fig = fig = px.scatter(df, x=x1, y=y1, trendline="ols", trendline_scope="overall")
    elif selection == 'NFCC vs Misperception':
        fig = fig = px.scatter(df, x=x2, y=y1, trendline="ols", trendline_scope="overall")
    elif selection == 'bficonmean vs Misperception':
        fig = fig = px.scatter(df, x=x3, y=y1, trendline="ols", trendline_scope="overall")
    elif selection == 'bfiopmean vs Misperception':
        fig = fig = px.scatter(df, x=x4, y=y1, trendline="ols", trendline_scope="overall")        
    elif selection == 'CRT vs Ambiguity':
        fig = fig = px.scatter(df, x=x1, y=y2, trendline="ols", trendline_scope="overall")
    elif selection == 'NFCC vs Ambiguity':
        fig = fig = px.scatter(df, x=x2, y=y2, trendline="ols", trendline_scope="overall")
    elif selection == 'bficonmean vs Ambiguity':
        fig = fig = px.scatter(df, x=x3, y=y2, trendline="ols", trendline_scope="overall")
    elif selection == 'bfiopmean vs Ambiguity':
        fig = fig = px.scatter(df, x=x4, y=y2, trendline="ols", trendline_scope="overall")    
    return fig


if __name__ == '__main__':
    app.run(debug=False)

enter image description here

I'm not sure that dropdown button on graph can change title of axis or not but callback can.

Upvotes: 1

Related Questions