Help with displaying two graphs in 1 row, 2 columns

Hi all,
First of all thanks a ton for building a great framework for creating web apps!

I have created an app for exploratory data analysis that displays multiple charts. At this moment, each dcc.Graph occupies the entire width of the screen. In order to make things more compact, the end goal would be to display horizontally two charts.

I encapsulated each graph in a html.Div component. One of them also contains additional dcc components (markdown text and dropdown):

        html.Div([
            #Boxplot
            html.Div([
                dcc.Markdown('Select grouping variable'),
                dcc.Dropdown(
                    id='group_var',
                    options=[{'label':i, 'value':i} for i in feats_cat],
                    value='GB'),
                dcc.Graph(id='var_boxplot')], 
            style={'width': '48%', 'display': 'inline-block'}),
            
            html.Div([
                #Step change cuts 
                dcc.Graph(id='var_stepchanges')],
            style={'width': '48%', 'align': 'right', 'display': 'inline-block'})
                ]),

The result looks like this:

On the other hand, if I comment out the line
dcc.Markdown(ā€˜Select grouping variableā€™),

the result looks like this:

For the final product, I want the markdown text to be present and have the charts next to each other horizontally.

Two questions:

  • any suggestions on how to fix this behaviour?
  • would you go for a better approach than encapsulating the graphs in html.Div blocks?

Thank you so much

Would you consider using subplots? There are built-in functions to have multiple plots displayed in any configuration of rows and columns.

thanks @hobobot for your reply.
we considered initially using the subplots feature from Plotyly but it had some caveats.

The main reason is that we want a modular application where each Graph lives on its own. This gives us the flexibility to

  • more easily adjust figure properties
  • reuse same graph component in various places across the dashboard
  • add other dcc components (such as dropdown for selecting variables) to individual charts. This wouldnā€™t be that easy/possible if embedding the plots in subplots.

Encapsulating the graphs is step 1. Step 2 would be to include
style = { ā€˜columnCountā€™: 2} inside the outer html.Div
This property drives the number of columns.

1 Like

would you go for a better approach than encapsulating the graphs in html.Div blocks?

You could take a look at dash-bootstrap-components (full disclosure, Iā€™m one of the developers working on it) which has Row and Col components that make layouts like this much easier to build. Hereā€™s the docs for the layout components.

In your case you would just replace your outer html.Div with dbc.Row and the two inner html.Divā€™s with dbc.Col.

The main bootstrap stylesheet adds a lot of styles and classes in addition to the layout options. If you just want to use the grid without modifying the typography or anything you can link only the bootstrap-grid css. Either download it from github and add it to your assets folder, or link from a CDN such as https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap-grid.min.css.

1 Like

If you change the dcc.Markdown to a simple html.P for your text, does the problem persist?

thanks all for the great tips and tricks!

for the time being I chose the solution from @giodcruz95 (easiest to implement)

Iā€™m also investigating the nice approach suggested by @tcbegley. However with the changes suggested by the author:

        dbc.Row([
            #Boxplot
            dbc.Col([
                dcc.Markdown('Select grouping variable'),
                dcc.Dropdown(
                    id='group_var',
                    options=[{'label':i, 'value':i} for i in feats_cat],
                    value='GB'),
                dcc.Graph(id='var_boxplot')
                    ]), 
            
            dbc.Col([
                #Step change cuts 
                dcc.Graph(id='var_stepchanges')
                    ]),
                ]),

unfortunately I get the two plots now in two rows (i.e. below each other).
And where exactly can I find the assets folder?
Thanks a lot!

Hey @ElChico

Did you link to a Bootstrap stylesheet? If not then the rows and columns wonā€™t work. dash-bootstrap-components uses Bootstrap CSS for styling and layout, but this is not included automatically with the Python package, you have to link it yourself. The reason for not including it automatically is because Bootstrap is highly customisable, there are many free themes online and people compile their own versions. We wanted to allow people to use dash-bootstrap-components with any of these sheets.

Fortunately linking is very simple. The easiest way is do add the following when you initialise your app object

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

This will link the standard Bootstrap stylesheet and the rows and columns should work. As I mentioned above, this will make some stylistic changes to the fonts and spacing etc. If you donā€™t want those, you can use the Bootstrap grid css on its own:

app = dash.Dash(external_stylesheets=[
    "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap-grid.min.css"
])

or as I mentioned in my previous post you can download the file directly from GitHub and place it in a folder called assets in the same directory as your main python file. Dash will automatically detect and link any css you put in this folder. See the Dash docs for details.

If you already did this, or if you try it and it still doesnā€™t work let me know.

Hi all, Iā€™m having trouble implementing this with the dash-bootstrap-components method as described by @tcbegley, and wondering if anyone can spot anything that Iā€™ve done wrong.

Hereā€™s my use case which returns a ā€œTypeError: unsupported format string passed to Col.formatā€ ā€“ Iā€™m not sure what this error means in this context and am stuck. Thanks in advance for any help!

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.figure_factory as ff
import dash_bootstrap_components as dbc

bullet_data = pd.read_json(ā€˜https://cdn.rawgit.com/plotly/datasets/master/BulletData.jsonā€™)

app = dash.Dash(name, external_stylesheets=[dbc.themes.BOOTSTRAP])

server = app.server

app.layout = html.Div(children=[
html.H1(children=ā€˜Web Appā€™),

html.Div([
    dbc.Row(
        dbc.Col(html.Div([
            dcc.Graph(id='bullet_chart',
                      figure=ff.create_bullet(
                          bullet_data,
                          ranges='ranges',
                          measures='measures',
                          title=None,
                          autosize=True,
                      )
                      ),
            ]), width={'size':3}  # this column is 3 units of the page width
        ),

        dbc.Col(html.Div([
            ]), width={'size':3}  # this column is 3 units of the page width
        ),
    ),
]),

])

if name == ā€˜mainā€™:
app.run_server(debug=True)

hey @jonathan.scott

You have

dbc.Row(dbc.Col(...), dbc.Col(...))

when you want

dbc.Row([dbc.Col(...), dbc.Col(...)])

i.e., if you have multiple children you need to pass them inside a list. The confusing error is because the second Col is being passed as the id argument of Row and Dash is trying to use string methods on it which are failing.

1 Like

Oh I see! Thanks so much @tcbegley for your quick reply, it works now. I think my only remaining issue is getting the figure_factory graph to resize to fit inside of the div. I was thinking that the ā€œautosize=Trueā€ part would accomplish this but I may be mistaken. If I attempt to put two of the ff.create_bullet() graphs side-by-side in the two dcb.Col containers, they overlap, like this:

Sorry if this is a simple problem ā€“ Iā€™m new to Dash & Plotly ā€“ and maybe this should be posted in a different place, but any suggestions? Thanks again.
Jonathan

Hey @jonathan.scott,

I took a look at this, and I found that figure_factory is setting the width of the figure to be 1000px, which is causing the overflow.

I managed to fix it by setting width and height to be None. E.g.

dcc.Graph(
    id="bullet_chart",
    figure=ff.create_bullet(
        bullet_data,
        ranges="ranges",
        measures="measures",
        title=None,
        width=None,
        height=None,
    ),
)
1 Like

Brilliant, many thanks again @tcbegley. Iā€™m not sure I would have found that solution, I appreciate it!

1 Like

Hi, I encounter a similar problem of not being able to obtain 2 columns, and I canā€™t spot the errors in my codes. The contents appear one on top of the other, instead of side-by-side. Will appreciate any help. Thanks.

My codes are:

import dash
import dash_core_components as dcc
import dash_html_components as html 
import dash_bootstrap_components as dbc 

body = dbc.Container(
    [dbc.Row(
        [dbc.Col(
            [
                html.H2("Heading"),
                html.P(
                    """\
                        This is supposed to be a paragraph of text. A large chunk of text."""
                ),
                dbc.Button("Show more", color="secondary"),],
                md=4,
        ),
        dbc.Col(
            [
                html.H2("Graph"),
                dcc.Graph(
                    figure={"data": [{"x": [1,2,3], "y": [1,4,9]}]}
                ),
            ]
        ),]
    )],
    className="mt-4"
)

def Homepage():
    layout = html.Div([
        body
    ])
    return layout 

app = dash.Dash(__name__, external_stylesheets = [dbc.themes.UNITED])
app.layout = Homepage()

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

I tried to simplify the body to the codes below but still couldnā€™t get 2 columns.

body = html.Div(
    [
    dbc.Row(dbc.Col(html.Div("A single column"))),
    dbc.Row(
        [
            dbc.Col(html.Div("Heading1")),
            dbc.Col(html.Div("Heading2")),
        ]
    ),
])

Thereā€™s two things you can try:

  1. You havenā€™t set the width of the second column, which means the column will be sized according to its contents. Since the second column contains a graph which likes to expand to fill space, the column will tend to like to expand and move onto a new line. Set the width of the second column manually, maybe with md=8.
  2. The md=4 argument only sets the width of the column on a medium sized screen or larger. If youā€™re viewing your app on a small screen it will have no effect. To fix that you can use the width argument in place of md. So
dbc.Row([dbc.Col(..., width=4), dbc.Col(..., width=8)])

Let me know if that helps!

Hi @tcbegley,

Thank you for the suggestion.

I tried width=6, and then width=3, on both versions of the body separately and both didnā€™t work. The width did not change. A colleague recommended that I add in a line of code before closing the parenthesis for dbc.Container

style=dict(display="inline-block")

With the above line, the correct width is displayed, however, the contents are still one on top of another, not side-by-side.

Any other aspects I should consider?

Hi @tcbegley,

My colleague mentioned that the 2 columns are not appearing because Iā€™m running the codes on a computer that does not have access to the internet. Do you think that could be the cause for the display issues?

I will search for resources for using dash_bootstrap_components offline. Let me know if you have any good recommendations.

Thanks!

Ah ok, that will be stopping you from linking to Bootstrap CSS which is why itā€™s not working.

Download the CSS instead and put it in your assets folder so that it gets served locally and everything should work.

Yes, that works. Thank you very much!