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

How to enable automatic autoscale

I have a Dash app that creates a new timeseries plot based on the selection the user makes on a scatter plot. If the user clicks on a point on the scatterplot, the timeseries grabs new data and updates itself. The problem I am facing is that the autoscale is not automatically activated when a new timeseries is plotted and user has to click on the autoscale button after the replot.

Here is my code:

@app.callback(
dash.dependencies.Output(‘store-time-series’, ‘figure’),
[dash.dependencies.Input(‘item-dropdown’, ‘value’),
dash.dependencies.Input(‘scatter-plot’, ‘clickData’)]
)
def update_time_series(selected_item, clickData):
store = clickData[‘points’][0][‘customdata’]
title = 'store name = ’ + store
df = ro.get_demand_timeseries_at_loc(selected_item, store)
fdf = df.groupby(‘timestamp’)[‘LineUnits’].sum().reset_index()
return {
‘data’: [
go.Scatter(
x=fdf.timestamp,
y=fdf.LineUnits,
mode=‘lines’
)
],
‘layout’: go.Layout(
xaxis={‘title’: ‘Time’, ‘autorange’: ‘true’},
yaxis={‘title’: ‘Store Level Demand’, ‘autorange’: ‘true’},
annotations=[
{
‘text’: title,
‘xref’: ‘paper’,
‘yref’: ‘paper’,
‘showarrow’: False,
‘align’: ‘left’,
‘bgcolor’: ‘rgba(255, 255, 255, 0.5)’,
‘x’: 0,
‘y’: 0.85,
‘xanchor’: ‘left’,
‘yanchor’: ‘bottom’
}
],
hovermode=‘closest’
)
}

Any pointer to achieve this functionality will be appreciated.

1 Like

Having the same issue where my bar plot actually goes blank when inputs are changed. I also need to press the autoscale button to update the plot. Help would definitely be appreciated!

1 Like

I’m not sure if this is the issue, but this line looks off to me. It should be xaxis={'title': 'Time', 'autorange': True} and similarly for the next line.

However, autorange: True is the default value, so I’m not sure what’s going on in this case.

Can you create a small reproducable example of this?

Hey Chris,

Here’s an example:

import dash
from dash.dependencies import Input, Output, State, Event
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go

app = dash.Dash()

app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})

app.layout = html.Div([
	
	html.H3('Visualization'),
	html.Div([

		html.Label('Slider1'),
        dcc.Slider(
        id='slider1',
        min=0,
        max=50,
        value=10,
        step=1
        ),

        html.Label('Slider2'),
        dcc.Slider(
        id='slider2',
        min=0,
        max=50,
        value=10,
        step=1
        ),

        html.Div(id = 'slider1-show'),
        html.Div(id = 'slider2-show'),

        html.Hr(),

        dcc.Graph(id='test-figure', animate=True)

		])

	])

@app.callback(
    Output('slider1-show', 'children'),
    [Input('slider1', 'value')])

def deletion_show(slider1_val):
	return 'Slider1: ' + str(slider1_val)

@app.callback(
    Output('slider2-show', 'children'),
    [Input('slider2', 'value')])

def insertion_show(slider2_val):
	return 'Slider2: ' + str(slider2_val)


@app.callback(
    Output('test-figure', 'figure'),
    [Input('slider1', 'value'),
    Input('slider2', 'value')])

def visualize(slider1, slider2):

	data = [go.Bar(x=range(slider1), y=range(slider1)), go.Bar(x=range(slider2), y=range(slider2))]
	layout = go.Layout(title='test')

	figure = go.Figure(data = data, layout = layout)

	return figure

if __name__ == '__main__':
	app.run_server(debug = True)

chriddyp: Right, the autorange did not make any difference.

So I changed to animate=False and it works now haha

1 Like

Setting animate=False did the trick for me too. Now the autoscale is automatically turned on.

1 Like

I’m still struggling to get this to work. I’ve played with both Graph.animate and Layout.autorange and nothing seems to work.

My code works as follows. I have market data preloaded into a MongoDb database. I created a “Next data” button to allow me to iterate through each document/row of the database, so that I can walk forward bar-by-bar. The Graph should display the last 100 bars of the market data (I’m using collections.deque to do that). So clicking the “Next data” button should display the latest bar of data, and drop off the oldest.

When I first click “Next data” it works fine, but after clicking several more times I’m noticing that I’m getting old data bars still displayed (almost like its walking backwards). Eventually no bars show up since the Y-Axis is all messed. But I observed that clicking “Autoscale” in the upper right hand corner fixes it each time. So to get this to work I’m forced to click on “Autoscale” after each “Next data” click.

Any thoughts on how to solve?

Here’s my code:

# System imports
from collections import deque
import random
import time

# Flash imports
from flask import Flask

# MongoDb imports
from pymongo import MongoClient

# Plotly imports
import plotly.graph_objs as go
import plotly

# Dash imports
import dash
from dash.dependencies import Input, Output, Event
import dash_core_components as dcc
import dash_html_components as html


MaxBars = 100
Dts = deque(maxlen=MaxBars)
Open = deque(maxlen=MaxBars)
High = deque(maxlen=MaxBars)
Low = deque(maxlen=MaxBars)
Close = deque(maxlen=MaxBars)

server = Flask(__name__)

MONGODB = "mongodb"
MONGODB_PORT = 27017
Symbol = 'ES'

Client = MongoClient(MONGODB, MONGODB_PORT)
Collection = getattr(Client.historical, Symbol)
#Cursor = Collection.find()
dateFromString = {
   "$dateFromString": {
      "dateString": {
         "$concat": ["$Date", "$Time"]
      }
   }
}
project = {
   'dts': dateFromString,
   'Open': 1,
   'High': 1,
   'Low': 1,
   'Close': 1,
   'Volume': 1,
}

pipeline = [
   {"$project": project},
   {"$sort": {"dts": 1}},
]

startTime = time.time()
print("Loading data from MongoDb")
Cursor = Collection.aggregate(pipeline, allowDiskUse=True)
for _ in range(MaxBars):
   Bar = Cursor.next()
   Dts.append(Bar['dts'])
   Open.append(Bar['Open'])
   High.append(Bar['High'])
   Low.append(Bar['Low'])
   Close.append(Bar['Close'])
   print("{}".format(Bar))
print("Finished loading data from MongoDb (duration={:2.2f}s)".format(
      time.time() - startTime
))

app = dash.Dash(server=server)
app.layout = html.Div(
   [
      dcc.Graph(id='live-graph', animate=False),
      #dcc.Interval(id='graph-update', interval=1*1000),
      html.Button('Next data', id='button'),
   ]
)

@app.callback(
   output=Output('live-graph', 'figure'),
   inputs=[
      Input('button', 'n_clicks'),
   ]
   #state=
   #events=[
   #   #Event('graph-update', 'interval'),
   #]
)
def update_graph(n_clicks):
   print("Update graph")

   Bar = Cursor.next()
   Dts.append(Bar['dts'])
   Open.append(Bar['Open'])
   High.append(Bar['High'])
   Low.append(Bar['Low'])
   Close.append(Bar['Close'])
   print("{}".format(Bar))

   data = go.Candlestick(x=list(Dts),
                         open=list(Open),
                         high=list(High),
                         low=list(Low),
                         close=list(Close))

   layout = go.Layout(
      title="{}".format(Symbol),
      xaxis=dict(
      #   range=[min(Dts),max(Dts)],
         rangeslider=dict(visible=False)
      ),
      yaxis={
         #'title': 'Price ($)',
         'autorange': True,
      #   #range=[0,max(High)+100],
      },
   )

   return {
      'data': [data],
      'layout': layout,
   }


if __name__ == '__main__':
   print("Starting chart app")
   App.run_server(debug=True)

Thanks for reporting @moonlight16! I think this issue is related in particular to the Candlestick charts and how they work with Plotly.react. I’ll try to issue an intermediate fix for this this week, but the ultimate fix is going to be rewriting candlestick charts (happening here: https://github.com/plotly/plotly.js/issues/2510)

Working on a fix in https://github.com/plotly/dash-core-components/pull/183

Thanks @chriddyp, I appreciate the quick response.

Try upgrading with

pip install dash-core-components==0.22.1

Okay, I applied this patch. The “Next data” function is now working properly. I also tried the interval code (it was commented out in the post above), and it also worked properly! Thanks a bunch for providing the patch (and making it happen quickly)!!!

1 Like

I was also having this exact same problem!! Doing animate=False did the trick!

Either this feature looks actually really buggy or I did not understand how it works.
When providing the range explicitly for both x and y, without autorange nor animate the Scatter graph does not show the full Y axis as expected
When providing autorange instead of the explicit range, with animate, then the graph shifts little by little to the left (xaxis)

I had to disable both explicit range and animate, and enable autorange to have something working.

The change that @chriddyp implemented doesn’t seem to address the fact that animate=False works but animate=True doesn’t, is there a different issue/forum post that addresses this discrepancy? I’m just using simple Scatter objects, not Candlestick.

edit: I just found the post by @chriddyp: Animations in Dash which calls out the constraints of animate=True