I have 5-second data, and their grouped averages for different time intervals (1 minute, 1 hour, 1 day, 1 week, etc).
If the plot’s x-range is 1 year, I don’t want to load the original 5-second data (some 6 million lines) but i.e. the hour data (only 365x24 lines). The plot is 1000 pixel wide anyway, why to load all point if only one out of 6000 is plotted?
How to know the x-range of a plot after it has been zoomed in/out?
Here is a demo code to show both how to read back the zoom limits (using rangeslider’s relayout callback) and how to plot different figures according to the zoom factor. Perhaps it can be interesting for somebody.
# Use Perlin noise to create fake data to plot
# pip install noise
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import plotly.graph_objs as go
import pandas as pd
import numpy as np
import noise
def create_plot_data(start=0, end=1, periods=1000):
x_range = np.arange(start=start, stop=end, step=1 / periods)
y_values = [noise.pnoise1(x*30) for x in x_range]
return pd.DataFrame({"x": x_range, "y": y_values})
def generate_complete_figure(relayout_values):
# relayout_values are only for debug print
df = create_plot_data()
fig = go.Figure()
fig.add_trace(
go.Scatter(
x=df.x,
y=df.y,
)
)
fig.update_layout(
title="Complete Plot (relayout_values=" + str(relayout_values) + ")",
xaxis={"rangeslider_visible": True,
"uirevision": "complete_figure"}
)
return fig
def generate_detail_figure(relayout_values, date_start, date_end):
df = create_plot_data(start=date_start, end=date_end)
fig = go.Figure()
for i in range(10):
fig.add_trace(
go.Scatter(
x=df.x,
y=df.y + i/100,
name="trace " + str(i),
)
)
fig.update_layout(
title="Detail Plot (relayout_values=" + str(relayout_values) + ")",
plot_bgcolor="lavender",
xaxis={"uirevision": "detail_figure"}
)
return fig
app = dash.Dash()
app.layout = dcc.Graph(
id="plot_id",
)
def slider_event(relayout_values):
range = relayout_values.get("xaxis.range")
if range:
range_start = range[0]
range_end = range[1]
else:
range_start = relayout_values.get("xaxis.range[0]")
range_end = relayout_values.get("xaxis.range[1]")
if range_start and range_end is not None:
range_start = range_start
range_end = range_end
else:
range_start = 0 # if Autorange=True, we force the limits to the standard initial values
range_end = 1
range_delta = range_end - range_start
if range_delta < .25:
return generate_detail_figure(relayout_values, range_start, range_end)
else:
return generate_complete_figure(relayout_values)
app.callback(
Output('plot_id', 'figure'),
Input('plot_id', 'relayoutData'),
prevent_initial_call=True)(slider_event)
app.run_server(debug=True)
1 Like