I have 2 heatmaps I'm trying to join together, one contains week over week data and the other contains 6W/YTD information. I keep them separate so their colors aren't skewed.
When they're put together in a subplot, the yaxis label on the right is the first row label on the left
I would like to remove that yaxis label, and have tried by means of fig.update_yaxes(title=''/None/False)
, also the title_text
argument, I've also tried accessing it via fig['layout']['scenes']['yaxis2']['title'] = ''/None/False
. Seems like a lot of resources (including official documentation) show how to change a single figure, as could be demonstrated with my code via
fig1['layout']['yaxis']['title']='This works with a single plot'
fig1.show()
which unfortunately doesn't carry over when fig2
is added to the subplot. I'm not sure how this assignment is happening. I've reviewed the JSON structure and see no assignment, I've also dug through the structure in their documentation to see if there was something I could override or set. Can someone help me figure out how to hide the fig2
yaxis label? Might be more accurate to ask how one would set this manually, but nonetheless.
*edit* I've taken a closer look at the annotations. The figures annotations are set with this bit
annotations=wow_annot+totals_annot
these are based on
wow['data_labels'] = int_to_str(wow['data'])
totals['data_labels'] = int_to_str(totals['data'])
which are just 2d arrays of integers. checking the annotations in each figure for 'A', the Figure they're added to as subplots is the only one that contains the 'A'
(Pdb) [i for i in fig1.layout.annotations if i.text == 'A']
[]
(Pdb) [i for i in fig2.layout.annotations if i.text == 'A']
[]
(Pdb) [i for i in fig.layout.annotations if i.text == 'A']
[layout.Annotation({
'font': {'size': 16},
'showarrow': False,
'text': 'A',
'textangle': 90,
'x': 0.98,
'xanchor': 'left',
'xref': 'paper',
'y': 0.5,
'yanchor': 'middle',
'yref': 'paper'
})]
based on that, this could easily be "solved" retroactively by overwriting the annotation like so
new_annot = []
for i in fig.layout.annotations:
if i.text == wow['y_labels']['labels'][0]:
i.text = ''
new_annot.append(i)
fig.update_layout(annotations=new_annot)
this works, but feels very finicky and I would still like to know how this is supposed to be done with plotly. This approach feels like it could have unintended effects depending on application.
Upon closer inspection I'm also realizing there are no annotations placed on the first 2 columns of the bottom row despite them having annotations in their original figure
(Pdb) fig1.layout.annotations[:2]
(layout.Annotation({
'font': {'color': 'black'}, 'showarrow': False, 'text': '0', 'x': 'W21', 'xref': 'x', 'y': 'A', 'yref': 'y'
}), layout.Annotation({
'font': {'color': 'black'}, 'showarrow': False, 'text': '0', 'x': 'W22', 'xref': 'x', 'y': 'A', 'yref': 'y'
}))
I'm not sure if there's something I'm missing or if it's my approach that's incorrect in setting annotations
Checking `wow_annot+totals_annot` for `W21:A` annotation
layout.Annotation({
'font': {'color': 'black'}, 'showarrow': False, 'text': '0', 'x': 'W21', 'xref': 'x', 'y': 'A', 'yref': 'y'
})
Checking the final `fig` for `W21:A` annotation
> d:\test_subplots.py(178)<module>()
-> fig.show()
(Pdb) len([i for i in totals_annot if i.y == 'A'])
2
(Pdb) len([i for i in wow_annot if i.y == 'A'])
6
(Pdb) len([i for i in totals_annot+wow_annot if i.y == 'A'])
8
(Pdb) len([i for i in fig.layout.annotations if i.y == 'A'])
6
I'll leave it as is because this post is becoming cumbersome, but there's a problem: 1) with the annotations and 2) the y title for fig2; I feel like they have to be related though I don't know how this is occurring
I've separated my code below, a Paste is available here.
imports
# Success Criteria for this exercise is a subplot containing 2 Heatmaps side by side in the same Figure
from pdb import set_trace
from covidDash.plot_handlers.colorscales import bone_r # this is a custom derived from matplotlib
from plotly.subplots import make_subplots
import plotly.figure_factory as ff
import plotly.graph_objects as go
data prep
# DATA PREP SECTION
# wow Heatmap data
wow = {'x_labels' : {'name' : 'Week',
'labels' : ['W21', 'W22', 'W23', 'W24', 'W25', 'W26']
},
'y_labels' : {'name' : 'Site',
'labels' : ['A', 'B', 'C', 'D', 'E', 'F', 'G']
},
'data' : [
[0, 0, 1, 0, 0, 0],
[0, 0, 3, 1, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
],
'data_labels' : []
}
# 6w and YTD Heatmap data
totals = {'x_labels' : {'name' : 'Week',
'labels' :['6W', 'YTD' ]
},
'y_labels' : wow['y_labels'],
'data' : [
[1, 16],
[4, 8],
[0, 1],
[1, 12],
[0, 5],
[1, 17],
[0, 1]
],
'data_labels' : []
}
# this function is simply a base func for now
def int_to_str(arr2d):
"""base function for handling data to label conversion
Args:
arr2d (list): a 2D array with numeric values
Returns:
r_data (list): a 2D array with values converted into strings
"""
r_data = []
for row in arr2d:
new_row = []
[new_row.append(str(n)) for n in row]
r_data.append(new_row)
return r_data
wow['data_labels'] = int_to_str(wow['data'])
totals['data_labels'] = int_to_str(totals['data'])
plot_prep
# PLOT PREP SECTION
# colorbar placement
wow_cbar= {
'x' : 1.0,
'title' : {
'text' : 'Wow',
'side' : 'right'
}
}
total_cbar = {
'x' : 1.05,
'title' : {
'text' : 'Totals',
'side' : 'right'
}
}
# xaxis conf
xaxis_conf={'rangeslider': {'visible': True},
'type' : 'category',
'side' : 'top'
}
creating and joining the figures
# week over week figure
fig1 = ff.create_annotated_heatmap(x=wow['x_labels']['labels'],
y=wow['y_labels']['labels'],
z=wow['data'],
colorscale=bone_r,
font_colors=['black','white'],
showscale=True,
annotation_text=wow['data_labels'],
colorbar=wow_cbar,
name='Wow'
)
# 6W and YTD
fig2 =ff.create_annotated_heatmap(x=totals['x_labels']['labels'],
y=totals['y_labels']['labels'],
z=totals['data'],
colorscale=bone_r,
font_colors=['black','white'],
showscale=True,
annotation_text=totals['data_labels'],
colorbar=total_cbar,
name='Totals',
)
# SUBPLOT PREP SECTION
# base subplot
fig = make_subplots(
rows=1, cols=2,
shared_yaxes=True,
horizontal_spacing=0,
row_titles=wow['y_labels']['labels'],
# y_title=[wow['y_labels']['name'],
x_title=wow['x_labels']['name'],
column_widths=[0.75, 0.25]
)
# add data
fig.add_trace(fig1.data[0], 1, 1)
fig.add_trace(fig2.data[0], 1, 2)
# apply annotations
wow_annot = list(fig1.layout.annotations)
totals_annot = list(fig2.layout.annotations)
for k in range(len(totals_annot)):
totals_annot[k]['xref'] = 'x2'
totals_annot[k]['yref'] = 'y2'
fig.update_layout(annotations=wow_annot+totals_annot,xaxis=xaxis_conf, xaxis2={'side':'top'})
set_trace()
fig.show()
something's wrong with fig.update_layout(annotations=foo)
, I was able to resolve this by applying the annotations directly by re-arranging as so
fig.update_layout(xaxis=xaxis_conf, xaxis2={'side':'top'})
fig['layout']['annotations'] = wow_annot+totals_annot
this makes it so all of the fields are properly annotated and a y_label is no longer assigned.
as to what the specific problem is with .update_layout
, I'm not sure but this is how one would go about resolving this issue if yaxis_title=foo
didn't work for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With