Simple dash app. Yaxis autoscale on xaxis zoom in stock market plot

Finally done.
Yaxis is now automatically updated on the clientside which is very responsive (I kept the slower serverside implementation for reference).

Here is the code:

import dash
from dash.dependencies import Output, Input, State
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import numpy as np
import pandas as pd
import datetime

#some random values
a = datetime.datetime.today()
numdays = 100
dateList = []
for x in range (0, numdays):
    dateList.append(a - datetime.timedelta(days = x))
xy = [dateList,np.random.rand(100)]
df = pd.DataFrame(data=xy[1],columns=["y"],index=xy[0])


#Graph data
dataS = [dict(
    x = df.index,
    y = df['y'],
    name = 'meter',
    mode = 'lines'
)]

#Graph layout
layoutS = go.Layout(
    title="Meter",
    xaxis=dict(
        rangeslider_visible=True,
        rangeselector=dict(
            buttons=list([
                dict(count=1, label="1m", step="month", stepmode="backward"),
                dict(count=6, label="6m", step="month", stepmode="backward"),
                dict(count=1, label="YTD", step="year", stepmode="todate"),
                dict(count=1, label="1y", step="year", stepmode="backward"),
                dict(count=5, label="5y", step="year", stepmode="backward"),
                dict(step="all")
            ])
        )
    ),
    yaxis=dict(range=[0,2])
)

#Dash app layout
app = dash.Dash()
app.title = 'Random'

app.layout = html.Div(
    html.Div([
        html.H1(children='Random nums'),
        html.Div(children='''
            Rand rand rand.
        '''),
        
        dcc.Input(
            id='input-y',
            placeholder='Insert y value',
            type='number',
            value='',
        ),
        html.Div(id='result'),
        
        dcc.Graph(id='RandGraph',figure=dict(data=dataS,layout=layoutS))
    ])
)

#client side implementation
app.clientside_callback(
    """
    function(relOut, Figure) {
        if (typeof relOut !== 'undefined') {
            if (typeof relOut["xaxis.range"] !== 'undefined') {
                //get active filter from graph
                fromS = new Date(relOut["xaxis.range"][0]).getTime()
                toS = new Date(relOut["xaxis.range"][1]).getTime()
                
                xD = Figure.data[0].x
                yD = Figure.data[0].y
                
                //filter y data with graph display
                yFilt = xD.reduce(function (pV,cV,cI){
                    sec = new Date(cV).getTime()
                    if (sec >= fromS && sec <= toS) {
                        pV.push(yD[cI])
                    }
                    return pV
                }, [])
                
                yMax = Math.max.apply(Math, yFilt)
                yMin = Math.min.apply(Math, yFilt)
            } else { 
                yMin = Math.min.apply(Math, Figure.data[0].y)
                yMax = Math.max.apply(Math, Figure.data[0].y) 
            }
        } else { 
            yMin = Math.min.apply(Math, Figure.data[0].y)
            yMax = Math.max.apply(Math, Figure.data[0].y) 
        }
        Figure.layout.yaxis = {
            'range': [yMin,yMax],
            'type': 'linear'
        }
        return {'data': Figure.data, 'layout': Figure.layout};
    }
    """,
    Output('RandGraph','figure'),
    [Input('RandGraph','relayoutData')],[State('RandGraph', 'figure')]
)

#Server side implementation (slow)
#@app.callback(
#    Output('RandGraph','figure'),
#    [Input('RandGraph','relayoutData')],[State('RandGraph', 'figure')]
#)
#def update_result(relOut,Fig):
#    ymin = df.loc[relOut['xaxis.range'][1]:relOut['xaxis.range'][0],'y'].min()
#    ymax = df.loc[relOut['xaxis.range'][1]:relOut['xaxis.range'][0],'y'].max()
#    newLayout = go.Layout(
#        title="OL Meter",
#        xaxis=dict(
#            rangeslider_visible=True,
#            rangeselector=dict(
#                buttons=list([
#                    dict(count=0, label="1m", step="month", stepmode="backward"),
#                    dict(count=6, label="6m", step="month", stepmode="backward"),
#                    dict(count=1, label="YTD", step="year", stepmode="todate"),
#                    dict(count=1, label="1y", step="year", stepmode="backward"),
#                    dict(count=5, label="5y", step="year", stepmode="backward"),
#                    dict(step="all")
#                ])
#            ),
#            range=relOut['xaxis.range']
#        ),
#        yaxis=dict(range=[ymin,ymax])
#    )
#    
#    
#    Fig['layout']=newLayout
#    return Fig

if __name__ == '__main__':
    app.run_server(debug=False)
2 Likes