How to plot multiple graphs/plots in a for loop in a Dash app?


#1

Here’s a simple example to demonstrate the issue. Say I want to plot an apple on the dash app. Here’s the code to do that:

import dash
import dash_core_components as dcc
import dash_html_components as html
import base64

app = dash.Dash()

app.layout = html.Div([
    dcc.Dropdown(
        id='name-dropdown',
        options=[
            {'label': 'Apple', 'value': 'Apple'},
            {'label': 'Watermelon', 'value': 'Watermelon'},
        ],
        placeholder="Select a fruit...",
    ),

    html.Div(id='output-div')
])


@app.callback(
    dash.dependencies.Output('output-div', 'children'),
    [dash.dependencies.Input('name-dropdown', 'value')])
def fruit_property_selection(fruit_value):
    if fruit_value=='Apple':
        image_filename = 'apple.jpg'   ### Have an image saved of an apple in the folder
    else:
        image_filename = 'watermelon.jpg' ### Have an image saved of a watermelon in the folder

    encoded_image = base64.b64encode(open(image_filename, 'rb').read())
    return html.Img(src='data:image/png;base64,{}'.format(encoded_image.decode()))


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

Now say I want to plot the same apple 10 times. I added a for loop before the return:

for _ in range(10):
        return html.Img(src='data:image/png;base64,{}'.format(encoded_image.decode()))

But it doesn’t work - it still shows only 1 image of the apple. So, how do I get it to show 10 apples (even better if the div is made scrollable to show the 10 apples)?


#2

You can return a list of html.div()s. Just append them to a list in the for loop and return that list.


#3

I did this. However, I just get a blank figure when returning the list of divs.

When I do the following:

for i in features:
    fig = func_to_generate_figure(x,y,z)
    return fig

It at least shows the first figure:
Capture

But when I do:

lis = []
for i in features:
    fig = func_to_generate_figure(x,y,z)
    lis.append(html.Div(fig))
return lis

it returns a blank figure:
Capture

I have also made the output div scrollable:
html.Div(id='output-div', style={'overflowY': 'scroll', 'height': '500'})

and it works on this toy Apple-Watermelon case, but it doesn’t work in my actual code which converts some matplotlib figures to plotly figures and plots them:

C:\Users\h473\AppData\Local\Programs\Python\Python35\lib\site-packages\plotly\matplotlylib\renderer.py:522: UserWarning:

Looks like the annotation(s) you are trying
to draw lies/lay outside the given figure size.

Therefore, the resulting Plotly figure may not be
large enough to view the full text. To adjust
the size of the figure, use the 'width' and
'height' keys in the Layout object. Alternatively,
use the Margin object to adjust the figure's margins.

Any idea how to fix this?


#5

func_to_generate_figure returns a plotly figure object. That’s why lis.append(html.Div(dcc.Graph(fig))) will not work, I think. I tried it anyway, and this time it plots the same blank figure, with the same errors and warnings.


#6

Sorry, I meant lis.append(html.Div(dcc.Graph(figure=fig)))


#8

@chriddyp Same result


#9

I have used something similar for my work and it works:

for i in range(0, n_plots):
    graph_init.append(html.Div(dcc.Graph(
        style={'width': '100%', 'height': '100%'},
        config={'displaylogo': False, 'editable': True, 'displayModeBar': False},
        id='graph-{}'.format(i),
        figure=dict(
            data=[dict(
                x=LAeq_all.iloc[:80000, 0], y=LAeq_all.iloc[:80000, 0], type='scattergl'
            )],
            layout=dict(
                title='Figure ' + str(i + 1) + ': Measured Sound Level Time History',
                font=dict(family='Arial, monospace', size=10),
                autosize=True,
                margin=dict(l=50, r=50, b=30, t=30, pad=0),
                yaxis=dict(title='Month', linewidth=1, mirror=True),
                xaxis=dict(title='Time', linewidth=1, mirror=True),
            ))
    ),
        style={'height': 'calc(100% / ' + str(n_plots) + ')'}

    ))

Are you sure that the ids of the figures in the loop are unique? You cannot have multiple graphs with the same id. It usually renders it blank in that case


#10

@vivekvs1 Thanks a lot. That was it, I had not given unique IDs to the output figures. Once I did that, it works fine.


#11

Note that as of 0.28.3 (https://github.com/plotly/dash-core-components/blob/master/CHANGELOG.md#0283---2018-09-07), id shouldn’t strictly be necessary.


#12

@chriddyp Oh, I see. I am on version 0.28.1


#13

I am trying to use this dash.append() method but it tells me that it doesn’t exist, has it been replaced with another name?


#14

I am new to Dash and I am encountering the similar issue, I need to plot multiple plots based on the number of values in a drop down list. Where would you above solution be used in callback or layout?


#15

in my code graph_init is just a python list and I am using the append() method on the list.


#16

In your case it might be better to initialize all the graphs in the layout and set the display style for the graphs to none/block depending on the dropdown option.


#17

yes! that’s a great solution!! I will try that out, but I have 100+ values in my dropdown initially and the number changes from 5 to 100 based on other components, it would be best if I can do a loop. anyways I will try it otherwise… I will post a more detailed version of my code here for better understanding on the issue at hand. thanks!