I'm developing a tool to visualize the effects of a set of parameters on a mathematical function by modifying those parameters via Dash sliders. I'm using the approach from a few of the Dash tutorial examples which use a callback to replace the figure.
This works, but the plot isn't as responsive to slider changes as built-in operations such as rotating the plot via dragging. This is especially the case when there are many elements in the figure.
Is there a different approach that will improve responsiveness? For example, is there a way to target only the plot elements that changed rather than replacing the entire figure?
Here's a minimal working example consisting of a static circle (with many samples) and a line segment that we rotate via a slider. Rotation of the line segment is quite choppy.
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
from dash.dependencies import Input, Output, State
import numpy as np
app = dash.Dash(__name__)
# plot a circle
t = np.linspace(0, 2*np.pi, 10000) # intentionally use many points to exaggerate the issue
x = np.cos(t)
y = np.sin(t)
z = np.zeros_like(t)
marker_size = 4
fig = go.Figure()
fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode='lines'))
fig.add_trace(go.Scatter3d(
x=[0.0, 0.0], y=[0.0, 0.0], z=[0.0, 0.0],
marker=go.scatter3d.Marker(size=marker_size),
line=dict(width=3.0),
showlegend=False,
))
fig.update_layout(
uirevision='constant',
autosize=False,
width=900,
height=900,
scene=dict(
xaxis=dict(range=[-1, 1]),
yaxis=dict(range=[-1, 1]),
zaxis=dict(range=[-1, 1.0]),
aspectratio=dict(x=2, y=2, z=2),
),
)
app.layout = html.Div(children=[
dcc.Graph(
id='example-graph',
figure=fig
),
html.Div(
[
dcc.Slider(
id='slider-phi',
min=0.0,
max=360.0,
step=1.0,
value=0.0,
marks={0: '0', 180: '180', 360: '360'},
updatemode='drag',
),
],
style=dict(width='50%'),
),
html.Div(children='', id='output-box'),
])
#app.callback(
Output('example-graph', 'figure'),
[Input('slider-phi', 'value')],
[State('example-graph', 'figure')]
)
def display_structure(phi, myfig):
myfig['data'][1]['x'][1] = np.cos(np.radians(phi))
myfig['data'][1]['y'][1] = np.sin(np.radians(phi))
myfig['data'][1]['z'][1] = 0
return myfig
if __name__ == '__main__':
app.run_server(debug=True)
The lack of responsiveness can be attributed to two key factors. The first is, as you note, that the whole figure is updated each time rather than just the desired trace. You can avoid this by targeting the extendData property instead of the figure property,
#app.callback(Output('example-graph', 'extendData'), [Input('slider-phi', 'value')])
def update_data(phi):
# tuple is (dict of new data, target trace index, number of points to keep)
return dict(x=[[0, np.cos(np.radians(phi))]], y=[[0, np.sin(np.radians(phi))]]), [1], 2
The second factor is that the callback is executed server side rather than client side, i.e. requests are exchange between client and server each time you move the slider. To avoid this, you can move the update to the client by converting the callback to a clientside callback,
app.clientside_callback(
"""
function(phi) {
// tuple is (dict of new data, target trace index, number of points to keep)
return [{x: [[0, Math.cos(phi/180*Math.PI)]], y:[[0, Math.sin(phi/180*Math.PI)]]}, [1], 2]
}
""", Output('example-graph', 'extendData'), [Input('slider-phi', 'value')]
)
which should yield reasonable responsiveness. Here is how it looks on my laptop,
For completeness, here is the full code,
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
from dash.dependencies import Input, Output, State
import numpy as np
app = dash.Dash(__name__)
# plot a circle
t = np.linspace(0, 2*np.pi, 10000) # intentionally use many points to exaggerate the issue
x = np.cos(t)
y = np.sin(t)
z = np.zeros_like(t)
marker_size = 4
fig = go.Figure()
fig.add_trace(go.Scatter3d(x=x, y=y, z=z, mode='lines'))
fig.add_trace(go.Scatter3d(
x=[0.0, 0.0], y=[0.0, 0.0], z=[0.0, 0.0],
marker=go.scatter3d.Marker(size=marker_size),
line=dict(width=3.0),
showlegend=False,
))
fig.update_layout(
uirevision='constant',
autosize=False,
width=900,
height=900,
scene=dict(
xaxis=dict(range=[-1, 1]),
yaxis=dict(range=[-1, 1]),
zaxis=dict(range=[-1, 1.0]),
aspectratio=dict(x=2, y=2, z=2),
),
)
app.layout = html.Div(children=[
dcc.Graph(
id='example-graph',
figure=fig
),
html.Div(
[
dcc.Slider(
id='slider-phi',
min=0.0,
max=360.0,
step=1.0,
value=0.0,
marks={0: '0', 180: '180', 360: '360'},
updatemode='drag',
),
],
style=dict(width='50%'),
),
html.Div(children='', id='output-box'),
])
app.clientside_callback(
"""
function(phi) {
// tuple is (dict of new data, target trace index, number of points to keep)
return [{x: [[0, Math.cos(phi/180*Math.PI)]], y:[[0, Math.sin(phi/180*Math.PI)]]}, [1], 2]
}
""", Output('example-graph', 'extendData'), [Input('slider-phi', 'value')]
)
if __name__ == '__main__':
app.run_server(debug=True)
It looks like although display_structure modifies myfig in place, the return value is taken as a brand new plot which plotly will render all over again.
Is there any chance that the callback does not require a return value? If your function modifies the plot attributes this could be in place.
FYI: I use bokeh and have limited knowledge about plotly.
Related
I want to use the plotly express lineplot to create a simple interactive plot that refreshes its data when choosing a new item in a dropdown box.
I have created a simple function prepare_dashboard_data which loads data from a database and does some filtering. The dashboard is showing the data from the initial dataload but I am completely lost on how to create a callback to the px.line function, such that the plot is updated with new data loaded from the database.
I have taken inspiration from this post where plotly.graph_objs are used. But i quite like the functionality of the default lineplot.
And in the example a preloaded dataframe is simply filtered based on the dropdown choice. This is not what I want.
I have some limited knowledge with ipython widgets and the observer pattern, but I am completely lost in this case. Here is a rough sceme of my current code:
import plotly.graph_objs as go
import plotly.express as px
def prepare_dashboard_data(serverconfig, shopname = "myshop"):
# Data is loaded form a db
# Transformed filtered by shopname and so on ...
# This returns a datframe with has an timestamp as an index and many items as columns
return df
# Prepare Dropdown menues
shopnames = df.columns # This just gives a list of available shopnames.
# plotly start
fig = go.Figure()
fig = px.line(prepare_dashboard_data(serverconfig=conf, shopname = "myshop"),width=1600, height=800)
# menu setup
updatemenu= []
# buttons for menu 1, shopnames
buttons=[]
# should i create traces for each shopnames alread here:
for webshop_name in shopnames :
buttons.append(dict(method='update',
label=webshop_name,
visible=True,
args=[#{I guess here goes the magic?}
]
)
)
# some adjustments to the updatemenus
updatemenu=[]
your_menu=dict()
updatemenu.append(your_menu)
updatemenu[0]['buttons']=buttons
updatemenu[0]['direction']='down'
updatemenu[0]['showactive']=True
fig.update_layout(
autosize=False,
width=1800,
height=800,
margin=dict(
l=50,
r=50,
b=100,
t=100,
pad=4
),
updatemenus=updatemenu,
paper_bgcolor="LightSteelBlue",
)
fig.show()
Any help would be really appreciated. I have tried to make sense of the documentation but i think i would need a pointer. Currently i generate my plots in a Vscode/jupyter notebook, not as a standalone app .
You should use plotly-Dash to solve this. Here is an example you can tune to fit the structure of your dataframe.
from dash import Dash, dcc, html, Input, Output
import pandas as pd
import plotly.express as px
app = Dash(__name__)
app.layout = html.Div([
dcc.Dropdown(['a','b','c'], 'a', id='dropdown'),
dcc.Graph(id='graph')
])
df = pd.DataFrame(
{'a': [1,2,3,4,5],
'b': [-1,-2,-3,-4,-5],
'c': [2,4,6,8,10]
}
)
#app.callback(
Output(component_id='graph', component_property='figure'),
Input(component_id='dropdown', component_property='value')
)
def update_output(column):
fig = px.line(df, x=column)
return fig
if __name__ == '__main__':
app.run_server(debug=True)
I'm plotting a multidimensional table in scatter plots to test relationships between columns of a unique data frame.
I would like to know if there is any way that I can change in the browser the variable in each ax in an interactive way, without needing to plot another grid.
I don't know if this is a redundant question, but I've already did some research without any success.
fig = px.scatter_3d(data, x="V1", y="V2", z= 'V3', hover_data=['Z'])
fig.show()
Thank you in advance.
The complete code snippet below will give you a Dash-app in JupyterLab that looks like this:
Here you can change which columns to display from the plotly dataset px.data.stocks(). If this approach is something you can use, I'd be happy to explain the details. ANd if JupyterLab is not your thing, just follow the three steps needed to rewrite it to a standard Dash app as described in the post Plotly: How to rewrite a standard dash app to launch it in JupyterLab?
Complete code_
import plotly as py
import pandas as pd
from plotly import tools
import plotly.express as px
import plotly.graph_objects as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from jupyter_dash import JupyterDash
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
colors = px.colors.qualitative.Plotly
# colors = ['blue', 'green', 'red', 'black', 'yellow']
symbols = ['circle', 'circle-open', 'square', 'square-open', 'diamond', 'diamond-open', 'cross', 'x']
df = px.data.stocks().set_index('date')
columns = df.columns
# Set up well organized controls in a dbc.Card()
controls = dbc.Card([dbc.FormGroup([dbc.Label("x-axis"),
dcc.Dropdown(id='dd_x',
options= [{'label': k, 'value': k} for k in columns],
value=columns[0],
),
],),
dbc.FormGroup([dbc.Label("y-axis"),
dcc.Dropdown(id='dd_y',
options= [{'label': k, 'value': k} for k in columns],
value=columns[1],
),
],),
dbc.FormGroup([dbc.Label("z-axis"),
dcc.Dropdown(id='dd_z',
options= [{'label': k, 'value': k} for k in columns],
value=columns[2],
),
],)
],
body=True,
style = {'font-size': 'large'}
)
# Set up the app layout using dbc.Container(), dbc.Row(), and dbc.Col()
app.layout = dbc.Container([html.H1("Make a column selection for each axis"),
html.Hr(),
dbc.Row([dbc.Col([controls],xs = 4),
dbc.Col([dbc.Row([dbc.Col(dcc.Graph(id="market_graph")),])]),
]),
html.Br(),
],
fluid=True,
)
# 3D figure with callbacks for color, symbol and size
#app.callback(
Output("market_graph", "figure"),
[
Input("dd_x", "value"),
Input("dd_y", "value"),
Input("dd_z", "value"),
],
)
def history_graph(x, y, z):
# df = px.data.iris()
fig = px.scatter_3d(df, x=df[x], y=df[y], z=df[z])
fig.data[0].update(marker_color=colors[4])
fig.data[0].update(marker_symbol=symbols[6])
fig.data[0].update(marker_size=8)
fig.update_layout(uirevision='constant')
fig.update_layout(template = 'plotly_dark')
fig.update_layout(margin=dict(l=10, r=10, b=10, t=10))
return fig
app.run_server(mode='inline', port = 8007)
I am trying to make an interactive plot with a dropdown menu that selects from a series of wholly unrelated figures (i.e. plots that rely on different data structures and that have very different layouts). All of the dropdown menu examples I have seen are based on either a single set of data or multiple datasets but that use a relatively simple plot layout. This is not applicable to my case where I am trying to merge dozens of plots with very different layouts and underlying data. Below is a working example of the plots that I am trying to merge. The layouts are highly different across each plot:
import plotly.graph_objs as go
import plotly.express as px
import pandas as pd
# Prep some fake data for a bar graph
df1 = pd.DataFrame(dict(
bar_y = ['Bar1', 'Bar2'],
bar_x = [2,3],
bar_z = [1,2]
))
# Make bar graph
fig1 = px.bar(df1,
x="bar_x",
y='bar_y',
color='bar_z',
orientation='h',
)
# Add layout attributes
fig1.update_layout(
xaxis_title="<b> Bar graph title <b>",
yaxis_title="<b> Bar x axis <b>",
legend_title="<b> Bar y axis <b>",
xaxis = dict(
showgrid=True,
ticks="",
showline = False,
gridcolor = 'white'
)
)
# Prep some fake data for a line graph
df2 = pd.DataFrame(dict(
line_y = [3,2,1, 1,2,3],
line_x = [1,2,3,1,2,3],
line_group = ['line1','line1','line1','line2','line2','line2']
))
# Make an ugly line graph
fig2 = px.line(
df2,
x= 'line_x',
y= 'line_y',
color = 'line_group'
)
# Add a number of layout attributes that are distinct from those above
fig2.update_layout(
shapes=[dict(
type= 'line',
fillcolor = 'black',
line_width=2,
yref= 'y', y0= 0, y1= 0,
xref= 'x', x0= 1, x1= 3,
)],
xaxis_title="<b> Line graph title <b>",
yaxis_title="<b> Line x axis <b>",
legend_title="<b> Line y axis <b>",
template='simple_white',
hoverlabel=dict(bgcolor="white")
)
# Create a dropdown menu. Below is close to what I'd like to do, but the data statements are not working correctly and the shape in fig2 is causing problems...
fig3 = go.Figure()
fig3.update_layout(
updatemenus=[
dict(
active=0,
buttons=list([
dict(label="Bar Graph",
method="update",
args=[fig1.to_dict()['data'],
fig1.to_dict()['layout']]
),
dict(label="Line Graph",
method="update",
args=[fig2.to_dict()['data'],
fig2.to_dict()['layout']]
),
]))
]
)
It appears that I am almost able to correctly update the layout of each dropdown constituent plot based on the layout of each original graph. However, is it possible to update the data via this sort of method
as well?
I may be missing the point completely here. And it may also be overkill to unleash a Dash app in this case. But I would like to show you how the following setup can enable you to return completely different figure objects using a dcc.Dropdown(). The code snippet below will produce the following app:
If you now select fig2, you'll get this:
We can talk more about the details if this is something you can use. Also, the design with the very wide dropdown button is admittedly not the prettiest one, but I assume that design isn't the primary objective here.
Complete code:
import dash
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
# Prep some fake data for a bar graph
df1 = pd.DataFrame(dict(
bar_y = ['Bar1', 'Bar2'],
bar_x = [2,3],
bar_z = [1,2]
))
# Make bar graph
fig1 = px.bar(df1,
x="bar_x",
y='bar_y',
color='bar_z',
orientation='h',
)
# Add layout attributes
fig1.update_layout(
xaxis_title="<b> Bar graph title <b>",
yaxis_title="<b> Bar x axis <b>",
legend_title="<b> Bar y axis <b>",
xaxis = dict(
showgrid=True,
ticks="",
showline = False,
gridcolor = 'white'
)
)
# Prep some fake data for a line graph
df2 = pd.DataFrame(dict(
line_y = [3,2,1, 1,2,3],
line_x = [1,2,3,1,2,3],
line_group = ['line1','line1','line1','line2','line2','line2']
))
# Make an ugly line graph
fig2 = px.line(
df2,
x= 'line_x',
y= 'line_y',
color = 'line_group'
)
# Add a number of layout attributes that are distinct from those above
fig2.update_layout(
shapes=[dict(
type= 'line',
fillcolor = 'black',
line_width=2,
yref= 'y', y0= 0, y1= 0,
xref= 'x', x0= 1, x1= 3,
)],
xaxis_title="<b> Line graph title <b>",
yaxis_title="<b> Line x axis <b>",
legend_title="<b> Line y axis <b>",
template='simple_white',
hoverlabel=dict(bgcolor="white")
)
# app = JupyterDash(__name__)
app = dash.Dash()
figs = ['fig1', 'fig2']
app.layout = html.Div([
html.Div([
dcc.Graph(id='plot'),
html.Div([
dcc.Dropdown(
id='variables',
options=[{'label': i, 'value': i} for i in figs],
value=figs[0]
)
])
])
])
#app.callback(
Output('plot', 'figure'),
[Input('variables', 'value')])
def update_graph(fig_name):
if fig_name == 'fig1':
# fig=go.Figure(go.Scatter(x=[1,2,3], y = [3,2,1]))
return fig1
if fig_name == 'fig2':
# fig=go.Figure(go.Bar(x=[1,2,3], y = [3,2,1]))
return fig2
# app.run_server(mode='external', debug=True)
app.run_server(debug=True,
use_reloader=False # Turn off reloader if inside Jupyter
)
I am building a simple python dashboard using dash and plotly. I am also new to python (as is probably evident!) and I'm happy for any/all corrections. I would like to plot a time series of data from a pre-determined CSV file. I have added a dropdown selection box with which I would like to allow multiple different columns to be plotted.
Sample data:
"TOA5","HE605_RV50_GAF","CR6","7225","CR6.Std.07","CPU:BiSP5_GAF_v2d.CR6","51755","SensorStats"
"TIMESTAMP","RECORD","BattV_Min","BattV_Avg","PTemp_C_Avg","SensorRel_Min(1)","SensorRel_Min(2)","SensorRel_Min(3)","SensorRel_Min(4)","SensorRel_Min(5)","SensorRel_Max(1)","SensorRel_Max(2)","SensorRel_Max(3)","SensorRel_Max(4)","SensorRel_Max(5)"
"TS","RN","Volts","Volts","Deg C","","","","","","","","","",""
"","","Min","Avg","Avg","Min","Min","Min","Min","Min","Max","Max","Max","Max","Max"
"2019-09-30 11:15:00",0,12.68219,12.74209,"NAN","NAN","NAN","NAN","NAN","NAN","NAN","NAN","NAN","NAN","NAN"
"2019-09-30 11:30:00",1,12.68466,12.73777,31.26331,-2.498894,-2.38887,-8.497528,-2.963989,-20.42339,41.51585,28.41309,88.98283,27.27819,17.98986
"2019-09-30 11:45:00",2,12.69364,12.74584,31.43891,-3.490456,-2.856804,-8.770081,-3.879868,-22.69171,42.27676,30.53723,89.47752,34.25191,23.92586
"2019-09-30 12:00:00",3,12.69078,12.74522,31.38461,-3.290047,-2.973389,-8.69928,-3.295074,-21.88254,42.72508,29.91062,83.36012,27.9931,22.6571
"2019-09-30 12:15:00",4,12.6914,12.74376,31.2449,-2.899231,-2.392128,-10.01413,-2.996033,-23.22171,42.97162,29.20943,106.1204,35.93995,41.74426
My python(3.7) code for this is:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import pandas as pd
import plotly.graph_objects as go
# Load external stylesheets
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# Load external datasets
df = pd.read_csv('../SampleData/test_data.dat', skiprows=3, na_values='NAN')
df.columns=["TIMESTAMP","RECORD","BattV_Min","BattV_Avg","PTemp_C_Avg","SensorRel_Min(1)","SensorRel_Min(2)","SensorRel_Min(3)","SensorRel_Min(4)","SensorRel_Min(5)","SensorRel_Max(1)","SensorRel_Max(2)","SensorRel_Max(3)","SensorRel_Max(4)","SensorRel_Max(5)"]
# define dropdown options
opts=[{'label': k, 'value': k} for k in list(df.columns.values)[1:]]
# create plotly figures
fig2=go.Figure()
# Create a Dash layout
app.layout = html.Div(children=[
html.H1(children='Testing dashboard v01'),
html.Div(children='''
Select variable to plot below.
'''),
html.Div(children='''
Select variables to add to plot below.
'''),
dcc.Dropdown(
id='multiVariableDropdown',
options=opts,
value='RECORD',
multi=True
),
dcc.Graph(
id='plot2'
)
])
# Add callback functions
## For plot 2
#app.callback(Output('plot2', 'figure'),
[Input('multiVariableDropdown', 'value')])
def update_graph(selectedVariable2):
trace_finalPlot2 = go.Scatter(
x=df['TIMESTAMP'],
y=df[selectedVariable2],
name=str(selectedVariable2))
fig2 = go.Figure(data=trace_finalPlot2)
return fig2
if __name__ == '__main__':
app.run_server(debug=True)
The initial rendering of the plot looks good, as this is what appear after running python3 app.py:
But once I add another column to be plotted from the multi-selection dropdown, the original data disappears and it only plots a single point:
Unfortunately, it's not returning any error data so I'm having trouble debugging. Any tips/hints appreciated.
The issue is that the dropdown returns a list of multiple variables (as you set multi=True), while your callback is designed to plot only one variable.
In order to plot multiple variables, you need to iterate through the selected list of variables (i.e. through selectedVariable2 in your code) and add the respective traces to the figure.
You should also make sure that the dropdown is initialized with a list rather than a string (i.e. you should replace value="RECORD" with value=["RECORD"].
I included an example below.
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
# Load external stylesheets
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# Create a sample dataset
df = pd.DataFrame({"TIMESTAMP": ["2019-09-30 11:15:00", "2019-09-30 11:30:00", "2019-09-30 11:45:00", "2019-09-30 12:00:00", "2019-09-30 12:15:00"],
"RECORD": [0, 1, 2, 3, 4],
"SensorRel_Min(2)": [12.68219, 12.68466, 12.69364, 12.69078, 12.6914],
"SensorRel_Min(3)": [14.74209, 13.73777, 10.74584, 9.74522, 16.74376]})
# Define dropdown options
opts = [{'label': k, 'value': k} for k in list(df.columns.values)[1:]]
# Create a Dash layout
app.layout = html.Div(children=[
html.H1(children='Testing dashboard v01'),
html.Div(children='''
Select variable to plot below.
'''),
html.Div(children='''
Select variables to add to plot below.
'''),
dcc.Dropdown(
id='multiVariableDropdown',
options=opts,
value=['RECORD'],
multi=True
),
dcc.Graph(
id='plot2'
)
])
# Add callback functions
## For plot 2
#app.callback(Output('plot2', 'figure'),
[Input('multiVariableDropdown', 'value')])
def update_graph(selectedVariable2):
traces = []
for var in selectedVariable2:
traces.append(go.Scatter(x=df['TIMESTAMP'],
y=df[var],
name=var))
fig2 = go.Figure(data=traces)
return fig2
if __name__ == '__main__':
app.run_server(debug=True)
Try to debug it by using print(). Something like below, this way you can see what`s getting sent to the Output component every time you add/remove something from dropdown. Hope it helps!
def update_graph(selectedVariable2):
trace_finalPlot2 = go.Scatter(
x=df['TIMESTAMP'],
y=df[selectedVariable2],
name=str(selectedVariable2))
fig2 = go.Figure(data=trace_finalPlot2)
print(df[selectedVariable2])
return fig2
I am trying to create choropleth maps. Below is an example that works:
df = px.data.gapminder().query("year==2007")
fig = go.Figure(data=go.Choropleth(
locations=happy['iso'], # Spatial coordinates
z = happy['Happiness'].astype(float), # Data to be color-coded
colorbar_title = "Happiness Score",
))
fig.update_layout(
title_text = 'Life Expectancy in 2007'
)
fig.show()
However, I would like to create a dropdown menu that will change the plotted values between different variables (e.g., Life Expectancy, GDP, Population). I believe that this is possible but have not seen any tutorial online. Most of them just uses other kind of barcharts or scatterplots.
Here is what I have gotten so far:
# Initialize figure
fig = go.Figure()
# Add Traces
fig.add_trace(go.Figure(data=go.Choropleth(
locations=df['iso_alpha'], # Spatial coordinates
z = df['lifeExp'].astype(float), # Data to be color-coded
colorbar_title = "Life Expectancy")))
fig.add_trace(go.Figure(data=go.Choropleth(
locations=df['iso_alpha'], # Spatial coordinates
z = df['gdpPercap'].astype(float), # Data to be color-coded
colorbar_title = "GDP per capita")))
But I am not sure how to proceed from here. Do I need to update the layout of the figure via fig.update_layout or something?
There are two ways to solve this
Dash
# save this as app.py
import pandas as pd
import plotly.graph_objs as go
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
# Data
df = px.data.gapminder().query("year==2007")
df = df.rename(columns=dict(pop="Population",
gdpPercap="GDP per Capita",
lifeExp="Life Expectancy"))
cols_dd = ["Population", "GDP per Capita", "Life Expectancy"]
app = dash.Dash()
app.layout = html.Div([
dcc.Dropdown(
id='demo-dropdown',
options=[{'label': k, 'value': k} for k in cols_dd],
value=cols_dd[0]
),
html.Hr(),
dcc.Graph(id='display-selected-values'),
])
#app.callback(
dash.dependencies.Output('display-selected-values', 'figure'),
[dash.dependencies.Input('demo-dropdown', 'value')])
def update_output(value):
fig = go.Figure()
fig.add_trace(go.Choropleth(
locations=df['iso_alpha'], # Spatial coordinates
z=df[value].astype(float), # Data to be color-coded
colorbar_title=value))
fig.update_layout(title=f"<b>{value}</b>", title_x=0.5)
return fig
if __name__ == '__main__':
app.run_server()
run this as python app.py and go to http://127.0.0.1:8050
Plotly
In this case we need to play with visibility of different traces and create buttons in a way they show one traces and hide all the others.
import pandas as pd
import numpy as np
import plotly.graph_objs as go
import plotly.express as px
# Data
df = px.data.gapminder().query("year==2007")
df = df.rename(columns=dict(pop="Population",
gdpPercap="GDP per Capita",
lifeExp="Life Expectancy"))
cols_dd = ["Population", "GDP per Capita", "Life Expectancy"]
# we need to add this to select which trace
# is going to be visible
visible = np.array(cols_dd)
# define traces and buttons at once
traces = []
buttons = []
for value in cols_dd:
traces.append(go.Choropleth(
locations=df['iso_alpha'], # Spatial coordinates
z=df[value].astype(float), # Data to be color-coded
colorbar_title=value,
visible= True if value==cols_dd[0] else False))
buttons.append(dict(label=value,
method="update",
args=[{"visible":list(visible==value)},
{"title":f"<b>{value}</b>"}]))
updatemenus = [{"active":0,
"buttons":buttons,
}]
# Show figure
fig = go.Figure(data=traces,
layout=dict(updatemenus=updatemenus))
# This is in order to get the first title displayed correctly
first_title = cols_dd[0]
fig.update_layout(title=f"<b>{first_title}</b>",title_x=0.5)
fig.show()