Search code examples
pythonhtmlplotlyplotly-dashdashboard

Python Dash: Fitting a table and a graph in one row of a grid


I am trying to fit a table and a graph in one single row of a grid. I have tried to resize both of them to fit by setting the style, but for some odd reason the table is placed beneath the graph.

Here is the code. What is it caused by?

from dash import Dash, dcc, html, Input, Output, no_update, dash_table
import plotly.express as px
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
import pandas as pd
from collections import OrderedDict

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


data = OrderedDict(
    [
        ("Date", ["2015-01-01", "2015-10-24", "2016-05-10", "2017-01-10", "2018-05-10", "2018-08-15"]),
        ("Region", ["Montreal", "Toronto", "New York City", "Miami", "San Francisco", "London"]),
        ("Temperature", [1, -20, 3.512, 4, 10423, -441.2]),
        ("Humidity", [10, 20, 30, 40, 50, 60]),
        ("Pressure", [2, 10924, 3912, -10, 3591.2, 15]),
    ]
)
df_a = pd.DataFrame(OrderedDict([(name, col_data * 10) for (name, col_data) in data.items()]))
df_a = df_a.iloc[0:20].copy()


df_b = px.data.gapminder().query("year == 2007")

dropdown_country_a = dcc.Dropdown(
    id="dropdown-a",
    options=df_b.country,
    value="Turkey",
)

dropdown_country_b = dcc.Dropdown(
    id="dropdown-b",
    options=df_b.country,
    value="Canada"
)

f1 = go.Figure(go.Bar(x=["a", "b", "c"], y=[2, 3, 1], marker_color="Gold"))
f2 = go.Figure(go.Bar(x=["a", "b", "c"], y=[2, 3, 1], marker_color="Gold"))
f3 = go.Figure(go.Bar(x=["a", "b", "c"], y=[2, 3, 1], marker_color="Gold"))

aa = {'width': '40%', 'display': 'inline-block'}

app.layout = html.Div([
    html.H1("USD", style={'textAlign': 'center'}),
    html.Div(children=[
        html.Div([dcc.Graph(id="1", figure=f1, style=aa), dcc.Graph(id="2", figure=f2, style=aa)]),
        html.Div([
        
        html.Div([
        dash_table.DataTable(id="table", data=df_a.to_dict('records'), columns=[{"name": i, "id": i} for i in df_a.columns])], style=aa),
        
        html.Div([dbc.Container([
            dbc.Row(dbc.Col([dropdown_country_a, dropdown_country_b], lg=6, sm=12)),
            dbc.Row(dbc.Col(dcc.Graph(id="asd"), lg=6, sm=12))])], style=aa)
    ])
    ])
])



# Callback for line_geo graph
@app.callback(
    Output("asd", "figure"),
    Input("dropdown-a", "value"),
    Input("dropdown-b", "value"),
)
def make_line_geo_graph(country_a, country_b):
    dff = df_b[df_b.country.isin([country_a, country_b])]

    fig = px.line_geo(
        dff,
        locations="iso_alpha",
        projection="orthographic",
    )

    fig_locations = px.line_geo(
        dff, locations="iso_alpha", projection="orthographic", fitbounds="locations"
    )

    fig.update_traces(
        line_width=3,
        line_color="red",
    )

    fig_locations.update_traces(
        line_width=3,
        line_color="red",
    )

    return fig


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

Solution

  • For better control of the components of your Dash app, you should consider using dbc.Col components within dbc.Row() components. If you exchange your:

    app.layout = html.Div([
        html.H1("USD", style={'textAlign': 'center'}),
        html.Div(children=[
            html.Div([dcc.Graph(id="1", figure=f1, style=aa),
                     dcc.Graph(id="2", figure=f2, style=aa)]),
            html.Div([
    
                html.Div([
                    dash_table.DataTable(id="table", data=df_a.to_dict('records'), columns=[{"name": i, "id": i} for i in df_a.columns])], style=aa),
    
                html.Div([dbc.Container([
                    dbc.Row(
                        dbc.Col([dropdown_country_a, dropdown_country_b], lg=6, sm=12)),
                    dbc.Row(dbc.Col(dcc.Graph(id="asd"), lg=6, sm=12))])], style=aa)
            ])
        ])
    ])
    

    With:

    app.layout = html.Div([
        html.H1("USD", style={'textAlign': 'center'}),
        html.Div(children=[
            html.Div([dcc.Graph(id="1", figure=f1, style=aa),
                     dcc.Graph(id="2", figure=f2, style=aa)]),
            dbc.Row([dbc.Col([dropdown_country_a, dropdown_country_b], lg=6, sm=12)]),
            dbc.Row([dbc.Col([dash_table.DataTable(id="table", data=df_a.to_dict(
                'records'), columns=[{"name": i, "id": i} for i in df_a.columns])]), dbc.Col([dcc.Graph(id="asd")])])
    
        ])
    ])
    

    Then you'll get the following setup:

    enter image description here

    From here you can tweak the layout of your app with the width attribute of your dbc.Col components.

    Complete code:

    from dash import Dash, dcc, html, Input, Output, no_update, dash_table
    import plotly.express as px
    import dash_bootstrap_components as dbc
    import plotly.graph_objects as go
    import pandas as pd
    from collections import OrderedDict
    
    app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
    
    
    data = OrderedDict(
        [
            ("Date", ["2015-01-01", "2015-10-24", "2016-05-10",
             "2017-01-10", "2018-05-10", "2018-08-15"]),
            ("Region", ["Montreal", "Toronto", "New York City",
             "Miami", "San Francisco", "London"]),
            ("Temperature", [1, -20, 3.512, 4, 10423, -441.2]),
            ("Humidity", [10, 20, 30, 40, 50, 60]),
            ("Pressure", [2, 10924, 3912, -10, 3591.2, 15]),
        ]
    )
    df_a = pd.DataFrame(OrderedDict([(name, col_data * 10)
                        for (name, col_data) in data.items()]))
    df_a = df_a.iloc[0:20].copy()
    
    
    df_b = px.data.gapminder().query("year == 2007")
    
    dropdown_country_a = dcc.Dropdown(
        id="dropdown-a",
        options=df_b.country,
        value="Turkey",
    )
    
    dropdown_country_b = dcc.Dropdown(
        id="dropdown-b",
        options=df_b.country,
        value="Canada"
    )
    
    f1 = go.Figure(go.Bar(x=["a", "b", "c"], y=[2, 3, 1], marker_color="Gold"))
    f2 = go.Figure(go.Bar(x=["a", "b", "c"], y=[2, 3, 1], marker_color="Gold"))
    f3 = go.Figure(go.Bar(x=["a", "b", "c"], y=[2, 3, 1], marker_color="Gold"))
    
    aa = {'width': '40%', 'display': 'inline-block'}
    
    # app.layout = html.Div([
    #     html.H1("USD", style={'textAlign': 'center'}),
    #     html.Div(children=[
    #         html.Div([dcc.Graph(id="1", figure=f1, style=aa),
    #                  dcc.Graph(id="2", figure=f2, style=aa)]),
    #         html.Div([
    
    #             html.Div([
    #                 dash_table.DataTable(id="table", data=df_a.to_dict('records'), columns=[{"name": i, "id": i} for i in df_a.columns])], style=aa),
    
    #             html.Div([dbc.Container([
    #                 dbc.Row(
    #                     dbc.Col([dropdown_country_a, dropdown_country_b], lg=6, sm=12)),
    #                 dbc.Row(dbc.Col(dcc.Graph(id="asd"), lg=6, sm=12))])], style=aa)
    #         ])
    #     ])
    # ])
    
    app.layout = html.Div([
        html.H1("USD", style={'textAlign': 'center'}),
        html.Div(children=[
            html.Div([dcc.Graph(id="1", figure=f1, style=aa),
                     dcc.Graph(id="2", figure=f2, style=aa)]),
            dbc.Row([dbc.Col([dropdown_country_a, dropdown_country_b], lg=6, sm=12)]),
            dbc.Row([dbc.Col([dash_table.DataTable(id="table", data=df_a.to_dict(
                'records'), columns=[{"name": i, "id": i} for i in df_a.columns])]), dbc.Col([dcc.Graph(id="asd")])])
    
        ])
    ])
    
    
    # Callback for line_geo graph
    @app.callback(
        Output("asd", "figure"),
        Input("dropdown-a", "value"),
        Input("dropdown-b", "value"),
    )
    def make_line_geo_graph(country_a, country_b):
        dff = df_b[df_b.country.isin([country_a, country_b])]
    
        fig = px.line_geo(
            dff,
            locations="iso_alpha",
            projection="orthographic",
        )
    
        fig_locations = px.line_geo(
            dff, locations="iso_alpha", projection="orthographic", fitbounds="locations"
        )
    
        fig.update_traces(
            line_width=3,
            line_color="red",
        )
    
        fig_locations.update_traces(
            line_width=3,
            line_color="red",
        )
    
        return fig
    
    
    if __name__ == "__main__":
        app.run_server(debug=True)