Skip to content Skip to sidebar Skip to footer

Making Labels Appear While Hovering Over Plot For Graphs *with High Y-axis Values*, Multiple Lines And Multiple Axes

I wanted to read values off of the graphs that I made using python similar to how the data point values show up in an excel plot when you hover over a data point. Using various sol

Solution 1:

Essentially, your problem is that you created your annotation as belonging to the axes y1_axis. When you were hovering over a point, you were setting the position of the annotation in the data coordinate of y1_axis, regardless of whether the line was in that axes or another.

The solution is to update not only the coordinates of the annotation, but also its transform to correctly map the point to the correct coordinates in pixels.

The same is true for the background of the annotation. Since you were creating it on the bottom-most axes, the annotation was above the line in these axes, but below the lines in the other axes. The solution here is to create the annotation in the top-most axes.

(...)
# annotation should be on the top axis to avoid zorder problems
annot = fig.axes[-1].annotate("", xy=(0, 0), xytext=(20, 20), textcoords="offset points",
                    bbox=dict(boxstyle="round", facecolor="#FFFFFF"),
                    arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)

(...)
defupdate_annot(line, annot, ind):
    posx, posy = [line.get_xdata()[ind], line.get_ydata()[ind]]
    annot.xycoords = line.axes.transData  # set the correct transform for that line
    annot.xy = (posx, posy)
    text = f'{line.get_label()}: ({posx:.2f},{posy:.2f})'
    annot.set_text(text)
    annot.get_bbox_patch().set_alpha(1)
(...)

Full code:

from matplotlib import pyplot as plt
import numpy as np; np.random.seed(1)

x_data = list(range(0,30))
y1_data_a = np.sort(np.random.rand(30))
y1_data_b = np.sort(np.random.rand(30))
y1_data_c = [0.4for point in x_data]
y2_data_a = [point**2for point in x_data]
y2_data_b = [point*0.5for point in y2_data_a]
y3_data = [(10/(point+1)) for point in x_data]

# #The code works fine with this data# x_data = list(range(0,30))# y1_data_a = np.sort(np.random.rand(30))# y1_data_b = np.sort(np.random.rand(30))# y1_data_c = [0.4 for point in x_data]# y2_data_a = np.random.rand(30)# y2_data_b = np.sort(np.random.rand(30))# y3_data = np.sort(np.random.rand(30))[::-1]

fig, y1_axis = plt.subplots()
fig.subplots_adjust(right=0.75)

y2_axis = y1_axis.twinx()
y3_axis = y1_axis.twinx()

defmake_patch_spines_invisible(ax):
    ax.set_frame_on(True)
    ax.patch.set_visible(False)
    for sp in ax.spines.values():
        sp.set_visible(False)

y3_axis.spines["right"].set_position(("axes", 1.2))
make_patch_spines_invisible(y3_axis)
y3_axis.spines["right"].set_visible(True)

plot1, = y1_axis.plot(x_data, y1_data_a, color="#000CFF", label="Temp1 (°C)")
plot2, = y1_axis.plot(x_data, y1_data_b, color="#FF5100", label="Temp2 (°C)")
plot3, = y1_axis.plot(x_data, y1_data_c, "r--", label="Critical Temp (°C)")

plot4, = y2_axis.plot(x_data, y2_data_a, color="#000000", label="Pressure1 (atm)")
plot5, = y2_axis.plot(x_data, y2_data_b, color="#17E111", label="Pressure2 (atm)")

plot6, = y3_axis.plot(x_data, y3_data, color="#D418DE", label="Volume (m3)")

y1_axis.set_xlabel("Time (hrs)")
y1_axis.set_ylabel("Temperature (°C)")
y2_axis.set_ylabel("Pressure (atm)")
y3_axis.set_ylabel("Volume (m3)")

y3_axis.yaxis.label.set_color(plot6.get_color())

tkw = dict(size=4, width=1.5)
y1_axis.tick_params(axis='y', **tkw)
y2_axis.tick_params(axis='y', **tkw)
y3_axis.tick_params(axis='y', colors=plot6.get_color(), **tkw)
y1_axis.tick_params(axis='x', **tkw)

lines = [plot1, plot2, plot4, plot5, plot6]

plt.title("Labeling data points for plots with Multiple Axes and Lines", fontdict=None, loc='center')

# annotation should be on the top axis to avoid zorder problems
annot = fig.axes[-1].annotate("", xy=(0, 0), xytext=(20, 20), textcoords="offset points",
                    bbox=dict(boxstyle="round", facecolor="#FFFFFF"),
                    arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)


defupdate_annot(line, annot, ind):
    posx, posy = [line.get_xdata()[ind], line.get_ydata()[ind]]
    annot.xycoords = line.axes.transData
    annot.xy = (posx, posy)
    text = f'{line.get_label()}: ({posx:.2f},{posy:.2f})'
    annot.set_text(text)
    annot.get_bbox_patch().set_alpha(1)


defhover(event):
    vis = annot.get_visible()
    if event.inaxes in [y1_axis, y2_axis, y3_axis]:
        for line in lines:
            cont, ind = line.contains(event)
            if cont:
                update_annot(line, annot, ind['ind'][0])
                annot.set_visible(True)
                fig.canvas.draw_idle()
            else:
                if vis:
                    annot.set_visible(False)
                    fig.canvas.draw_idle()


fig.canvas.mpl_connect("motion_notify_event", hover)
plt.show()

Post a Comment for "Making Labels Appear While Hovering Over Plot For Graphs *with High Y-axis Values*, Multiple Lines And Multiple Axes"