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 the ones below as an example of all three:
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=1,col=1)
student_subplot.add_trace(figure1_traces,row=1,col=2)
student_subplot.add_trace(figure1_traces,row=1,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)
but I keep getting an error about the 'data' property of the trace...I cannot figure it out.. could someone help me understand how to combine the two different types of plots in a 'make_subplot' ?
ALSO…the end result is supposed to look something like this…
sorry for the terrible depiction.
With respect to the data error, there are three possible causes: first, the string of the extraction condition simply does not match the string of the data frame. The data frame is 'primer' and the argument is 'Premir'. The second is that the bar chart is drawn grouped by achievement rate, which does not match the number of bars in the subplot. So it needs to be modified to get three graphs in the three platform conditions. third, I added a bar chart to fig in the function and then added more graphs outside the function. The modification is made to the graph data only. I have not addressed the decorations on the bar graph.
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 = JupyterDash()
app.layout = html.Div([
card
],
)
if __name__ == '__main__':
app.run_server(host="0.0.0.0", port=8070, debug=True)