如何在matplotlib子图中选择一个点并在相邻子图中高亮显示
我想创建一个散点图矩阵,这个矩阵会由一些子图组成。我从一个.txt文件中提取了我的数据,并创建了一个形状为(x,y,z,p1,p2,p3)的数组。数组的前三列代表了这些数据来自的原始图像的x、y、z坐标,最后三列(p1, p2, p3)是一些其他参数。因此,在数组的每一行中,参数p1、p2、p3都有相同的坐标(x,y,z)。在散点图中,我想先可视化p1参数与p2、p3参数之间的关系。对于我选择的每一个点,我希望能标注出它的(x,y,z)参数,并且在相邻的子图中突出显示具有相同坐标的点,或者改变它的颜色。
在我的代码中,创建了两个子图,并且在终端中打印出通过选择一个点获得的(p1,p2或p3)值,以及相邻子图中同一点的相应值和该点的(x,y,z)参数。
此外,当我在第一个子图中选择一个点时,第二个子图中对应点的颜色会改变,但反过来就不行。这种颜色的变化只有在我手动调整图形大小时才能看到。我该如何为两个子图添加交互功能,而不需要调整图形才能注意到任何变化?为了让这种交互在像这个问题中提到的简化散点图矩阵中可行,我需要做哪些修改?是否有函数可以在matplotlib中制作散点图矩阵? 我不是一个经验丰富的Python和matplotlib用户,所以任何帮助都将不胜感激。
import numpy as np
import matplotlib.pyplot as plt
import pylab as pl
def main():
#load data from file
data = np.loadtxt(r"data.txt")
plt.close("all")
x = data[:, 3]
y = data[:, 4]
y1 = data[:, 5]
fig1 = plt.figure(1)
#subplot p1 vs p2
plt.subplot(121)
subplot1, = plt.plot(x, y, 'bo', picker=3)
plt.xlabel('p1')
plt.ylabel('p2')
#subplot p1 vs p3
plt.subplot(122)
subplot2, = plt.plot(x, y1, 'bo', picker=3)
plt.xlabel('p1')
plt.ylabel('p3')
plt.subplots_adjust(left=0.1, right=0.95, wspace=0.3, hspace=0.45)
# art.getp(fig1.patch)
def onpick(event):
thisevent = event.artist
valx = thisevent.get_xdata()
valy = thisevent.get_ydata()
ind = event.ind
print 'index', ind
print 'selected point:', zip(valx[ind], valy[ind])
print 'point in the adjacent subplot', x[ind], y1[ind]
print '(x,y,z):', data[:, 0][ind], data[:, 1][ind], data[:, 2][ind]
for xcord,ycord in zip(valx[ind], valy[ind]):
plt.annotate("(x,y,z):", xy = (x[ind], y1[ind]), xycoords = ('data' ),
xytext=(x[ind] - .5, y1[ind]- .5), textcoords='data',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3"),
)
subplot2, = plt.plot(x[ind], y[ind], 'ro', picker=3)
subplot1 = plt.plot(x[ind], y[ind], 'ro', picker=3)
fig1.canvas.mpl_connect('pick_event', onpick)
plt.show()
main()
总之,当我选择一个点时,信息会在终端中打印出来,无论是哪个子图。但是,颜色只会在右侧子图中改变,当我在左侧子图中选择一个点,而不是反过来。此外,颜色的变化在我调整图形之前是不可见的(例如,移动或调整大小),而当我选择第二个点时,之前的点仍然保持着颜色。
任何贡献都将受到欢迎。提前谢谢你。
1 个回答
你现在的代码方向是对的。其实你只需要在你的 onpick
函数里加一个 plt.draw()
的调用就行了。
不过,在我们评论的讨论中提到了 mpldatacursor
,你还问了关于用这种方式做的例子。
现在的 HighlightingDataCursor
在 mpldatacursor
中是为了突出显示整个 Line2D
对象,而不是仅仅突出显示其中的某个特定索引。(这是故意设计的,因为在 matplotlib 中没有好的方法可以为任何艺术对象绘制任意的高亮,所以我把高亮的部分做得比较简单。)
不过,你可以像这样创建一个子类(假设你在使用 plot
,并且想要在每个坐标轴中使用你绘制的第一个东西)。我还用 point_labels
来演示,以防你想为每个显示的点设置不同的标签:
import numpy as np
import matplotlib.pyplot as plt
from mpldatacursor import HighlightingDataCursor, DataCursor
def main():
fig, axes = plt.subplots(nrows=2, ncols=2)
for ax, marker in zip(axes.flat, ['o', '^', 's', '*']):
x, y = np.random.random((2,20))
ax.plot(x, y, ls='', marker=marker)
IndexedHighlight(axes.flat, point_labels=[str(i) for i in range(20)])
plt.show()
class IndexedHighlight(HighlightingDataCursor):
def __init__(self, axes, **kwargs):
# Use the first plotted Line2D in each axes
artists = [ax.lines[0] for ax in axes]
kwargs['display'] = 'single'
HighlightingDataCursor.__init__(self, artists, **kwargs)
self.highlights = [self.create_highlight(artist) for artist in artists]
plt.setp(self.highlights, visible=False)
def update(self, event, annotation):
# Hide all other annotations
plt.setp(self.highlights, visible=False)
# Highlight everything with the same index.
artist, ind = event.artist, event.ind
for original, highlight in zip(self.artists, self.highlights):
x, y = original.get_data()
highlight.set(visible=True, xdata=x[ind], ydata=y[ind])
DataCursor.update(self, event, annotation)
main()
再次说明,这里假设你在使用 plot
而不是 scatter
。虽然用 scatter
也可以做到这一点,但你需要修改很多繁琐的细节。(没有通用的方法可以高亮任意的 matplotlib 艺术对象,所以你需要写很多冗长的代码来处理每种类型的艺术对象。)
希望这些对你有帮助。