Black Lives Matter. Please consider donating to Black Girls Code today.

Scatter after zoom in/out: How to read x-range?

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)