Search code examples
plotlymissing-dataplotly-dashplotly-python

Signal missing data to user


I have a line chart where the user can select the x range. A callback then requests data for the given x range from a database and displays it.

However, there may not be any data for the chosen x range, resulting in an empty graph, which looks a bit clumsy. What would be a good way to signal the user that there is no data for the selected x range?

Here is a small example I put together. Selecting some x range between 4 and 11 (exlusive) by drag-selection on the graph will result in an empty graph.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go

app = dash.Dash()
app.layout = html.Div([dcc.Graph(id='my-graph'), dcc.Store(id='my-data')])

@app.callback(Output('my-data', 'data'),
                    Input('my-graph', 'relayoutData'),)
def update_data(relayout_data):
    start, end = 1, 14
    if isinstance(relayout_data, dict):
        if 'xaxis.range[0]' in relayout_data and 'xaxis.range[1]' in relayout_data:
            start = relayout_data['xaxis.range[0]']
            end   = relayout_data['xaxis.range[1]']
    return pull_data(start, end)

def pull_data(start, end): # imagine this being a DB request or similar
    x = [1,2,3,4,11,12,13,14]
    x = [i for i in x if start <= i <= end]
    y = [i**2 for i in x]
    return [x,y]

@app.callback(Output('my-graph', 'figure'),
                    Input('my-data', 'data'),)
def update_graph(data):
    print("selected data:", data)
    x, y = data
    fig = go.Figure(data=go.Scatter(x=x, y=y))
    fig.update_yaxes(fixedrange=True)
    if not x:
        pass # what could I do here?
    return fig

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

Solution

  • One way I found for now is to add a very visible annotation to inform the user about the missing data, like so:

    if not x:
        fig.add_annotation(text='No data in chosen x range',
            xref='paper', yref='paper', x=0.5, y=0.5, showarrow=False,
            font=dict(size=24), bgcolor="rgba(255,127,14,0.5)",)
    

    The exact position (x and y), the font size and the bgcolor can be adjusted of course. One could also use the visible argument of fig.add_annotation for further control.

    It's still a bit clumsy for the example chosen in the question, because the x axis range won't be accurate, but at least the user is told what is going on.

    Edit: Having an accurate x axis range was important for me in a later use case, where the x axis was of type date. Luckily, that's easy to set manually if the start and end of the chosen range are also stored, like so: fig.update_xaxes(range=[start,end])