简体   繁体   中英

How to create hover annotations on a subplot using matplotlib for BrokenBarHCollection objects?

I currently have a figure with three subplots that all share the y-axis but not the x-axis. For each subplot, I generated the data points using a for-loop that created a BrokenBarHCollection. The for loop I used is below (the function "f" just creates the xranges and yrange for each subplot):

for c in f(t):
    axis[1].add_collection(c)

Now, I want the user to be able to hover over certain points in that subplot and have an annotation appear about what they hovered over. However, the label I need to show wasn't originally used to create the subplot so I created a list containing all of the labels I need and another lists containing all of the points in the x-axis I want to be specified. Since they are rectangles (using BrokenBarHCollection) the points in the second list is the middle of rectangle). The y-range is just 0. I later created a dictionary with the labels being the keys and the points being the values and figured out how to generate annotation in the subplot using the following code:

for keys, values in gene_dict.items():
    y = 0
    annot = axs[1].annotate(keys, xy = (values, y), fontsize = 4) 

Here is the resulting dictionary:

{'YFL067': 2074.5, 'YFL041': 49352.5, 'YPT1': 56193.5, 'PAU5': 99435.0, 'YFL019': 100497.0, 'SMX2': 103801.5, 'YFL015': 106649.5, 'HSP12': 107304.5, 'YFL012': 110789.5, 'AUA1': 114958.0, 'WWM1': 115252.0, 'YPI1': 152424.0, 'MIC19': 166370.5, 'YFR012': 168579.0, 'RPL29': 222135.5, 'CDC26': 225896.0, 'YMR31': 247177.5}

So, my question is how can I use the labels I generated and the points along the x-axis to create a hover-over annotation? I've seen posts on here that use mplcursors, but when I do that, nothing happens:

mplcursors.cursor(axs[1]).connect("add", lambda sel: sel.annot.set_text(keys[sel.target.index]))

I think I have to create a onHover event function but I'm not sure how to do that with a BrokenBarHCollection object. Anybody have any ideas?

What the subplot currently looks like:

在此处输入图像描述

It's a bit unclear how you create your plot and how it looks like. The approach below assigns a label to each of the little bars. A broken_barh can only have one label for the whole set, so instead of drawing ax.broken_barh() , individual rectangles are created, each with their own label. An additional possibility is to also assign individual colors to each rectangle (the example below supposes a maximum of 20 colors).

In that case, mplcursors default generates an annotation with label, x and y position of the cursor. You can change the default annotation, for example only using the label.

Note that mplcursors.cursor(..., hover=True) shows an annotation while hovering. The default only shows the annotation when clicking.

import matplotlib.pyplot as plt
import mplcursors
import numpy as np

gene_dict = {'YFL067': 2074.5, 'YFL041': 49352.5, 'YPT1': 56193.5, 'PAU5': 99435.0, 'YFL019': 100497.0, 'SMX2': 103801.5, 'YFL015': 106649.5, 'HSP12': 107304.5, 'YFL012': 110789.5, 'AUA1': 114958.0, 'WWM1': 115252.0, 'YPI1': 152424.0, 'MIC19': 166370.5, 'YFR012': 168579.0, 'RPL29': 222135.5, 'CDC26': 225896.0, 'YMR31': 247177.5}
color_dict = { key:color for key, color in zip(gene_dict.keys(), plt.cm.tab20.colors )}

fig, ax = plt.subplots()

for y in range(10, 51, 10):
    keys = np.random.choice(list(gene_dict.keys()), 5, replace=False)
    width = 3000
    for key in keys:
        ax.add_patch(plt.Rectangle((gene_dict[key] - width / 2, y), width, 9,
                                   color=color_dict[key], label=key))
ax.relim() # needed to update the xlim and ylim when using add_patch
ax.autoscale()

mplcursors.cursor(hover=True).connect("add", lambda sel: sel.annotation.set_text(sel.artist.get_label()))
plt.show()

broken_barh 的悬停注释

Some explanation about mplcursors.cursor(...).connect("add", lambda sel: ...) . lambda is a way to quickly write a short function (without name) so it can be used as a parameter to another function. The code is equivalent to:

def update_annotation(sel):
    """ update the annotation belonging to the current selected item (sel) """
    # get the label of the graphical element that is selected
    label = sel.artist.get_label()
    # change the text of the annotation
    sel.annotation.set_text(label)

# create an mplcursor object that shows an annotation while hovering
cursor = mplcursors.cursor(hover=True)
# call the function "update_annotation" each time a new element gets hovered over
cursor.connect("add", update_annotation)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM