Skip to content Skip to sidebar Skip to footer

How To Use Two Mplcursors Simultaneously For A Scatter Plot Of Two Sets?

I am trying to get a scatter plot of 2 different variables, one set of scatter points is height values for a projectile under the effect of air resistant, and one is a projectile w

Solution 1:

Instead of connecting the cursors to the ax, try connecting them only to the scatter plot they belong to. The first parameter of mplcursors.cursor is meant for just that.

You'll notice that when you move to a point from the other set, that the annotation of the previous one is not removed. Therefore, I added some code to remove the other annotation when a new one is opened.

Note that you can directly access the index via sel.target.index. Calling get_index(airRangeValues, ...) with a point of the wrong set could be a cause of the error you encountered.

Here's some code to demonstrate the principles. The backgroundcolor of the annotations is set differently to better illustrate which cursor is being displayed. Also, the alpha is changed to ease reading the text.

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

def cursor1_annotations(sel):
    sel.annotation.set_text(
        'Cursor One:\nRange {:.2f} m\nHeight {:.2f} m\nindex: {}'.format(sel.target[0], sel.target[1], sel.target.index))
    sel.annotation.get_bbox_patch().set(fc="powderblue", alpha=0.9)
    for s in crs2.selections:
        crs2.remove_selection(s)

def cursor2_annotations(sel):
    sel.annotation.set_text(
        'Cursor Two:\nRange {:.2f} m\nHeight {:.2f} m\nindex: {}'.format(sel.target[0], sel.target[1], sel.target.index))
    sel.annotation.get_bbox_patch().set(fc="lightsalmon", alpha=0.9)
    for s in crs1.selections:
        crs1.remove_selection(s)

N = 100
nairRangeValues = np.random.normal(30, 10, N)
nairHeightValues = np.random.uniform(40, 100, N)
airRangeValues = np.random.normal(40, 10, N)
airHeightValues = np.random.uniform(50, 120, N)

fig, ax = plt.subplots()

nair_scatter = ax.scatter(nairRangeValues, nairHeightValues, c="blue", label="No air resistance", s=3)
air_scatter = ax.scatter(airRangeValues, airHeightValues, c="red", label="Air resistance", s=3)
ax.legend()
plt.xlabel("Range", size=10)
plt.ylabel("Height", size=10)

crs1 = mplcursors.cursor(nair_scatter, hover=True)
crs1.connect("add", cursor1_annotations)
crs2 = mplcursors.cursor(air_scatter, hover=True)
crs2.connect("add", cursor2_annotations)

plt.show()

example plot

PS: Something similar can also be achieved using only one cursor and adding a test. In that case there is no need to manually remove the other cursor:

def cursor_annotations(sel):
    if sel.artist == nair_scatter:
        sel.annotation.set_text(
            'Cursor One:\nRange {:.2f} m\nHeight {:.2f} m\nindex: {}'.format(sel.target[0], sel.target[1], sel.target.index))
        sel.annotation.get_bbox_patch().set(fc="powderblue", alpha=0.9)
    else:
        sel.annotation.set_text(
            'Cursor Two:\nRange {:.2f} m\nHeight {:.2f} m\nindex: {}'.format(sel.target[0], sel.target[1], sel.target.index))
        sel.annotation.get_bbox_patch().set(fc="lightsalmon", alpha=0.9)

crs = mplcursors.cursor([nair_scatter, air_scatter], hover=True)
crs.connect("add", cursor_annotations)

Post a Comment for "How To Use Two Mplcursors Simultaneously For A Scatter Plot Of Two Sets?"