Search code examples
pythonpandasjupyter-notebookplotlypython-multiprocessing

updating plotly figure [every several seconds] in jupyter


I'm new to the plotly python package and I've faced with such problem:
There is a pandas dataframe that is updating in a loop and I have to plot data from it with plotly.
At the beginning all df.response values are None and then it starts to fill it. Here is an example:
at the beginning
after it starts to fill
I want plotly to react this changes, but I don't know how to do it in the most "canonical" and simple way. (It would be great if data updating loop and plotly updating would work simultaneously, but if plotly will react every several seconds it would be fine too). I found some functions but don't exactly understand how they work:

import plotly.graph_objects as go
import cufflinks as cf
from plotly.offline import init_notebook_mode

init_notebook_mode(connected=True)
cf.go_offline()

fig = go.Figure(data=go.Heatmap(z=df.response,
                                x=df.currents,
                                y=df.frequencies))

fig.update_layout(datarevision=???) # This one

...

Solution

    • given you want to update as data arrives you need an event / interrupt handling approach
    • this example uses time as the event / interupt, a dash Interval
    • simulates more data by concatenating additional data to dataframe then updates the figure in the callback
    import plotly.graph_objects as go
    import numpy as np
    import pandas as pd
    from jupyter_dash import JupyterDash
    import dash_core_components as dcc
    import dash_html_components as html
    from dash.dependencies import Input, Output, State
    
    # initialise dataframe and figure
    df = pd.DataFrame({"response":[], "currents":[], "frequencies":[]})
    fig = go.Figure(data=go.Heatmap(z=df.response,
                                    x=df.currents,
                                    y=df.frequencies))
    
    
    
    # Build App
    app = JupyterDash(__name__)
    app.layout = html.Div(
        [
            dcc.Graph(id="heatmap", figure=fig),
            dcc.Interval(id="animateInterval", interval=400, n_intervals=0),
        ],
    )
    
    @app.callback(
        Output("heatmap", "figure"),
        Input("animateInterval", "n_intervals"),
        State("heatmap", "figure")
    )
    def doUpdate(i, fig):
        global df
        df = pd.concat([df, pd.DataFrame({"response":np.random.uniform(1,5,100), "currents":np.random.randint(1,20,100), "frequencies":np.random.randint(1,50,100)})])
    
        return go.Figure(fig).update_traces(go.Heatmap(z=df.response,
                                    x=df.currents,
                                    y=df.frequencies))
    
    
    # Run app and display result inline in the notebook
    app.run_server(mode="inline")