Plotly: refresh data passed to px.line via dropdown from Database - python

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)

Related

Plotly ipwidgets with two Scattermapbox traces does not update the markers

i'm trying to make a simple widget that displays two points on a map together with a dropdown which allows you to select the case. On selecting a different case, different points are displayed on the map.
I initialise the map with the first case and on changing the value of the dropdown i update the data of the traces. The initial state works fine, but when i select a different case, the markes are not updated but the tooltip is updated and displayed in the right position.
Initial state
After changing the dropdown
the data i'm using looks like this
How do i force the traces to update the markers as well? I already tried updating the mode and marker properties of the Scattermapbox, that did not work.
I tried in a Jupyter notebook and in a Databricks notebook
Here is my example code:
from ipywidgets import widgets
import pandas as pd
import plotly.graph_objects as go
data = pd.DataFrame({"lat1":[46,48,47],"lon1":[13,11,13],"lat2":[45,47,46],"lon2":[12,10,12],"name":["aaa","bbb","ccc"],"info":[111,222,333]})
dropdown = widgets.Dropdown(
description='Case: ',
value=data['name'][0],
options=data['name'].unique().tolist()
)
one = go.Scattermapbox(
lat=[data['lat1'][0]],
lon=[data['lon1'][0]],
mode='markers+text',
marker=go.scattermapbox.Marker(
size=14
),
text=data[['name'][0]],
name = "one"
)
two = go.Scattermapbox(
lat=[data['lat2'][0]],
lon=[data['lon2'][0]],
mode='markers+text',
marker=go.scattermapbox.Marker(
size=14
),
text=data[['name','info'][0]],
name="two"
)
g = go.FigureWidget(data=[one, two],
layout=go.Layout(
title=dict(
text='Two traces'
),
mapbox = {
'center': {'lon': 13, 'lat': 46 },
'style': "stamen-terrain",
'zoom': 6},
width=1600,
height=900
))
def response(change):
temp_df = data[data['name']==dropdown.value]
with g.batch_update():
g.data[0].lat=temp_df['lat1']
g.data[0].lon=temp_df['lon1']
g.data[1].lat=temp_df['lat2']
g.data[1].lon=temp_df['lon2']
g.data[0].text=temp_df[['name']]
g.data[1].text=temp_df[['name','info']]
g.mode[0]='markers+text'
g.mode[1]='markers+text'
dropdown.observe(response, names="value")
widgets.VBox([dropdown,g])

Plotly: How to make a plotly dropdown menu for figures with wholly different data and layouts?

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
)

Change hover text of a plotly express treemap

I want to show in a treemap just the label and value of each item, not parent or ID. I have defined it with plotly express. No matter how much I have tinkered with it, I haven’t been able to restrict hover text to the fields I need. Check the code and capture
import plotly.express as px
fig = px.treemap(dfconcepto, path=['type','name'],
values = 'count',
width=900, height=900,
hover_data = ['count'],
)
fig.show()
I also have tried to create it with non-express treemap. Hovertext is what I want, but then a treemap with two levels is rendered asymmetric.
What I want is something like the hovertext of non-express treemap, but balanced and symmetric as in express treemap
What can I do?
Thanks in advance!
It seems to me you should overwrite your hover template
import pandas as pd
import plotly.express as px
url = "https://gist.githubusercontent.com/jlchulilla/3b4e40f68ba73b5dbcb661a1d861f308/raw/e564973db30a4612aba60c5b26dd108edc98f048/test2sof.csv"
df = pd.read_csv(url).drop("Unnamed: 0", axis=1)
fig = px.treemap(df, path=['type','name'],
values = 'coincidencia',
width=900, height=900,
)
# Now your hovertemplate looks like
# fig.data[0].hovertemplate
# 'labels=%{label}<br>coincidencia=%{value}<br>parent=%{parent}<br>id=%{id}<extra></extra>'
# But it seems to me you want something like
fig.data[0].hovertemplate = '%{label}<br>%{value}'
fig.show()

Plotly Dash: Why is my figure failing to show with a multi dropdown selection?

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

Dropdown menu for Plotly Choropleth Map Plots

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()

Categories