Adding a drop down list based on RadioItems - python

This code works fine. I can update graphics according to RadioItem. There is no dropdown in my dash now but I want to add 4 dropdown in total. 2 of them should be for the first RadioItem and 2 of them for the other RadioItem. In other words, I'll be updating my chart for the second time when I add the dropdowns. I could not find sample code for this situation. Here is my code:
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
from plotly.subplots import make_subplots
pp_list=['pp1', 'pp2', 'pp3', 'pp4', 'pp5', 'pp6', 'pp7', 'pp8']
group_list=['gr1', 'gr2', 'gr3', 'gr4', 'gr5', 'gr6', 'gr7', 'gr8']
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Markdown('''**Choose to Compare:**'''),
dcc.RadioItems(
id='radio',
value='pp_basis',
options=[{'label': x, 'value': x}
for x in ['pp_basis', 'group_basis']]
),
dcc.Graph(id="graph"),
])
#app.callback(
Output("graph", "figure"),
[Input("radio", "value")])
def display_(radio_value):
fig = make_subplots(specs=[[{"secondary_y": True}]])
if radio_value == 'pp_basis':
fig.add_trace(
go.Scatter(x=['2019-01', '2019-02', '2019-03'], y=[2.1, 1.8, 1.3], name="pp1"),
)
fig.add_trace(
go.Scatter(x=['2019-01', '2019-02', '2019-03'], y=[1.9, 1.5, 0.8], name="pp2"),
)
fig.add_trace(
go.Scatter(x=['2019-01', '2019-02', '2019-03'], y=[300, 271, 311], mode='markers', name="secondary axis"),
secondary_y=True,
)
else:
fig.add_trace(
go.Bar(x=['2019-01', '2019-02', '2019-03'], y=[1.5, 1.3, 1.38], name="gr1"),
)
fig.add_trace(
go.Bar(x=['2019-01', '2019-02', '2019-03'], y=[1.3, 1.1, 1.18], name="gr2"),
)
fig.add_trace(
go.Scatter(x=['2019-01', '2019-02', '2019-03'], y=[300, 271, 311], mode='markers', name="secondary axis"),
secondary_y=True,
)
fig.update_layout(
title_text="Cost Analysis"
)
fig.update_xaxes(title_text="<b>Date</b>")
if radio_value == 'pp_basis':
fig.update_yaxes(
title_text="<b>Cost</b>",
)
else:
fig.update_yaxes(
title_text="<b>WACC</b>",
)
fig.update_yaxes(
title_text="<b>Secondary Axis</b>",
secondary_y=True)
return fig
app.run_server(debug=True)
I add two lists which are pp_list and group_list in the code. The result I want to design is as follows:
How can I do that?

I made the following changes to your code:
Created a dropdown item in the app layout
Created a callback with this new dropdown options/values as output and radio item value as input
Adapted the graph creator callback to use the dropdown value as input
Now, based on a radio item selection, the dropdown options are refreshed.
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
from plotly.subplots import make_subplots
pp_list=['pp1', 'pp2', 'pp3', 'pp4', 'pp5', 'pp6', 'pp7', 'pp8']
group_list=['gr1', 'gr2', 'gr3', 'gr4', 'gr5', 'gr6', 'gr7', 'gr8']
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Markdown('''**Choose to Compare:**'''),
dcc.RadioItems(
id='radio',
value='pp_basis',
options=[{'label': x, 'value': x}
for x in ['pp_basis', 'group_basis']]
),
dcc.Dropdown(id= 'dropdown'),
dcc.Graph(id="graph"),
])
#app.callback(
[Output('dropdown', 'options'),
Output('dropdown', 'value')],
Input('radio', 'value'))
def dropdown_options(radio_value):
if radio_value == 'pp_basis':
options = [{'label': x, 'value': x} for x in pp_list]
value = pp_list[0]
else:
options = [{'label': x, 'value': x} for x in group_list]
value = group_list[0]
return options, value
#app.callback(
Output("graph", "figure"),
[Input("dropdown", "value")])
#here you will define what the graph should look like based on the dropdown
def display_(dropdown_value):
fig = make_subplots(specs=[[{"secondary_y": True}]])
if dropdown_value.startswith('pp'):
fig.add_trace(
go.Scatter(x=['2019-01', '2019-02', '2019-03'], y=[2.1, 1.8, 1.3], name="pp1"),
)
fig.add_trace(
go.Scatter(x=['2019-01', '2019-02', '2019-03'], y=[1.9, 1.5, 0.8], name="pp2"),
)
fig.add_trace(
go.Scatter(x=['2019-01', '2019-02', '2019-03'], y=[300, 271, 311], mode='markers', name="secondary axis"),
secondary_y=True,
)
else:
fig.add_trace(
go.Bar(x=['2019-01', '2019-02', '2019-03'], y=[1.5, 1.3, 1.38], name="gr1"),
)
fig.add_trace(
go.Bar(x=['2019-01', '2019-02', '2019-03'], y=[1.3, 1.1, 1.18], name="gr2"),
)
fig.add_trace(
go.Scatter(x=['2019-01', '2019-02', '2019-03'], y=[300, 271, 311], mode='markers', name="secondary axis"),
secondary_y=True,
)
fig.update_layout(
title_text="Cost Analysis"
)
fig.update_xaxes(title_text="<b>Date</b>")
if dropdown_value.startswith('pp'):
fig.update_yaxes(
title_text="<b>Cost</b>",
)
else:
fig.update_yaxes(
title_text="<b>WACC</b>",
)
fig.update_yaxes(
title_text="<b>Secondary Axis</b>",
secondary_y=True)
return fig
app.run_server(debug=True, port=5050, host='0.0.0.0')

Related

Dash+Plotly Synchronize zoom and pan between two plots using imshow

I try to synchronize zoom and pan between two graphs in a dashboard (dash + plotly). I obtain strange behavior when I zoom on a graph, the second graph does not update. I need to zoom on the second graph to make both graphs update but not with the same zoom nor the same location on the graphs. Furthermore the shapes of the two graphs change.
Below is the code I am in. I do not see I am doing wrong.
import os
from dash import Dash, html, dcc, Input, Output, State
import plotly.express as px
import numpy as np
import rasterio as rio
app2 = Dash(__name__)
data_folder = r'.\data'
store = {}
for filename in os.listdir(data_folder):
if os.path.isfile(os.path.join(data_folder, filename)):
band_name = filename.replace('.', '_').split(sep='_')[-2]
with rio.open(os.path.join(data_folder, filename)) as dataset:
nb_band = dataset.count
if nb_band == 1:
data = dataset.read(1)
else:
data = dataset.read(tuple(range(1, nb_band + 1)))
if band_name == 'triband':
data = np.swapaxes(data, 2, 0)
data = np.swapaxes(data, 0, 1)
store[band_name] = data.astype(float)
else:
store[f'B{band_name}'] = data.astype(float)
fig1 = px.imshow(store['triband'])
fig1.update_xaxes(showticklabels=False, showgrid=False, zeroline=False)
fig1.update_yaxes(showticklabels=False, showgrid=False, zeroline=False)
fig1.update_layout(
margin=dict(l=0, r=0, t=0, b=0),
plot_bgcolor='rgba(0, 0, 0, 0)',
paper_bgcolor='rgba(0, 0, 0, 0)',
)
# Application structure and content
app2.layout = html.Div(className='main', children=[
html.H1(children='Hello Dash', style={'padding': 10}),
html.Div(children=[
html.Div(children=[
dcc.Graph(
id='graph1',
figure=fig1,
responsive=True
)
], style={'padding': 5, 'flex': 1}),
html.Div(children=[
dcc.Graph(
id='graph2',
figure=fig1,
responsive=True
)
], style={'padding': 5, 'flex': 1})
], style={'display': 'flex', 'flex-direction': 'row'}),
])
#app2.callback(Output('graph2', 'figure'),
Input('graph1', 'relayoutData'),
State('graph2', 'figure'))
def graph_event1(select_data, fig):
if select_data is not None:
try:
fig['layout']['xaxis']['range'] = [select_data['xaxis.range[0]'], select_data['xaxis.range[1]']],
fig['layout']['yaxis']['range'] = [select_data['yaxis.range[0]'], select_data['yaxis.range[1]']]
except KeyError:
pass
return fig
#app2.callback(Output('graph1', 'figure'),
Input('graph2', 'relayoutData'),
State('graph1', 'figure'))
def graph_event2(select_data, fig):
if select_data is not None:
try:
fig['layout']['xaxis']['range'] = [select_data['xaxis.range[0]'], select_data['xaxis.range[1]']],
fig['layout']['yaxis']['range'] = [select_data['yaxis.range[0]'], select_data['yaxis.range[1]']]
except KeyError:
pass
return fig
if __name__ == '__main__':
app2.run_server(debug=True)
I found a solution : rather than creating two graphs, I created a graph with several subplots and force zoom and pan between subplots.
fig = make_subplots(rows=1, cols=3, shared_xaxes=True, shared_yaxes=True)
fig.add_trace(
px.imshow(store['triband']).data[0],
row=1, col=1
)
fig.add_trace(
px.imshow(index_store['NDVI']).data[0],
row=1, col=2
)
fig.add_trace(
px.imshow(np.where(index_store['NDVI'] >= np.median(index_store['NDVI']),
0.8 * np.max(index_store['NDVI']),
0.8 * np.min(index_store['NDVI']))
).data[0],
row=1, col=3
)
fig.update_xaxes(matches='x', showticklabels=False, showgrid=False, zeroline=False)
fig.update_yaxes(matches='y', showticklabels=False, showgrid=False, zeroline=False)

Changing line color based on other line's index

I have a out dataframe containing two columns, Actual_Values and Predicted_Values.
I am trying to create a graph:
import pandas as pd
import plotly.graph_objects as go
x_data = out.index
trace1 = go.Scatter(
x=x_data,
y=out['Actual_Values'],
name="Actual Values"
)
trace2 = go.Scatter(
x=x_data,
y=out['Predicted_Values'],
name="Predictions"
)
traces = [trace1, trace2]
layout = go.Layout(
xaxis=dict(
autorange=True
),
yaxis=dict(
autorange=True
)
)
fig = go.Figure(data=traces, layout=layout)
plot(fig, include_plotlyjs=True)
which gives:
however, I need a graph, in which the blue line's changes to some other color from the start of the red line.
Does this help you?
import pandas as pd
import numpy as np
import plotly.graph_objects as go
# Data
n = 150
n_pred = 10
df1 = pd.DataFrame(
{"x": np.arange(n),
"actual_value": np.random.randint(0, 100, n)})
df2 = pd.DataFrame(
{"x": np.arange(n-n_pred, n),
"predicted_value": np.random.randint(0, 100, n_pred)})
# You need Outer join when prediction range is
# larger than actual value one.
df = pd.merge(df1, df2, on="x", how="outer")
idx_min = df[df["predicted_value"].notnull()].index[0]
# Plot
trace1 = go.Scatter(
x=df["x"][:idx_min+1],
y=df['actual_value'][:idx_min+1],
name="Actual Values",
line=dict(color="blue")
)
trace2 = go.Scatter(
x=df["x"][idx_min:],
y=df['actual_value'][idx_min:],
name="Actual Values",
mode="lines",
line=dict(color="green"),
showlegend=False
)
trace3 = go.Scatter(
x=df["x"],
y=df['predicted_value'],
name="Predicted Values",
line=dict(color="red")
)
traces = [trace1, trace2, trace3]
layout = go.Layout(
xaxis=dict(
autorange=True
),
yaxis=dict(
autorange=True
)
)
fig = go.Figure(data=traces, layout=layout)
fig.show()

Plotting 3D Chart in Dash Plotly

I have some difficulties to create a 3D chart for my Dash App. The code does not throw any error. It returns an empty 2D chart (not even a 3D chart).
I checked the variables z, x, y - they contain the correct values + shape. Code snippet is from Plotly, Chart Example "Passing x and y data to 3D Surface Plot". Any idea what I am missing?
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output
import plotly.graph_objects as go
app = dash.Dash()
app.layout = html.Div(children=[
html.H1(children="My 3D Chart!"),
dcc.Graph(
id='my-graph'
),
])
#app.callback(Output('my-graph', 'figure'))
def create_chart():
z = df_size_rolled.values
sh_0, sh_1 = z.shape
x, y = np.linspace(0, 1, sh_0), np.linspace(0, 1, sh_1)
fig = go.Figure(data=[go.Surface(z=z, x=x, y=y)])
return fig
if __name__ == '__main__':
app.run_server(debug=True)
I also tried, but didn't work:
data=[go.Surface(z=z, x=x, y=y)]
return {'data': [data]}
Any help much appreciated.
Seems like the ´data´- property is not needed in Dash.
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1("3D Charts", style={"textAlign": "center"}),
html.Div([html.Div([html.Span("Type Of Chart : ")], className="six columns",
style={"textAlign": "right", "padding-right": 30, "padding-top": 7}),
html.Div([dcc.Dropdown(id='select-date', options=[{'label': i, 'value': i} for i in my_dates],
value="2018-02-06")], className="six columns",
style={"width": "40%", "margin-left": "auto", "margin-right": "auto", "display": "block"}),
], className="row", style={"width": "80%"}),
html.Div([dcc.Graph(id='my-graph')], className="row")
], className="container")
#app.callback(
dash.dependencies.Output('my-graph', 'figure'),
[dash.dependencies.Input('select-date', 'value')])
def update_graph(selected):
global df_sliced
df_sliced = df_size.loc[selected:selected]
df_sliced = df_sliced.rolling(6).mean()
df_sliced = df_sliced.dropna()
trace2 = [go.Surface(
z = df_sliced.values,
colorscale='Rainbow', colorbar={"thickness": 10, "len": 0.5, "title": {"text": "Volume"}})]
layout2 = go.Layout(
title="Orderbook Structure " + str(selected), height=1000, width=1000, scene = dict(
xaxis_title='Order Level - Bid Side[0-9], Ask Side[10-19]',
yaxis_title='Time 08.00 until 22.00 (5Min Intervals)',
zaxis_title='Volume (Trailing Mean - 30Min)',
aspectmode='cube'),
scene_camera_eye=dict(x=2, y=-1.5, z=1.25),
)
return {"data": trace2, "layout": layout2}
if __name__ == '__main__':
app.run_server(debug=True)

Share all x-axes of a subplot (rows and columns)

I try to share all x-axes of a subplot structure with several columns, but I can't get the solution. With 'share_xaxes=True' only the x-axes of the same row are linked, and I am not able to change the 'xaxis' paramater from the figures in the subplot. Any idea?
In the Plotly documentation you can see that the axes have an attribute called scaleanchor (see https://plot.ly/python/reference/#layout-xaxis-scaleanchor). You can use it to connect as many axes as you like. I tested it out on a simple subplot with 2 rows and 2 columns where all x-axes are connected:
import plotly.plotly as py
import plotly.graph_objs as go
# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
def create_figure():
trace1 = go.Scatter(
x=[1, 2, 3],
y=[2, 3, 4]
)
trace2 = go.Scatter(
x=[1, 2, 3],
y=[5, 5, 5],
xaxis='x2',
yaxis='y2'
)
trace3 = go.Scatter(
x=[1, 2, 3],
y=[600, 700, 800],
xaxis='x3',
yaxis='y3'
)
trace4 = go.Scatter(
x=[1, 2, 3],
y=[7000, 8000, 9000],
xaxis='x4',
yaxis='y4'
)
data = [trace1, trace2, trace3, trace4]
layout = go.Layout(
xaxis=dict(
domain=[0, 0.45],
anchor='y'
),
xaxis2=dict(
domain=[0.55, 1],
anchor='y2',
scaleanchor='x'
),
xaxis3=dict(
domain=[0, 0.45],
anchor='y3',
scaleanchor='x'
),
xaxis4=dict(
domain=[0.55, 1],
anchor='y4',
scaleanchor='x'
),
yaxis=dict(
domain=[0, 0.45],
anchor='x'
),
yaxis2=dict(
domain=[0, 0.45],
anchor='x2'
),
yaxis3=dict(
domain=[0.55, 1],
anchor='x3'
),
yaxis4=dict(
domain=[0.55, 1],
anchor='x4'
)
)
fig = go.Figure(data=data, layout=layout)
return fig
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
html.Div(children='''
Dash: A web application framework for Python.
'''),
dcc.Graph(
id='example-graph',
figure=create_figure()
)
])
if __name__ == '__main__':
app.run_server(debug=True)
I know this post is old, but maybe this can help someone else:
Just use the option shared_xaxes = 'all' when you create subplots with make_subplots() and all x-axes will be shared.

Plotly legend next to each subplot, Python

After noticing that there was no answer to this question at the moment, I would like to know if anyone has an idea how to:
Have a legends for each subplot.
Group legends by name. (Ex: for different subplots, all have the same two curves but with different values).
Here's my Plotly script:
from plotly import tools
import plotly.plotly as py
import plotly.graph_objs as go
import plotly
nom_plot=[]
trace1 = go.Scatter(x=[1, 2, 3], y=[4, 5, 6],name='1',showlegend=True)
nom_plot.append('GRAPH 1')
trace2 = go.Scatter(x=[20, 30, 40], y=[50, 60, 70],name='2',yaxis='y2')
nom_plot.append('GRAPH 2')
trace3 = go.Scatter(x=[300, 400, 500], y=[600, 700, 800],showlegend=False)
nom_plot.append('GRAPH 3')
trace4 = go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000])
nom_plot.append('GRAPH 4')
trace5 = go.Scatter(x=[20, 30, 40], y=[50, 60, 70])
nom_plot.append('GRAPH 5')
print(trace1)
fig = tools.make_subplots(rows=4, cols=2, subplot_titles=(nom_plot))
fig.append_trace(trace1, 1, 1)
fig['layout']['xaxis1'].update(title='xaxis 1 title')
fig.append_trace(trace2, 1, 1)
fig.append_trace(trace3, 2, 1)
fig.append_trace(trace4, 2, 2)
fig['layout']['yaxis3'].update(title='yaxis 3 title')
fig.append_trace(trace5, 3, 1)
fig['layout']['yaxis2'].update(
overlaying='y1',
side='right',
anchor='x1',
# domain=[0.15, 1],
range=[2, 6],
# zeroline=False,
showline=True,
showgrid=False,
title='yaxis 3 title'
)
fig['layout'].update(height=1000, width=1000, title='Multiple Subplots' +' with Titles')
plotly.offline.plot(fig, filename='multiple-y-subplots6.html')
This what I obtain (Using Plotly Script above):
And this is what I want (Made by Pygal):
The solution is to create an HTML file that merge sevral charts offline rendered as html files:
import plotly
import plotly.offline as py
import plotly.graph_objs as go
fichier_html_graphs=open("DASHBOARD.html",'w')
fichier_html_graphs.write("<html><head></head><body>"+"\n")
i=0
while 1:
if i<=40:
i=i+1
#______________________________--Plotly--______________________________________
color1 = '#00bfff'
color2 = '#ff4000'
trace1 = go.Bar(
x = ['2017-09-25','2017-09-26','2017-09-27','2017-09-28','2017-09-29','2017-09-30','2017-10-01'],
y = [25,100,20,7,38,170,200],
name='Debit',
marker=dict(
color=color1
)
)
trace2 = go.Scatter(
x=['2017-09-25','2017-09-26','2017-09-27','2017-09-28','2017-09-29','2017-09-30','2017-10-01'],
y = [3,50,20,7,38,60,100],
name='Taux',
yaxis='y2'
)
data = [trace1, trace2]
layout = go.Layout(
title= ('Chart Number: '+str(i)),
titlefont=dict(
family='Courier New, monospace',
size=15,
color='#7f7f7f'
),
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
yaxis=dict(
title='Bandwidth Mbit/s',
titlefont=dict(
color=color1
),
tickfont=dict(
color=color1
)
),
yaxis2=dict(
title='Ratio %',
overlaying='y',
side='right',
titlefont=dict(
color=color2
),
tickfont=dict(
color=color2
)
)
)
fig = go.Figure(data=data, layout=layout)
plotly.offline.plot(fig, filename='Chart_'+str(i)+'.html',auto_open=False)
fichier_html_graphs.write(" <object data=\""+'Chart_'+str(i)+'.html'+"\" width=\"650\" height=\"500\"></object>"+"\n")
else:
break
fichier_html_graphs.write("</body></html>")
print("CHECK YOUR DASHBOARD.html In the current directory")
Result:
I used two side by side Div elements to emulate Plotly subplot. Doing this way, we have independent legends. However, if we want to share an axis, we should do it manually:
app.layout = html.Div(children=[
html.Div(['YOUR FIRST GRAPH OBJECT'],
style = {'float':'left', 'width':'49%'}) ,
html.Div(['YOUR SECOND GRAPH OBJECT'],
style = {'float':'right', 'width':'49%'})
])

Categories