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

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)

Related

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

Plotly: How to display graph after clicking a button?

I want to use plotly to display a graph only after a button is clicked but am not sure how to make this work. My figure is stored in the following code bit
fig1 = go.Figure(data=plot_data, layout=plot_layout)
I then define my app layout with the following code bit:
app.layout = html.Div([
#button
html.Div(className='submit', children=[
html.Button('Forecast', id='submit', n_clicks=0)
]),
#loading
dcc.Loading(
id="loading-1",
type="default",
children=html.Div(id="loading-output-1")
),
#graph
dcc.Graph(id= 'mpg-scatter',figure=fig),
#hoverdata
html.Div([
dcc.Markdown(id='hoverdata-text')
],style={'width':'50%','display':'inline-block'})
])
#app.callback(Output('hoverdata-text','children'),
[Input('mpg-scatter','hoverData')])
def callback_stats(hoverData):
return str(hoverData)
if __name__ == '__main__':
app.run_server()
But the problem is i only want the button displayed at first. Then when someone clicks on the forecast button the loading feature appears and a second later the graph displays. I defined a dcc.loading component but am not sure how to define the callback for this feature.
SUGGESTION 3 - dcc.Store() and dcc.Loading
This suggestion uses a dcc.Store() component, a html.Button() and a dcc.Loading component to produce what I now understand to be the desired setup:
Launch an app that only shows a button.
Click a button to show a loading icon, and then
display a figure.
Click again to show the next figure in a sequence of three figures.
Start again when the figure sequence is exhausted.
Upon launch, the app will look like this:
Now you can click Figures once to get Figure 1 below, but only after enjoying one of the following loading icons: ['graph', 'cube', 'circle', 'dot', or 'default'] of which 'dot' will trigger ptsd, and 'cube' happens to be my favorite:
Loading...
Figure 1
Now you cann keep on clicking for Figure 2 and Figure 3. I've set the loading time for Figure 1 no less than 5 seconds, and then 2 seconds for Figure 2 and Figure 3. But you can easily change that.
When you've clicked more than three times, we start from the beginning again:
I hope I've finally figured out a solution for what you were actually looking for. The setup in the code snippet below builds on the setup described here, but has been adjusted to hopefully suit your needs. Let me know how this works out for you!
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_table
from dash.exceptions import PreventUpdate
import dash_bootstrap_components as dbc
import time
time.sleep(5) # Delay for 5 seconds.
global_df = pd.DataFrame({'value1':[1,2,3,4],
'value2':[10,11,12,14]})
# app = JupyterDash(__name__)
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
df = pd.DataFrame({'Value 1': [1,2,3],
'Value 2':[10,11,12],
'Value 3':[14,12,9]})
df.set_index('Value 1', inplace = True)
app.layout = html.Div([
# The memory store reverts to the default on every page refresh
dcc.Store(id='memory'),
# The local store will take the initial data
# only the first time the page is loaded
# and keep it until it is cleared.
# Same as the local store but will lose the data
# when the browser/tab closes.
html.Table([
html.Thead([
html.Tr(html.Th('Click to launch figure:')),
html.Tr([
html.Th(html.Button('Figures', id='memory-button')),
]),
]),
]),
dcc.Loading(id = "loading-icon",
#'graph', 'cube', 'circle', 'dot', or 'default'
type = 'cube',
children=[html.Div(dcc.Graph(id='click_graph'))])
])
# Create two callbacks for every store.
# add a click to the appropriate store.
#app.callback(Output('memory', 'data'),
[Input('memory-button', 'n_clicks')],
[State('memory', 'data')])
def on_click(n_clicks, data):
if n_clicks is None:
# prevent the None callbacks is important with the store component.
# you don't want to update the store for nothing.
raise PreventUpdate
# Give a default data dict with 0 clicks if there's no data.
data = data or {'clicks': 0}
data['clicks'] = data['clicks'] + 1
if data['clicks'] > 3: data['clicks'] = 0
return data
# output the stored clicks in the table cell.
#app.callback(Output('click_graph', 'figure'),
# Since we use the data prop in an output,
# we cannot get the initial data on load with the data prop.
# To counter this, you can use the modified_timestamp
# as Input and the data as State.
# This limitation is due to the initial None callbacks
# https://github.com/plotly/dash-renderer/pull/81
[Input('memory', 'modified_timestamp')],
[State('memory', 'data')])
def on_data(ts, data):
if ts is None:
#raise PreventUpdate
fig = go.Figure()
fig.update_layout(plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
yaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')),
xaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')))
return(fig)
data = data or {}
0
# plotly
y = 'Value 2'
y2 = 'Value 3'
fig = go.Figure()
fig.update_layout(plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
yaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')),
xaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')))
if data.get('clicks', 0) == 1:
fig = go.Figure(go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines'))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='plotly_dark',
title = 'Plot number ' + str(data.get('clicks', 0)))
# delay only after first click
time.sleep(2)
if data.get('clicks', 0) == 2:
fig = go.Figure((go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines')))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='seaborn',
title = 'Plot number ' + str(data.get('clicks', 0)))
if data.get('clicks', 0) == 3:
fig = go.Figure((go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines')))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='plotly_white',
title = 'Plot number ' + str(data.get('clicks', 0)))
# Aesthetics
fig.update_layout(margin= {'t':30, 'b':0, 'r': 50, 'l': 50, 'pad': 0},
hovermode = 'x',
legend=dict(x=1,y=0.85),
uirevision='constant')
# delay for every figure
time.sleep(2)
return fig
app.run_server(mode='external', port = 8070, dev_tools_ui=True,
dev_tools_hot_reload =True, threaded=True)
SUGGESTION 2
After a little communation we now know that you'd like to:
only display a button first (question)
when the button is clicked once fig 1 is displayed at the bottom , on 2nd click fig 2 is displayed, and on 3rd click fig 3 is displayed (comment)
I've made a new setup that should meet all criteria above. At first, only the control options are being showed. And then you can select which figure to display: Fig1, Fig2 or Fig3. To me it would seem like a non-optimal user iterface if you have to cycle through your figures in order to select which one you would like to display. So I'v opted for radio buttons such as this:
Now you can freely select your figure to display, or go back to showing nothing again, like this:
Display on startup, or when None is selected:
Figure 1 is selected
You still haven't provided a data sample, so I'm still using my synthetic data from Suggestion 1, and rather letting the different layouts indicate which figure is shown. I hope that suits your needs since it seemed that you would like to have different layouts for the different figures.
Complete code 2
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_bootstrap_components as dbc
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
pd.options.plotting.backend = "plotly"
from datetime import datetime
palette = px.colors.qualitative.Plotly
# sample data
df = pd.DataFrame({'Prices': [1,10,7,5, np.nan, np.nan, np.nan],
'Predicted_prices':[np.nan, np.nan, np.nan, 5, 8,6,9]})
# app setup
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
# controls
controls = dbc.Card(
[dbc.FormGroup(
[
dbc.Label("Options"),
dcc.RadioItems(id="display_figure",
options=[ {'label': 'None', 'value': 'Nope'},
{'label': 'Figure 1', 'value': 'Figure1'},
{'label': 'Figure 2', 'value': 'Figure2'},
{'label': 'Figure 3', 'value': 'Figure3'}
],
value='Nope',
labelStyle={'display': 'inline-block', 'width': '10em', 'line-height':'0.5em'}
)
],
),
dbc.FormGroup(
[dbc.Label(""),]
),
],
body=True,
style = {'font-size': 'large'})
app.layout = dbc.Container(
[
html.H1("Button for predictions"),
html.Hr(),
dbc.Row([
dbc.Col([controls],xs = 4),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id="predictions")),
])
]),
]),
html.Br(),
dbc.Row([
]),
],
fluid=True,
)
#app.callback(
Output("predictions", "figure"),
[Input("display_figure", "value"),
],
)
def make_graph(display_figure):
# main trace
y = 'Prices'
y2 = 'Predicted_prices'
# print(display_figure)
if 'Nope' in display_figure:
fig = go.Figure()
fig.update_layout(plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
yaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')),
xaxis = dict(showgrid=False, zeroline=False, tickfont = dict(color = 'rgba(0,0,0,0)')))
return fig
if 'Figure1' in display_figure:
fig = go.Figure(go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines'))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='plotly_dark')
# prediction trace
if 'Figure2' in display_figure:
fig = go.Figure((go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines')))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='seaborn')
if 'Figure3' in display_figure:
fig = go.Figure((go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines')))
fig.add_traces(go.Scatter(name=y, x=df.index, y=df[y2], mode = 'lines'))
fig.update_layout(template='plotly_white')
# Aesthetics
fig.update_layout(margin= {'t':30, 'b':0, 'r': 0, 'l': 0, 'pad': 0})
fig.update_layout(hovermode = 'x')
fig.update_layout(showlegend=True, legend=dict(x=1,y=0.85))
fig.update_layout(uirevision='constant')
fig.update_layout(title = "Prices and predictions")
return(fig)
app.run_server(mode='external', port = 8005)
SUGGESTION 1
This suggestion will focus directly on:
I want to use plotly to display a graph only after a button is clicked
Which means that I don't assume that dcc.Loading() has to be a part of the answer.
I find that dcc.Checklist() is an extremely versatile and user-friendly component. And when set up correctly, it will appear as a button that has to be clicked (or an option that has to be marked) in order to trigger certain functionalities or visualizations.
Here's a basic setup:
dcc.Checklist(
id="display_columns",
options=[{"label": col + ' ', "value": col} for col in df.columns],
value=[df.columns[0]],
labelStyle={'display': 'inline-block', 'width': '12em', 'line-height':'0.5em'}
And here's how it will look like:
Along with, among other things, the following few lines, the dcc.Checklist() component will let you turn the Prediction trace on and off as you please.
# main trace
y = 'Prices'
fig = make_subplots(specs=[[{"secondary_y": True}]])
if 'Prices' in display_columns:
fig.add_trace(go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines'), secondary_y=False)
# prediction trace
if 'Predicted_prices' in display_columns:
fig.add_trace(go.Scatter(name = 'predictions', x=df.index, y=df['Predicted_prices'], mode = 'lines'), secondary_y=False
Adding to that, this setup will easily let you handle multiple predictions for multiple traces if you would like to extend this example further. Give it a try, and let me know how it works out for you. And if something is not clear, then we can dive into the details when you find the time.
Here's how the app will look like with and without Predictions activated:
OFF
ON
Complete code:
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_bootstrap_components as dbc
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
pd.options.plotting.backend = "plotly"
from datetime import datetime
palette = px.colors.qualitative.Plotly
# sample data
df = pd.DataFrame({'Prices': [1,10,7,5, np.nan, np.nan, np.nan],
'Predicted_prices':[np.nan, np.nan, np.nan, 5, 8,6,9]})
# app setup
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
# input controls
controls = dbc.Card(
[dbc.FormGroup(
[
dbc.Label("Options"),
dcc.Checklist(
id="display_columns",
options=[{"label": col + ' ', "value": col} for col in df.columns],
value=[df.columns[0]],
labelStyle={'display': 'inline-block', 'width': '12em', 'line-height':'0.5em'}
#clearable=False,
#multi = True
),
],
),
dbc.FormGroup(
[dbc.Label(""),]
),
],
body=True,
style = {'font-size': 'large'})
app.layout = dbc.Container(
[
html.H1("Button for predictions"),
html.Hr(),
dbc.Row([
dbc.Col([controls],xs = 4),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id="predictions")),
])
]),
]),
html.Br(),
dbc.Row([
]),
],
fluid=True,
)
#app.callback(
Output("predictions", "figure"),
[Input("display_columns", "value"),
],
)
def make_graph(display_columns):
# main trace
y = 'Prices'
fig = make_subplots(specs=[[{"secondary_y": True}]])
if 'Prices' in display_columns:
fig.add_trace(go.Scatter(name=y, x=df.index, y=df[y], mode = 'lines'), secondary_y=False)
# prediction trace
if 'Predicted_prices' in display_columns:
fig.add_trace(go.Scatter(name = 'predictions', x=df.index, y=df['Predicted_prices'], mode = 'lines'), secondary_y=False)
# Aesthetics
fig.update_layout(margin= {'t':30, 'b':0, 'r': 0, 'l': 0, 'pad': 0})
fig.update_layout(hovermode = 'x')
fig.update_layout(showlegend=True, legend=dict(x=1,y=0.85))
fig.update_layout(uirevision='constant')
fig.update_layout(template='plotly_dark',
plot_bgcolor='#272B30',
paper_bgcolor='#272B30')
fig.update_layout(title = "Prices and predictions")
return(fig)
app.run_server(mode='external', port = 8005)

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