Having trouble creating subplots with multiple plot types

I’ve scowled the internet and tried various ways to create a subplot with three scatterpolar plots and three bar plots.

I’ve come down to this…

Let’s say I have three dataframes that I filtered from another database to gives me data for students and how much they completed each category. We’ll use this one as an example of all three:

platform_df = pd.DataFrame({'category': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
                                  '%' : ["55","5","100","95","45","99","75","64","93","10"],
                                  'platform' : ['primer','primer','primer','primer','primer','primer','primer','primer', 'primer', 'primer']})

I have a function that allows me to create a bar plot with the color variations of the bars based on the grades:


grade_colors = RangeDict({
                            range(0,60): 'crimson',
                            range(60,70): '#E34363',
                            range(70,80): '#FFB733',
                            range(80,90): '#29CC92',
                            range(90,101): '#339933'})
def student_category_bar_grade(df,platform):
    df_filtered = df[df['platform']==platform].sort_values(by='Category',ascending=True)
    fig = go.Figure()
    for completed,sdf in df_filtered.groupby('completed'):
        print(sdf)
        color = grade_colors[completed]
        fig.add_traces(
            go.Bar(x=sdf['Category'],
                y=sdf['completed'],
                customdata=sdf['platform'],
                name=str(completed)+" %",
                marker={'color': color},
                hovertemplate="<br>".join([
                    "Platform: %{customdata}",
                    "Category: %{x}",
                    ]),
            )
        )
    # Change the bar mode
    fig.update_layout(title_text=platform)
    fig.update_layout(barmode='group'
                        )
                        
    fig.update_layout(showlegend=False) 
    fig.update_layout(
        title_x=0.5,
        title_font_size=26,
        title_font_family="Copperplate",
        title_font_color="teal",
    )
    return fig
def student_topic_scatter_polar_graph(df,platform):
    df_filtered = df[df['platform']==platform].sort_values(by='topic',ascending=True)
    color = colors[platform]
    print(color)
    fig = go.Scatterpolar(
                    r=df_filtered.completed,
                    theta=df_filtered.topic,
                    fill='toself',
                    name="%s - Focused Topics"%platform,
                    fillcolor=color, 
                    opacity=0.6,
                    line=dict(color=color)
    )

and I have a function for creating scatterpolar plots. Let’s use the same dataframe as an example:

def student_topic_scatter_polar_graph(df,platform):
    df_filtered = df[df['platform']==platform].sort_values(by='topic',ascending=True)
    color = colors[platform]
    print(color)
    fig = go.Scatterpolar(
                    r=df_filtered.completed,
                    theta=df_filtered.category,
                    fill='toself',
                    name="%s - Focused Categories"%platform,
                    fillcolor=color, 
                    opacity=0.6,
                    line=dict(color=color)
    )

    fig.update_layout(
    template=None,
    polar = dict(
        radialaxis = dict(range=[0, 100],
                    visible=True,)
    ),
    title="%s - Focused Categories"%platform)
    return fig



primer_category_bar_fig = student_category_bar_grade(students_category_earned,'Primer')

primer_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_category_earned,'Primer')
                

I started building the subplots like so:

            #create subplot
            student_subplot = make_subplots(rows=2, cols=3, 
                        specs=[
                        [{'type': 'scatterpolar'},{'type': 'scatterpolar'},{'type': 'scatterpolar'}],
                       [{'type': 'bar'},{'type': 'bar'},{'type': 'bar'}]])
            
            figure1_traces = []
            figure2_traces = []
            figure3_traces = []
            # combine the figs to the subplot
            for trace in range(len(primer_category_bar_fig ["data"])):
                figure1_traces.append(primer_category_bar_fig ["data"][trace])
            for trace in range(len(primer_category_bar_fig ["data"])):
                figure2_traces.append(primer_category_bar_fig ["data"][trace])
            for trace in range(len(primer_category_bar_fig ["data"])):
                figure3_traces.append(primer_category_bar_fig ["data"][trace])

            student_subplot.add_trace(primer_topic_scatter_polar_fig,row=1,col=1)
            student_subplot.add_trace(primer_topic_scatter_polar_fig,row=1,col=2)
            student_subplot.add_trace(primer_topic_scatter_polar_fig,row=1,col=3)
            student_subplot.add_trace(figure1_traces,row=2,col=1)
            student_subplot.add_trace(figure2_traces,row=2,col=2)
            student_subplot.add_trace(figure3_traces,row=2,col=3)
dbc.CardBody([
  html.H4("students stats", className="card-title"),
  dcc.Graph(figure=student_subplot),
  
])

But I can’t figure out what I’m doing wrong.

Can anyone please help?

Hi @jarelan

I did not understand, what you are trying to do, or where exactly you need help.

Please try posting your code as one block and make sure, that nothing is missing (functions, imports…) so that others who might help you can just copy&paste your code and execute it without getting an error (which is the case right now).

I am sorry for not having a clear example of what I’m dealing with. Here is my revamped effort.

NOTE: that this will give you an error about the ‘data’ property…I need to figure out how to get these six plots to go together in a subplot. I just can’t figure out how to do that combo. Could someone guide me a bit?


import pandas as pd 
pd.set_option('display.max_rows', None)
# Run this app with `python app.py`
from dash import Dash, dcc, html
#import plotly.express as px
from plotly.subplots import make_subplots
from plotly import graph_objects as go
import dash_bootstrap_components as dbc

import pandas as pd
Dash(assets_ignore='.*ignored.*')

app = Dash(__name__)

# neglect false warnings
pd.options.mode.chained_assignment = None  # default='warn'


# color dictionary 
class RangeDict(dict):
    def __getitem__(self, item):
        if not isinstance(item, range):
            for key in self:
                if item in key:
                    return self[key]
            raise KeyError(item)
        else:
            return super().__getitem__(item)

grade_colors = RangeDict({
                            range(0,60): 'crimson',
                            range(60,70): '#E34363',
                            range(70,80): '#FFB733',
                            range(80,90): '#29CC92',
                            range(90,101): '#339933'})

colors = {
    'background': '#111111',
    'text': 'teal',
    "0":"silver",
    "1":"#FBEC5D",
    "5":"#50C878",
    "10":"#40E0D0",
    "15":"#A23D60",
    0:"silver",
    1:"#FBEC5D",
    5:"#50C878",
    10:"#40E0D0",
    15:"#A23D60",
    'Linux':'#50C878',
    'Windows':'#66D3F4',
    'Primer':'#FFD700',
}

# pandas dataframe used for plots

students_category_earned = pd.DataFrame({
    'Category': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','A','A','B','B','C','C'],
    'completed' : [ 55 , 55 , 100 , 95 , 45 , 99 , 75 , 64 , 93 , 10 , 15 , 55 , 45 , 78 , 98 , 33 ],
    'platform' : ['primer','primer','primer','primer','primer','primer','primer','primer', 'primer', 'primer','Linux','Windows','Linux','Windows','Linux','Windows']
    })


print( students_category_earned )
print(" - "*9)
print(students_category_earned)

students_topics_earned = pd.DataFrame({
    'topic': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','A','A','B','B','C','C'],
    'completed' : [ 55 , 55 , 100 , 95 , 45 , 99 , 75 , 64 , 93 , 10 , 15 , 55 , 45 , 78 , 98 , 33 ],
    'platform' : ['primer','primer','primer','primer','primer','primer','primer','primer', 'primer', 'primer','Linux','Windows','Linux','Windows','Linux','Windows']
    })

print("students_topics_earned")
print("-"*9)
print(students_topics_earned)

# functions to creating the plots


def student_category_bar_grade(df,platform):
    df_filtered = df[df['platform']==platform].sort_values(by='Category',ascending=True)
    fig = go.Figure()
    for completed,sdf in df_filtered.groupby('completed'):
        color = grade_colors[completed]
        fig.add_traces(
            go.Bar(x=sdf['Category'],
                y=sdf['completed'],
                customdata=sdf['platform'],
                name=str(completed)+" %",
                marker={'color': color},
                hovertemplate="<br>".join([
                    "Platform: %{customdata}",
                    "Category: %{x}",
                    ]),
            )
        )
    # Change the bar mode
    fig.update_layout(title_text=platform)
    fig.update_layout(barmode='group',
                        plot_bgcolor=colors['background'],
                        paper_bgcolor=colors['background'],
                        font_color=colors['text']
                        )
                        
    fig.update_layout(showlegend=False) 
    fig.update_layout(
        title_x=0.5,
        title_font_size=26,
        title_font_family="Copperplate",
        title_font_color="teal",
    )
    return fig

               
def student_topic_scatter_polar_graph(df,platform):

    df_filtered = df[df['platform']==platform].sort_values(by='topic',ascending=True)
    color = colors[platform]
    
    fig = go.Scatterpolar(
                    r=df_filtered.completed,
                    theta=df_filtered.topic,
                    fill='toself',
                    name="%s - Focused Topics"%platform,
                    fillcolor=color, 
                    opacity=0.6,
                    line=dict(color=color)
    )

    return fig


# calling the plot functinos
linux_category_bar_fig = student_category_bar_grade(students_category_earned,'Linux')
windows_category_bar_fig = student_category_bar_grade(students_category_earned,'Windows')
primer_category_bar_fig = student_category_bar_grade(students_category_earned,'Primer')

linux_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,'Linux')
windows_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,'Windows')
primer_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,'Primer')
#create subplot
student_subplot =  make_subplots(rows=2, cols=3,
                    specs=[[{"type": "scatterpolar"}, {"type": "scatterpolar"}, {"type": "scatterpolar"}],
                        [{"type": "bar"}, {"type": "bar"}, {"type": "bar"}]]  )    
figure1_traces = []
figure2_traces = []
figure3_traces = []
# combine the figs to the subplot
for trace in range(len(linux_category_bar_fig["data"])):
    figure1_traces.append(linux_category_bar_fig["data"][trace])
for trace in range(len(windows_category_bar_fig["data"])):
    figure2_traces.append(windows_category_bar_fig["data"][trace])
for trace in range(len(primer_category_bar_fig["data"])):
    figure3_traces.append(primer_category_bar_fig["data"][trace])

student_subplot.add_trace(linux_topic_scatter_polar_fig,row=1,col=1)
student_subplot.add_trace(windows_topic_scatter_polar_fig,row=1,col=2)
student_subplot.add_trace(primer_topic_scatter_polar_fig,row=1,col=3)
student_subplot.add_trace(figure1_traces,row=2,col=1)
student_subplot.add_trace(figure1_traces,row=2,col=2)
student_subplot.add_trace(figure1_traces,row=2,col=3)

# creating card that the subplots will be in
card = dbc.Card(
                    [
                        dbc.CardBody([
                            html.H4("Student Name", className="card-title"),
                            dcc.Graph(figure=student_subplot),
                            
                        ])
                    ]
                )

app.layout = html.Div([
        card
    ],
)




if __name__ == '__main__':
    app.run_server(host="0.0.0.0", port=8070, debug=True) 

ALSO…the end result is supposed to look something like this…

image

sorry for the terrible depiction.

1 Like

Hi @jarelan

I did not fully understand how or why you wanted to use subplots so I changed your layout. Basically each figure is separated from each other. As you are using dash anyway I thought, I could do this :see_no_evil:

from dash import Dash, dcc, html, Output, Input
from plotly import graph_objects as go
import dash_bootstrap_components as dbc
import pandas as pd

pd.set_option('display.max_rows', None)

Dash(assets_ignore='.*ignored.*')

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

# neglect false warnings
pd.options.mode.chained_assignment = None  # default='warn'

# color dictionary
class RangeDict(dict):
    def __getitem__(self, item):
        if not isinstance(item, range):
            for key in self:
                if item in key:
                    return self[key]
            raise KeyError(item)
        else:
            return super().__getitem__(item)

grade_colors = RangeDict({
    range(0, 60): 'crimson',
    range(60, 70): '#E34363',
    range(70, 80): '#FFB733',
    range(80, 90): '#29CC92',
    range(90, 101): '#339933'})

colors = {
    'background': '#111111',
    'text': 'teal',
    "0": "silver",
    "1": "#FBEC5D",
    "5": "#50C878",
    "10": "#40E0D0",
    "15": "#A23D60",
    0: "silver",
    1: "#FBEC5D",
    5: "#50C878",
    10: "#40E0D0",
    15: "#A23D60",
    'Linux': '#50C878',
    'Windows': '#66D3F4',
    'Primer': '#FFD700',
}

# pandas dataframe used for plots

students_category_earned = pd.DataFrame({
    'Category': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'A', 'A', 'B', 'B', 'C', 'C'],
    'completed': [55, 55, 100, 95, 45, 99, 75, 64, 93, 10, 15, 55, 45, 78, 98, 33],
    'platform': ['primer', 'primer', 'primer', 'primer', 'primer', 'primer', 'primer', 'primer',
                 'primer', 'primer', 'Linux', 'Windows', 'Linux', 'Windows', 'Linux', 'Windows']
})

print(students_category_earned)
print(" - " * 9)
print(students_category_earned)

students_topics_earned = pd.DataFrame({
    'topic': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'A', 'A', 'B', 'B', 'C', 'C'],
    'completed': [55, 55, 100, 95, 45, 99, 75, 64, 93, 10, 15, 55, 45, 78, 98, 33],
    'platform': ['primer', 'primer', 'primer', 'primer', 'primer', 'primer', 'primer', 'primer',
                 'primer', 'primer', 'Linux', 'Windows', 'Linux', 'Windows', 'Linux', 'Windows']
})

print("students_topics_earned")
print("-" * 9)
print(students_topics_earned)


# functions to creating the plots
def student_category_bar_grade(df, platform):
    df_filtered = df[df['platform'] == platform].sort_values(by='Category', ascending=True)
    fig = go.Figure()
    for completed, sdf in df_filtered.groupby('completed'):
        color = grade_colors[completed]
        fig.add_trace(
            go.Bar(
                x=sdf['Category'],
                y=sdf['completed'],
                customdata=sdf['platform'],
                name=str(completed) + " %",
                marker={'color': color},
                hovertemplate="<br>".join([
                    "Platform: %{customdata}",
                    "Category: %{x}"]),
            ),
        )
    # Change the bar mode
    fig.update_layout(title_text=platform)
    fig.update_layout(barmode='group',
                      plot_bgcolor=colors['background'],
                      paper_bgcolor=colors['background'],
                      font_color=colors['text']
                      )

    fig.update_layout(showlegend=False)
    fig.update_layout(
        title_x=0.5,
        title_font_size=26,
        title_font_family="Copperplate",
        title_font_color="teal",
    )
    return fig


def student_topic_scatter_polar_graph(df, platform):
    df_filtered = df[df['platform'] == platform].sort_values(by='topic', ascending=True)
    color = colors[platform]

    fig = go.Scatterpolar(
        r=df_filtered.completed,
        theta=df_filtered.topic,
        fill='toself',
        name="%s - Focused Topics" % platform,
        fillcolor=color,
        opacity=0.6,
        line=dict(color=color)
    )

    return go.Figure(fig)


# ------------------------------------------------MAIN CHANGES
# create a grid of dcc.Graph(), rows x columns
rows = 2
columns = 3
content = []
component_ids = []
for r in range(rows):
    row_content = []
    for c in range(columns):
        component_id = f'row{r}_col{c}'
        component_ids.append(component_id)
        row_content.append(
            dbc.Col(
                dbc.Card(
                    children=dcc.Graph(id=component_id),
                )
            )
        )
    dbc_rows = dbc.Row(
        id=f'row{r}',
        children=row_content
    )
    content.append(dbc_rows)

# app layout
app.layout = html.Div(
    [
        html.H4("Student Name"),
        dbc.Container(
            id='container',
            children=content,
            fluid=True
        ),
        # dummy component for callback triggering at startup
        html.Div(id='dummy')
    ]
)


@app.callback(
    [Output(f'{cid}', 'figure') for cid in component_ids],
    Input('dummy', 'children')
)
def update(_):
    # calling the plot functions
    linux_category_bar_fig = student_category_bar_grade(students_category_earned, 'Linux')
    windows_category_bar_fig = student_category_bar_grade(students_category_earned, 'Windows')
    primer_category_bar_fig = student_category_bar_grade(students_category_earned, 'Primer')

    linux_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,
                                                                      'Linux')
    windows_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,
                                                                        'Windows')
    primer_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,
                                                                       'Primer')
    return [
        linux_topic_scatter_polar_fig,
        windows_topic_scatter_polar_fig,
        primer_topic_scatter_polar_fig,
        linux_category_bar_fig,
        windows_category_bar_fig,
        primer_category_bar_fig
    ]


if __name__ == '__main__':
    app.run_server(host="0.0.0.0", port=8070, debug=True)

which creates:

1 Like

That works too… thanks. The reason for hte subplot is that I am generating one of these on a page for every student listed from a dropbox. It has other parts to it but I need the instructors to have a way to see select students stats. I also got it figured out …

import pandas as pd 
pd.set_option('display.max_rows', None)
# Run this app with `python app.py`
from dash import Dash, dcc, html
#import plotly.express as px
from plotly.subplots import make_subplots
from plotly import graph_objects as go
import dash_bootstrap_components as dbc

import pandas as pd
Dash(assets_ignore='.*ignored.*')

app = Dash(__name__)

# neglect false warnings
pd.options.mode.chained_assignment = None  # default='warn'


# color dictionary 
class RangeDict(dict):
    def __getitem__(self, item):
        if not isinstance(item, range):
            for key in self:
                if item in key:
                    return self[key]
            raise KeyError(item)
        else:
            return super().__getitem__(item)

grade_colors = RangeDict({
                            range(0,60): 'crimson',
                            range(60,70): '#E34363',
                            range(70,80): '#FFB733',
                            range(80,90): '#29CC92',
                            range(90,101): '#339933'})

colors = {
    'background': '#111111',
    'text': 'teal',
    "0":"silver",
    "1":"#FBEC5D",
    "5":"#50C878",
    "10":"#40E0D0",
    "15":"#A23D60",
    0:"silver",
    1:"#FBEC5D",
    5:"#50C878",
    10:"#40E0D0",
    15:"#A23D60",
    'Linux':'#50C878',
    'Windows':'#66D3F4',
    'Primer':'#FFD700',
}

# pandas dataframe used for plots

students_category_earned = pd.DataFrame({
    'Category': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','A','A','B','B','C','C'],
    'completed' : [ 55 , 55 , 100 , 95 , 45 , 99 , 75 , 64 , 93 , 10 , 15 , 55 , 45 , 78 , 98 , 33 ],
    'platform' : ['Primer','Primer','Primer','Primer','Primer','Primer','Primer','Primer', 'Primer', 'Primer','Linux','Windows','Linux','Windows','Linux','Windows']
    })


print( students_category_earned )
print(" - "*9)
print(students_category_earned)

students_topics_earned = pd.DataFrame({
    'topic': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','A','A','B','B','C','C'],
    'completed' : [ 55 , 55 , 100 , 95 , 45 , 99 , 75 , 64 , 93 , 10 , 15 , 55 , 45 , 78 , 98 , 33 ],
    'platform' : ['Primer','Primer','Primer','Primer','Primer','Primer','Primer','Primer', 'Primer', 'Primer','Linux','Windows','Linux','Windows','Linux','Windows']
    })

print("students_topics_earned")
print("-"*9)
print(students_topics_earned)

# functions to creating the plots


fig = go.Figure()
def student_category_bar_grade(df,platform):
    df_filtered = df[df['platform']==platform].sort_values(by='Category',ascending=True)

    color = [grade_colors[x] for x in df_filtered['completed']]

    completed = ['{} %'.format(x) for x in df_filtered['completed']]
    data = go.Bar(x=df_filtered['Category'],
                 y=df_filtered['completed'],
                 customdata=df_filtered['platform'],
                 name=platform,
                 text=completed,
                 marker={'color': color},
                 hovertemplate="<br>".join([
                     "Platform: %{customdata}",
                     "Category: %{x}",
                 ]),
                 )

    return data

               
def student_topic_scatter_polar_graph(df,platform):

    df_filtered = df[df['platform']==platform].sort_values(by='topic',ascending=True)
    color = colors[platform]

    fig = go.Scatterpolar(
                    r=df_filtered.completed,
                    theta=df_filtered.topic,
                    fill='toself',
                    name="%s - Focused Topics"%platform,
                    fillcolor=color, 
                    opacity=0.6,
                    line=dict(color=color),
                    mode='markers'
    )
    return fig

# calling the plot functinos
linux_category_bar_fig = student_category_bar_grade(students_category_earned,'Linux')
windows_category_bar_fig = student_category_bar_grade(students_category_earned,'Windows')
Primer_category_bar_fig = student_category_bar_grade(students_category_earned,'Primer')

linux_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,'Linux')
windows_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,'Windows')
Primer_topic_scatter_polar_fig = student_topic_scatter_polar_graph(students_topics_earned,'Primer')
#create subplot
student_subplot =  make_subplots(rows=2, cols=3,
                    specs=[[{"type": "polar"}, {"type": "polar"}, {"type": "polar"}],
                        [{"type": "bar"}, {"type": "bar"}, {"type": "bar"}]])

student_subplot.add_trace(linux_topic_scatter_polar_fig,row=1,col=1)
student_subplot.add_trace(windows_topic_scatter_polar_fig,row=1,col=2)
student_subplot.add_trace(Primer_topic_scatter_polar_fig,row=1,col=3)

student_subplot.add_trace(linux_category_bar_fig,row=2,col=1)
student_subplot.add_trace(windows_category_bar_fig,row=2,col=2)
student_subplot.add_trace(Primer_category_bar_fig,row=2,col=3)

# creating card that the subplots will be in
card = dbc.Card(
                    [
                        dbc.CardBody([
                            html.H4("Student Name", className="card-title"),
                            dcc.Graph(figure=student_subplot),
                            
                        ])
                    ]
                )

app.layout = html.Div([
        card
    ],
)


if __name__ == '__main__':
    app.run_server(host="0.0.0.0", port=8070, debug=True)

I really do appreciate you helping me out AIMPED!

1 Like