Dynamically adjusting Y axis of a graph produced by plotly.graph_objects - python

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.

Related

How to add a number of points on a current view of plotly scatterplot?

I have a plotly generated plot in python.
It can be zoomed or a specific region selected by window selection.
Is there any solution to calculate current number of points on current view of scatterplot?
E.g. initial screen gives us 1000 points, but when I zoom or using a window to choose any specific area - I want to see that this area includes only 100 points from initial scatterplot. Is it possible? Or maybe to get bounds from x-axis of a plot to use it in further dashboard - e.g. to calculate max/min/mean values for the points on the screen..
you clearly state dashboard hence assuming dash
zoom and pan result in relayoutDatacallback being triggered
this passes a dict which can be parsed for min/max x and y
code below shows this, filtering dataframe used to create scatter to get number of points
import dash
import plotly.express as px
from dash.dependencies import Input, Output, State
from jupyter_dash import JupyterDash
import numpy as np
import pandas as pd
r = np.random.RandomState(42)
# some data to plot
df = pd.DataFrame(
{"x-val": np.linspace(1, 100, 1000), "y-val": r.uniform(1, 100, 1000)}
)
fig = px.scatter(df, x="x-val", y="y-val")
app = JupyterDash(__name__)
app.layout = dash.html.Div(
[dash.dcc.Graph(id="graph", figure=fig), dash.html.Div(id="debug")]
)
# simple callback capture zoom and pan
#app.callback(Output("debug", "children"), Input("graph", "relayoutData"))
def figEvent(relayoutData):
r = relayoutData
# parse out min & max values displayed
rng = {
ax: [df[c].min(), df[c].max()]
if f"{ax}axis.range[0]" not in r.keys()
else [r[f"{ax}axis.range[0]"], r[f"{ax}axis.range[1]"]]
for ax, c in zip("xy", ["x-val", "y-val"])
}
# filter dataframe and get number of rows
n = df.loc[df["x-val"].between(*rng["x"]) & df["y-val"].between(*rng["y"])].shape[0]
return n
app.run_server(mode="inline", debug=True)

How to obtain Click Event Coordinate Data on Plotly Dash Graph in Python?

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

Is there a better way to use Jupyter IntSlider with Python Plotly?

In the following code block I use a Jupyter IntSlider to adjust the number of dots visualized in a Plotly express scatter 3d plot. The example already fits my use case, but I noticed that Plotly has built-in slider functionalities that could improve the performance.
As a Plotly beginner I find it quite hard to map the slider example from Plotly to my use case.
Any suggestions?
import numpy as np
import plotly.express as px
import pandas as pd
from ipywidgets import interact, widgets
NUM_DOTS = 100
NUM_DIMS = 3
random_data = pd.DataFrame(np.random.random((NUM_DOTS,NUM_DIMS) ), columns=['x_1','x_2','x_3'])
def update_plotly(x):
fig = px.scatter_3d(random_data[:x], x='x_1', y='x_2', z='x_3')
fig.show()
interact(update_plotly, x=widgets.IntSlider(min=1, max=NUM_DOTS, step=1, value=NUM_DOTS))
Actually it's not that hard to build the slider, just follow the path of the example shown by plotly:
import plotly.graph_objects as go
import numpy as np
NUM_DOTS = 100
NUM_DIMS = 3
# Create figure
fig = go.Figure()
# Add traces, one for each slider step
for step in np.arange(1, NUM_DOTS, 1):
#Random data
random_data = pd.DataFrame(np.random.random((step, NUM_DIMS)), columns=['x_1','x_2','x_3'])
fig.add_trace(
go.Scatter3d(
visible=False,
line=dict(color="#00CED1", width=6),
name="𝜈 = " + str(step),
z=random_data['x_3'],
x=random_data['x_1'],
y=random_data['x_2']))
# Make 10th trace visible
fig.data[10].visible = True
# Create and add slider
steps = []
for i in range(len(fig.data)):
step = dict(
method="restyle",
args=["visible", [False] * len(fig.data)],
)
step["args"][1][i] = True # Toggle i'th trace to "visible"
steps.append(step)
sliders = [dict(
active=10,
currentvalue={"prefix": "Frequency: "},
pad={"t": 50},
steps=steps
)]
fig.update_layout(
sliders=sliders
)
fig.show()
resulting:
or with more points:
As you correctly figured out, it is way more performant than the widget slider, because with this method, you just toggle the trace visibility in the 3D Scatter chart.

How to add more than one shape with loop in plotly

I use plotly package to show dynamic finance chart at python. However I didn't manage to put my all key points lines on one chart with for loop. Here is my code:
fig.update_layout(
for i in range(0,len(data)):
shapes=[
go.layout.Shape(
type="rect",
x0=data['Date'][i],
y0=data['Max_alt'][i],
x1='2019-12-31',
y1=data['Max_ust'][i],
fillcolor="LightSkyBlue",
opacity=0.5,
layer="below",
line_width=0)])
fig.show()
I have a data like below one. It is time series based EURUSD parity financial dataset. I calculated two constraits for both Local Min and Max. I wanted to draw rectangule shape to based on for each Min_alt / Min_ust and Max_alt / Max_range. I can draw for just one date like below image however I didn't manage to show all ranges in same plotly graph.
Here is the sample data set.
Here is the solution for added lines:
import datetime
colors = ["LightSkyBlue", "RoyalBlue", "forestgreen", "lightseagreen"]
ply_shapes = {}
for i in range(0, len(data1)):
ply_shapes['shape_' + str(i)]=go.layout.Shape(type="rect",
x0=data1['Date'][i].strftime('%Y-%m-%d'),
y0=data1['Max_alt'][i],
x1='2019-12-31',
y1=data1['Max_ust'][i],
fillcolor="LightSkyBlue",
opacity=0.5,
layer="below"
)
lst_shapes=list(ply_shapes.values())
fig1.update_layout(shapes=lst_shapes)
fig1.show()
However I have still problems to add traces to those lines. I mean text attribute.
Here is my code:
add_trace = {}
for i in range(0, len(data1)):
add_trace['scatter_' + str(i)] = go.Scatter(
x=['2019-12-31'],
y=[data1['Max_ust'][i]],
text=[str(data['Max_Label'][i])],
mode="text")
lst_trace = list(add_trace.values())
fig2=go.Figure(lst_trace)
fig2.show()
The answer:
For full control of each and every shape you insert, you could follow this logic:
fig = go.Figure()
#[...] data, traces and such
ply_shapes = {}
for i in range(1, len(df)):
ply_shapes['shape_' + str(i)]=go.layout.Shape()
lst_shapes=list(ply_shapes.values())
fig.update_layout(shapes=lst_shapes)
fig.show()
The details:
I'm not 100% sure what you're aimin to do here, but the following suggestion will answer your question quite literally regarding:
How to add more than one shape with loop in plotly?
Then you'll have to figure out the details regarding:
manage to put my all key points lines on one chart
Plot:
The plot itself is most likely not what you're looking for, but since you for some reason are adding a plot by the length of your data for i in range(0,len(data), I've made this:
Code:
This snippet will show how to handle all desired traces and shapes with for loops:
# Imports
import pandas as pd
#import matplotlib.pyplot as plt
import numpy as np
import plotly.graph_objects as go
#from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
# data, random sample to illustrate stocks
np.random.seed(12345)
rows = 20
x = pd.Series(np.random.randn(rows),index=pd.date_range('1/1/2020', periods=rows)).cumsum()
y = pd.Series(x-np.random.randn(rows)*5,index=pd.date_range('1/1/2020', periods=rows))
df = pd.concat([y,x], axis = 1)
df.columns = ['StockA', 'StockB']
# lines
df['keyPoints1']=np.random.randint(-5,5,len(df))
df['keyPoints2']=df['keyPoints1']*-1
# plotly traces
fig = go.Figure()
stocks = ['StockA', 'StockB']
df[stocks].tail()
traces = {}
for i in range(0, len(stocks)):
traces['trace_' + str(i)]=go.Scatter(x=df.index,
y=df[stocks[i]].values,
name=stocks[i])
data=list(traces.values())
fig=go.Figure(data)
# shapes update
colors = ["LightSkyBlue", "RoyalBlue", "forestgreen", "lightseagreen"]
ply_shapes = {}
for i in range(1, len(df)):
ply_shapes['shape_' + str(i)]=go.layout.Shape(type="line",
x0=df.index[i-1],
y0=df['keyPoints1'].iloc[i-1],
x1=df.index[i],
y1=df['keyPoints2'].iloc[i-1],
line=dict(
color=np.random.choice(colors,1)[0],
width=30),
opacity=0.5,
layer="below"
)
lst_shapes=list(ply_shapes.values())
fig.update_layout(shapes=lst_shapes)
fig.show()
Also you can use fig.add_{shape}:
fig = go.Figure()
fig.add_trace(
go.Scatter( ...)
for i in range( 1, len( vrect)):
fig.add_vrect(
x0=vrect.start.iloc[ i-1],
x1=vrect.finish.iloc[ i-1],
fillcolor=vrect.color.iloc[ i-1]],
opacity=0.25,
line_width=0)
fig.show()

Showing a simple matplotlib plot in plotly Dash

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 👍👍

Categories

Resources