Is it possible to show a simple matplotlib plot (the kind usually generated by plt.show()) in plotly's Dash framework? Or just plotly-like graphs with plotly's Scatters and Data traces?
Specifically I guess I need a different component than Graph (see below) and a way to return the simple plot in the update_figure function.
Example:
import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
import matplotlib.pyplot as plt
app = dash.Dash()
app.layout = html.Div(children=[
html.H1(children='Hello Dash'),
dcc.Slider(
id='n_points',
min=10,
max=100,
step=1,
value=50,
),
dcc.Graph(id='example') # or something other than Graph?...
])
#app.callback(
dash.dependencies.Output('example', 'figure'),
[dash.dependencies.Input('n_points', 'value')]
)
def update_figure(n_points):
#create some matplotlib graph
x = np.random.rand(n_points)
y = np.random.rand(n_points)
plt.scatter(x, y)
# plt.show()
return None # return what, I don't know exactly, `plt`?
if __name__ == '__main__':
app.run_server(debug=True)
Refer to https://plot.ly/matplotlib/modifying-a-matplotlib-figure/ . There is a mpl_to_plotly function in plotly.tools library that will return a plotly figure(which can then be returned to Graph's figure attribute) from matplotlib figure.
Edit: Just noticed you asked this a while back. Maybe the above is a new feature but its the cleanest way.
If you don't want an interactive plot, you can return a static one (found from this help)
import io
import base64
...
app.layout = html.Div(children=[
...,
html.Img(id='example') # img element
])
#app.callback(
dash.dependencies.Output('example', 'src'), # src attribute
[dash.dependencies.Input('n_points', 'value')]
)
def update_figure(n_points):
#create some matplotlib graph
x = np.random.rand(n_points)
y = np.random.rand(n_points)
buf = io.BytesIO() # in-memory files
plt.scatter(x, y)
plt.savefig(buf, format = "png")
plt.close()
data = base64.b64encode(buf.getbuffer()).decode("utf8") # encode to html elements
buf.close()
return "data:image/png;base64,{}".format(data)
UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail
in my case it works, despite the warning message 👍👍
Related
The graph above is created by using the following code.
import plotly.graph_objects as go
candlestick = go.Candlestick(x=data.index,
open=data.open,
high=data.high,
low=data.low,
close=data.close,
increasing_line_color='red',
decreasing_line_color='green')
fig = go.Figure(data=[candlestick])
fig.show()
After the graph is generated, I can change its X axis by clicking "Zoom in" and "Zoom out" buttons on the top right corner.
However, I can't find any way to change its Y axis. I mean, for example, I want to set the minimum value of Y axis to 175, and the maximum value of Y axis to 177.
Is it possible to dynamically change the range of Y axis AFTER the graph is generated?
I know I can use fig.update_yaxes(range=[175,177]) to update the range of Y axis. But running this command requires me to restart my program, which is very inconvenient.
The graph can be updated with fig.update_*. I will answer with an example from the official reference, where the y-axis is restricted.
import plotly.graph_objects as go
import pandas as pd
from datetime import datetime
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
fig = go.Figure(data=[go.Candlestick(x=df['Date'],
open=df['AAPL.Open'],
high=df['AAPL.High'],
low=df['AAPL.Low'],
close=df['AAPL.Close'])])
fig.update_yaxes(range=[100,120])
fig.show()
Example without y-axis restriction
I create the following solution based on this document.
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
import plotly.graph_objects as go
candlestick = go.Candlestick(x=data.index,
open=data.open,
high=data.high,
low=data.low,
close=data.close,
increasing_line_color='red',
decreasing_line_color='green')
fig = go.Figure(data=[candlestick])
def get_start_end_range(rng):
if rng:
if "xaxis.range" in rng.keys():
return rng['xaxis.range'][0], rng['xaxis.range'][1]
if 'xaxis.range[0]' in rng.keys():
return rng['xaxis.range[0]'], rng['xaxis.range[1]']
return None, None
app = Dash(__name__)
app.layout = html.Div(children=[
html.H1(children='Signals Graph'),
dcc.Graph(
id='signals-graph',
figure=fig
)
])
# Use callback to dynamically adjust the scale of Y axis
#app.callback(
Output(component_id="signals-graph", component_property="figure"),
Input(component_id="signals-graph", component_property="relayoutData"),
)
def scaleYaxis(rng):
print(f"rng: {rng}")
xstart, xend = get_start_end_range(rng)
if xstart != None:
try:
range_data = data.loc[
xstart : xend,
["high", "low", "open", "close"],
]
if len(range_data) > 0:
fig["layout"]["yaxis"]["range"] = [range_data['low'].min()*0.95, range_data['high'].max()*1.05]
except Exception as e:
print(f"Something wrong occured: {e}")
finally:
print(f"update x axis: {[xstart, xend]}")
fig["layout"]["xaxis"]["range"] = [xstart, xend]
return fig
app.run_server()
The solution above uses callback to dynamically adjust the scale of Y axis.
The above code would crate a server listening to port 8050 at 127.0.0.1.
You can visit http://127.0.0.1:8050 to see this graph.
I've tried navigating around the web for a solution to this problem, but have had no luck. The closest solution I found was this, however, it's in R and I've been trying to look for a solution in Python for this these past twos days.
I've already messed with Plotly Dash's documentation here on Interactive Graphing and the clickData and hoverData seems to only output coordinate data on actual data points on the graph that are clicked/hovered over on. My goal is obtain a pair of x,y coordinate through two mouse clicks on the graph, so that I am able to draw a line on the graph using that pair of coordinates. However, it seems Plotly's Dash Interactive Graphing is limited in its ability to provide mouse coordinate data that are not on actual data points. Is there any work around to this or an obvious solution / part of the documentation that I missed?
you can create an additional trace of transparent points that are scattered uniformly across the figure
then it's a simple case of using clickData callback
full code below
import numpy as np
import pandas as pd
import plotly.express as px
import math, json
import dash
from dash.dependencies import Input, Output, State
from jupyter_dash import JupyterDash
GS = 100
fig = px.line(
x=np.linspace(0, 1, 300), y=(np.sin(np.linspace(0, math.pi * 3, 300)) / 2) + 0.5
).add_traces(
px.scatter(
x=np.repeat(np.linspace(0, 1, GS), GS), y=np.tile(np.linspace(0, 1, GS), GS)
)
.update_traces(marker_color="rgba(0,0,0,0)")
.data
)
# Build App
app = JupyterDash(__name__)
app.layout = dash.html.Div(
[dash.dcc.Graph(id="graph", figure=fig), dash.html.Div(id="where")]
)
#app.callback(
Output("where", "children"),
Input("graph", "clickData"),
)
def click(clickData):
if not clickData:
raise dash.exceptions.PreventUpdate
return json.dumps({k: clickData["points"][0][k] for k in ["x", "y"]})
# Run app and display result inline in the notebook
app.run_server(mode="inline")
I'm currently trying to create a graph with plotly,
My goal would be to create a combined Barplot / Data table both controled with a range slider in order to controle the values with the date. I've succeded to create the barplot controled with the range slider.
I can't manage to control the table :/
Here is a combined plot but where the range slider is attached to the table, as you can see it does not control the date but the table view
https://plotly.com/~tristan1551/31/
Here is an exemple of a barplot i've done with a range slider https://plotly.com/~tristan1551/23/
Another idea would be to only to control the table with the ranger slider, i can't manager to do that too.
Is there a way to achive what i want to do ?
Thank you for your herlp :)
to synchronise a table with a range slider on a figure you can use a dash callback
below code creates a bar chart with a rangeslider
attaches a callback to changes in figure to get position of rangeslider
constructs table based on these inputs
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Input, Output, State
import pandas as pd
import numpy as np
import plotly.express as px
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/2014_apple_stock.csv")
df["AAPL_x"] = pd.to_datetime(df["AAPL_x"])
fig = px.bar(df, x="AAPL_x", y="AAPL_y").update_layout(
xaxis={
"range": [df["AAPL_x"].quantile(0.9), df["AAPL_x"].max()],
"rangeslider": {"visible": True},
}
)
# Build App
app = JupyterDash(__name__)
app.layout = html.Div(
[dcc.Graph(id="bargraph", figure=fig), html.Div(id="bartable", children=[])],
)
#app.callback(
Output("bartable", "children"),
Input("bargraph", "relayoutData"),
)
def updateTable(graphData):
global df
if graphData and "xaxis.range" in graphData.keys():
d1 = pd.to_datetime(graphData["xaxis.range"][0])
d2 = pd.to_datetime(graphData["xaxis.range"][1])
else:
d1 = df["AAPL_x"].quantile(0.9)
d2 = df["AAPL_x"].max()
dft = df.loc[df["AAPL_x"].between(d1, d2)]
return dash_table.DataTable(
columns=[{"name": c, "id": c} for c in dft.columns],
data=dft.to_dict("records"),
)
# Run app and display result inline in the notebook
app.run_server(mode="inline")
I've been trying to create a candlestick graph that shows the prices of NASDAQ and moving average on it, which has been a partial success:
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import random
import plotly.graph_objs as go
import yfinance as yf
import plotly.express as px
import datetime as dt
from datetime import datetime
from metody import metody
import time
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
NASDAQ = pd.read_excel(r'file.xlsx')
app = dash.Dash(external_stylesheets=external_stylesheets)
fig = go.Figure(data=go.Candlestick(
open=NASDAQ['Open'],
close=NASDAQ['Close'],
low=NASDAQ['Low'],
high=NASDAQ['High'],
))
close = NASDAQ['Close']
open = NASDAQ['Open']
srednia = metody.generateMovingAverage(NASDAQ['Close'], 3)
fig.add_trace(
go.Scatter(
y=srednia
)
)
app.layout = html.Div([
html.H1(
children="This is a chart of {}".format("NASDAQ"),
style={
'text-align': 'center'
}
),
dcc.Graph(
id='candles',
animate=True,
figure=fig,
),
dcc.Interval(
id='update',
interval=1000
)
])
app.run_server(debug=True)
unfortunately however when I'm trying to zoom the results, the candles do not upscale so that it's readable:
My question is: how do I deal with that? I'd love my chart to be nicely interactive (meaning the user can adjust the period and the candles are as big as it fits to the size of the chart).
PS: I'm really new to Dash, so if you've got any comments on my code or you know something I've done the wrong way round, please tell me :)
You need to get this into your code somewhere (upon zoom trigger):
fig.update_yaxes(range=[minY, maxY])
with minY and maxY being chosen from a downselection of your results (and rounded down and up respectively to look a bit better)
But I don't see where your zoom is being done - I assume you aren't using the plotly default but instead closing in using the lower summary bar?
You might have to dive in to the .css I'm afraid - or maybe you can insert additional lines into it before passing it to dash.Dash()
I am creating a dashboard in dash for a course at university. I created 3 histograms however, there are many unique values which give a long range of x values. In my plots I would like to show only the 10 or 20 values that have the highest count (top 10 values). Can someone help me out?
import plotly.express as px
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
# Build App
app = JupyterDash(__name__)
app.layout = html.Div([
html.H1("forensics "),
dcc.Graph(id='graph'),
dcc.Graph(id='graph1'),
dcc.Graph(id='graph2'),
html.Label([
"select market",
dcc.Dropdown(
id='market', clearable=False,
value='whitehousemarket', options=[
{'label': c, 'value': c}
for c in posts['marketextract'].unique()
])
]),
])
# Define callback to update graph
#app.callback(
Output('graph', 'figure'),
Output('graph1', 'figure'),
Output('graph2', 'figure'),
[Input("market", "value")]
)
def update_figure(market):
fig=px.histogram(x=posts['datetime'].loc[posts['marketextract']==market])
fig1=px.histogram(x=posts['username'].loc[posts['marketextract']==market])
fig2=px.histogram(x=posts['drugs'].loc[posts['marketextract']==market])
return [fig, fig1, fig2]
# Run app and display result inline in the notebook
app.run_server(mode='inline')
To my knowledge, px.histogram() does not have a method to exclude certain observations of bins. But judging by the look of your data (please consider sharing a proper sample), what you're doing here is just showing the different counts of some user names. And you can easily do that through a combination of df.groupby() and px.histogram. Or px.bar() or go.Bar() for that matter, but we'll stick with px.histogram since that is what you're seeking help with. Anyway, using random selections of country names from px.gapminder you can use:
dfg = df.groupby(['name']).size().to_frame().sort_values([0], ascending = False).head(10).reset_index()
fig = px.histogram(dfg, x='name', y = 'count')
And get:
If you drop .head(10) you'll get this instead:
And I hope this is the sort of functionality you were looking for. And don't be intimidated by the long df.groupby(['name']).size().to_frame().sort_values([0], ascending = False).reset_index(). I'm not a pandas expert, so you could quite possibly find a more efficient approach. But it does the job. Here's the complete code with some sample data:
# imports
import pandas as pd
import plotly.express as px
import random
# data sample
gapminder = list(set(px.data.gapminder()['country']))[1:20]
names = random.choices(gapminder, k=100)
# data munging
df = pd.DataFrame({'name':names})
dfg = df.groupby(['name']).size().to_frame().sort_values([0], ascending = False).reset_index()
dfg.columns = ['name', 'count']
# plotly
fig = px.histogram(dfg, x='name', y = 'count')
fig.layout.yaxis.title.text = 'count'
fig.show()