Reputation: 555
I understand that if I want to set all of the subplot titles then I can do that when I declare the figure.
import pandas as pd
import plotly.graph_objs as go
from plotly.subplots import make_subplots
fig = make_subplots(
###
subplot_titles=['Plot 1', 'Plot 2', 'Plot 3', 'Plot 4', etc.]
)
However, I create each subplot graph within a loop, and I think it would be easiest to set each subplot's title during that process. In my case, I created a groupby object based on date, and then I loop through each group (date) dataframe. Each dataframe is used to create the subplot graph, and I'd like the title to be the date. I understand that I can figure this out before the loop and set the subplot_titles as normal, but it seems like it'd be a one-liner within the loop. If it matters, the traces I'm adding are choropleths. I'm not going to include a working dataset since my code works fine- I just want to know what line of code I need to add.
#Create groupby object, splitting data frame into separate data frames based on 'State'
death_counts_gbo = death_counts_by_week.groupby('End Week')
#Prepare subplots for choropleths
rows = 4
cols = 7
fig = make_subplots(
rows=rows, cols=cols,
specs = [[{'type': 'choropleth'} for c in np.arange(cols)] for r in np.arange(rows)],
)
#Graph each state's data
for i, (date, df) in enumerate(death_counts_gbo):
fig.add_trace(go.Choropleth(
locations=df['State Abbr'], # Spatial coordinates
z = df['COVID-19 Deaths per 100k'].astype(float), # Data to be color-coded
locationmode = 'USA-states', # set of locations match entries in `locations`
colorscale = 'Reds',
zmin = 0,
zmax = 30,
colorbar_title = "Deaths per 100k",
text = date.strftime('%m-%d-%Y')
), row=i//7+1, col=i%7+1)
### This is where I'd like to set the subplot's title to 'date' ###
# Set title of entire figure
# Set each subplot map for the USA map instead of default world map
# Set each subplot map's lake color to light blue
fig.update_layout(
title_text = 'COVID-19 Deaths per 100k at week ending',
**{'geo' + str(i) + '_scope': 'usa' for i in [''] + np.arange(2,len(death_counts_gbo)+1).tolist()},
**{'geo' + str(i) + '_lakecolor': 'lightblue' for i in [''] + np.arange(2,len(death_counts_gbo)+1).tolist()},
)
fig.show()
EDIT: For my example, I can set the subplot titles during the figure declaration with the following kwarg:
subplot_titles = [date.strftime('%m-%d-%Y') for date in list(death_counts_gbo.groups.keys())]
However, for my own edification and if I have a future case where the determination of the subtitle is more involved, I would still like to know how to set it during the loop / after the figure declaration.
EDIT2: Thanks to @rpanai I have a solution, though it required a change in my make_subplots
declaration, in addition to a one-liner in the loop. He informed me that subtitles are annotations stored in fig.layout.annotations
and that, while a list of subtitles may be provided to it, calculating the appropriate x
and y
coordinates might be a hassle. I worked around this issue by creating temporary subtitles and forcing make_subplots
to calculate the x
and y
coordinates. I then updated the text in the loop.
The updated make_subplots
figure declaration (subplot_titles added):
fig = make_subplots(
rows=rows, cols=cols,
specs = [[{'type': 'choropleth'} for c in np.arange(cols)] for r in np.arange(rows)],
subplot_titles = ['temp_subtitle' for date in np.arange(len(death_counts_gbo))]
)
One-liner (first line of the for-loop):
fig.layout.annotations[i]['text'] = date.strftime('%m-%d-%Y')
Upvotes: 30
Views: 35349
Reputation: 61214
You mentioned that you'd like to be able to edit the titles (that are annotations) after you'be built your figures as well. And if you already have some title in place, and would like to change them, you can use fig.for_each_annotation()
in combination with a dict like this:
names = {'Plot 1':'2016', 'Plot 2':'2017', 'Plot 3':'2018', 'Plot 4':'2019'}
fig.for_each_annotation(lambda a: a.update(text = names[a.text]))
Which will turn this:
into this:
You can also easily combine existing titles with an addition of your choice with:
fig.for_each_annotation(lambda a: a.update(text = a.text + ': ' + names[a.text]))
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(
rows=2, cols=2,
subplot_titles=("Plot 1", "Plot 2", "Plot 3", "Plot 4"))
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),
row=1, col=1)
fig.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),
row=1, col=2)
fig.add_trace(go.Scatter(x=[300, 400, 500], y=[600, 700, 800]),
row=2, col=1)
fig.add_trace(go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000]),
row=2, col=2)
fig.update_layout(height=500, width=700,
title_text="Multiple Subplots with Titles")
names = {'Plot 1':'2016', 'Plot 2':'2017', 'Plot 3':'2018', 'Plot 4':'2019'}
# fig.for_each_annotation(lambda a: a.update(text = names[a.text]))
fig.for_each_annotation(lambda a: a.update(text = a.text + ': ' + names[a.text]))
fig.show()
Upvotes: 22