如何在matplotlib子图中选择一个点并在相邻子图中高亮显示

2 投票
1 回答
5226 浏览
提问于 2025-04-17 21:57

我想创建一个散点图矩阵,这个矩阵会由一些子图组成。我从一个.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 个回答

3

你现在的代码方向是对的。其实你只需要在你的 onpick 函数里加一个 plt.draw() 的调用就行了。

不过,在我们评论的讨论中提到了 mpldatacursor,你还问了关于用这种方式做的例子。

现在的 HighlightingDataCursormpldatacursor 中是为了突出显示整个 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()

enter image description here

再次说明,这里假设你在使用 plot 而不是 scatter。虽然用 scatter 也可以做到这一点,但你需要修改很多繁琐的细节。(没有通用的方法可以高亮任意的 matplotlib 艺术对象,所以你需要写很多冗长的代码来处理每种类型的艺术对象。)

希望这些对你有帮助。

撰写回答