Nathan Thomas
Nathan Thomas

Reputation: 270

Periodically updating figures in plotly

Below is a simple script which retrieves live population data. It updates periodically and updates the plotly figure:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc


# Retrieve data
link = requests.get("https://countrymeters.info/en/World").text
table = pd.read_html(link)[0]
table = table
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')

# Dash app
app = dash.Dash(external_stylesheets=[dbc.themes.LUX])

# Bootstrap CSS
app.css.append_css({"external_url": "https://codepen.io/amyoshino/pen/jzXypZ.css"})
link = session.get("https://countrymeters.info/en/World").text

app.layout = html.Div(children=[
    html.H1("Population Data Scrape"),

    html.Div(children=
    '''
    A summary of the latest population data across the globe.
    '''     ),

    # Graph 1
    html.Div([
        dcc.Tabs([ #Tab 1
            dcc.Tab(label="Population Data", children=[

                html.Div([
                    dcc.Graph(
                        id = "Data",
                        figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
                            ),
                            dcc.Interval(
                                id="4secinterval",
                                interval="4000",
                                n_intervals=0
                            )], className = "six columns"),



                        ]),


                ])
            ])
        ])


# Update graph
@app.callback(Output("Data", "figure"),
            [Input("4secinterval", "n_intervals")])
def draw_figure(n):      
    test = session.get("https://countrymeters.info/en/World").text
    table = pd.read_html(test)[0]
    table = table
    figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
    return figure

if __name__ == "__main__":
    app.run_server(debug=False)

In the "update graph" section of my code, for the graph to update I have to call the web scrape again to retrieve the latest data and define it in a whole function. I've tried defining the function before and using:

@app.callback(Output("Data", "figure"),
            [Input("4secinterval", "n_intervals")])
draw_figure(n)

which I was hoping would just return the figure, however, this doesn't work. Is there a way in plotly/Dash to update the figure in a shorter way (i.e without having to scrape and format the data all over again)?

Upvotes: 2

Views: 1167

Answers (1)

Syamanthaka
Syamanthaka

Reputation: 1327

The catch here is in the dcc.Graph section. You are calling a global variable table.iplot() where table is defined as a global variable in your retrieve section.

Try to put all the functions in a separate file say `useful_functions.py'

def draw_graph():
  link = requests.get("https://countrymeters.info/en/World").text
  table = pd.read_html(link)[0]
  table = table
  figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
  return figure
the_graph = draw_graph()

Now, in your main file as above, remove the global declaration of table and figure. To display the graph, in your graph section call the draw_graph() function as:

 import useful_functions as uf
 <rest of the html app code>
 dcc.Graph(
   id = "graph_id",
   figure = uf.the_graph
),

This will call the graph for the first time on load. Now for the refresh bit, the callback would look like:

@app.callback(Output("Data", "figure"),
        [Input("4secinterval", "n_intervals")])
def draw_figure(n):
    fig = uf.draw_graph()
    return fig

Upvotes: 2

Related Questions