I am making a series of tiled figures, each with 4 subplots. First I make the individual figures:
def create_plot(list1, list2):
'''
Given two lists of metric values, plot corresponding distributions and
return plot object.
INPUT
2 lists
RETURN
1 plotly figure object
'''
# grab labels (TP/FP or samplename) to make column below
# uses pop() as string label needs separating from int values
label1 = list1.pop(0)
label2 = list2.pop(0)
# make combined df column
values = list1 + list2
# calculate centiles for each entry in the arrays (displayed silently)
# and turn into column for dataframe (same order as values)
centiles = list(
calculate_centiles(list1)
) + list(
calculate_centiles(list2)
)
# make TPFP column for dataframe
TPFP = ([label1] * len(list1)) + ([label2] * len(list2))
# make dataframe
df = pd.DataFrame(
{'values': values, 'TPFP': TPFP, 'centiles': centiles}
)
fig = px.histogram(
df, x='values', color='TPFP',
hover_data=[df.columns[2]], marginal='rug', barmode='overlay'
)
# set format for hovertext using hovertemplate (even index = histogram,
# odd index = rug)
for i, trace in enumerate(fig['data']):
group = trace['legendgroup']
if i % 2 == 0:
trace['hovertemplate'] = (
f'True/False Positive={group}<br>'
'Bin=%{x}<br>Count=%{y}<extra></extra>'
)
else:
trace['hovertemplate'] = (
'<br>Metric value=%{x}<br>'
'Centile=%{customdata[0]}<br><extra></extra>'
)
return fig
Then I insert these into a multi-plot object:
def make_tiled_figure(subfigs, metric):
'''
Take list of figures (plotly plot objects) to be combined into
tiled image. Return single figure object with tiled subplots.
INPUT
1 list of plotly figure objects, 1 string
RETURN
1 plotly figure object
'''
fig = make_subplots(rows=1, cols=4, subplot_titles=[
'SNP_HET', 'SNP_HOM', 'INDEL_HET', 'INDEL_HOM'])
# decide on position and add subfigures to plot
for i, subfig in enumerate(subfigs):
if subfig:
for trace in subfig.data:
fig.add_trace(trace, row=1, col=i+1)
fig.update_layout(hovermode='x unified')
# specify plot size and title
fig.update_layout(
height=500, width=1800, title_text=metric, showlegend=False
)
return fig
When I open the figures individually (fig.show()), the hover text works perfectly, showing all the info specified (including the counts for each histogram bin). However, when I do the same for the tiled figure the hover text is messed up - now the 'Count' field says either '{y}' (without fetching the actual number) or a value from the FPTP column.
Why do the histogram counts go missing? What am I missing?
EDIT: I've added my solution as an answer, but for more context I am adding an image of what it looked like before.
Wrong output (count=%{y} and mashed rug plot)
The input to create_plot() was just a pair of lists in the format ['TP',27,284,74,483,374,493,394,12,10,902,83] etc. where the string label in position 0 is either TP or FP (True/False Positive) in this iteration. The resulting figures were the input to make_tiled_figure() along with a single string representing a QC metric e.g. 'DP'. The calculate_centiles() function just returns a list of numbers the same length as its input (that will be displayed by the hovertext), and make_subplots() is provided by plotly.subplots