如何同时使用两个 mplcursors 绘制两组散点图?
How to use two mplcursors simultaneously for a scatter plot of two sets?
我想得到 2 个不同变量的散点图,一组散点是弹丸在空气阻力作用下的高度值,一组是没有空气阻力的弹丸。我可以使用一组散点来实现这一点,但不能同时使用它们。当悬停在一个点上时,我正在使用 mplcursors library 来显示注释标签。
这是相关代码:
import numpy as np
from matplotlib import pyplot as plt
import mplcursors
# ...
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)
crs = mplcursors.cursor(ax,hover=True)
crs.connect("add", lambda sel: sel.annotation.set_text(
'Range {} m\nHeight {} m\nVelocity {} m/s at angle {} degrees\nDisplacement {} m\nTime of flight {} s' .format(
(sel.target[0]), (sel.target[1]),
(airVelocityValues[get_index(airRangeValues, sel.target[0])]),
(airAngleValues[get_index(airRangeValues, sel.target[0])]),
(airDisplacementValues[get_index(airRangeValues, sel.target[0])]),
(airTimeValues[get_index(airRangeValues, sel.target[0])]) ) ) )
crs2 = mplcursors.cursor(ax,hover=True)
crs2.connect("add", lambda ok: ok.annotation.set_text(
'Range {} m\nHeight {} m' .format(ok.target[0], ok.target[1])))
plt.show()
这有一些问题。首先,它给了我一个巨大的错误并在最后说 StopIteration
。另一个是它在一组散点上显示了正确的标签,但也显示了同一散点图的 crs2 值,而不是另一个散点图。我不知道如何让它们能够对每个散点集都是唯一的,如果有人可以帮助我欣赏它。
不要将光标连接到斧头,而是尝试将它们仅连接到它们所属的散点图。 mplcursors.cursor
的第一个参数就是这个意思。
您会注意到,当您从另一组移动到一个点时,不会删除前一个的注释。因此,我添加了一些代码以在打开新注释时删除另一个注释。
请注意,您可以通过 sel.target.index
直接访问索引。使用错误集合的点调用 get_index(airRangeValues, ...)
可能是您遇到错误的原因。
下面是一些演示原理的代码。注释的背景颜色设置不同,以更好地说明显示的是哪个光标。此外,更改了 alpha 以方便阅读文本。
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()
PS: 仅使用一个游标并添加测试也可以实现类似的效果。在这种情况下,无需手动删除另一个光标:
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)
我想得到 2 个不同变量的散点图,一组散点是弹丸在空气阻力作用下的高度值,一组是没有空气阻力的弹丸。我可以使用一组散点来实现这一点,但不能同时使用它们。当悬停在一个点上时,我正在使用 mplcursors library 来显示注释标签。 这是相关代码:
import numpy as np
from matplotlib import pyplot as plt
import mplcursors
# ...
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)
crs = mplcursors.cursor(ax,hover=True)
crs.connect("add", lambda sel: sel.annotation.set_text(
'Range {} m\nHeight {} m\nVelocity {} m/s at angle {} degrees\nDisplacement {} m\nTime of flight {} s' .format(
(sel.target[0]), (sel.target[1]),
(airVelocityValues[get_index(airRangeValues, sel.target[0])]),
(airAngleValues[get_index(airRangeValues, sel.target[0])]),
(airDisplacementValues[get_index(airRangeValues, sel.target[0])]),
(airTimeValues[get_index(airRangeValues, sel.target[0])]) ) ) )
crs2 = mplcursors.cursor(ax,hover=True)
crs2.connect("add", lambda ok: ok.annotation.set_text(
'Range {} m\nHeight {} m' .format(ok.target[0], ok.target[1])))
plt.show()
这有一些问题。首先,它给了我一个巨大的错误并在最后说 StopIteration
。另一个是它在一组散点上显示了正确的标签,但也显示了同一散点图的 crs2 值,而不是另一个散点图。我不知道如何让它们能够对每个散点集都是唯一的,如果有人可以帮助我欣赏它。
不要将光标连接到斧头,而是尝试将它们仅连接到它们所属的散点图。 mplcursors.cursor
的第一个参数就是这个意思。
您会注意到,当您从另一组移动到一个点时,不会删除前一个的注释。因此,我添加了一些代码以在打开新注释时删除另一个注释。
请注意,您可以通过 sel.target.index
直接访问索引。使用错误集合的点调用 get_index(airRangeValues, ...)
可能是您遇到错误的原因。
下面是一些演示原理的代码。注释的背景颜色设置不同,以更好地说明显示的是哪个光标。此外,更改了 alpha 以方便阅读文本。
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()
PS: 仅使用一个游标并添加测试也可以实现类似的效果。在这种情况下,无需手动删除另一个光标:
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)