【问题标题】:Getting Lasso to work correctly on subplots in matplotlib让套索在 matplotlib 中的子图上正常工作
【发布时间】:2014-06-14 08:55:07
【问题描述】:

我想创建一个由一些子图组成的散点图矩阵。我从 .txt 文件中提取了我的数据并创建了一个形状为 (x,y,z,p1,p2,p3,p4) 的数组。数组的前三列表示这些数据来自的原始图像的 x、y、z 坐标,最后四列(p1、p2、p3、p4)表示其他一些参数。因此,在数组的每一行中,参数 p1、p2、p3、p4 具有相同的坐标(x、y、z)。在散点图中,我想将每个 p_i(例如 p1)参数与另一个 p_i(例如 p2, p3, p4) 参数。

我想在每个子图中绘制一个感兴趣区域 (ROI),突出显示每个子图中 ROI 中包含的点。在每个子图中,不同的参数被可视化(例如 p1 与 p2),但对于每个子图中的一个点,在其余子图中还有另一个具有相同 x、y、z 坐标的点。我使用matplotlib 示例Lasso 实现了ROI 的绘制。下图显示了此代码实现的示例。

我的实现出现故障。我可以在每个子图中绘制套索,但只有在特定子图中绘制套索时才会突出显示点,这对应于我的代码中LassoManager 函数的第一次调用(在我的代码selector1 中)。从下图中可以看出,给lassos一个初始值,可以在不同的子图中绘制,但只使用选择器1中对应的id导致代码故障,独立于我在哪个子图中绘制投资回报率。

这是我的代码:

"""
Show how to use a lasso to select a set of points and get the indices
of the selected points.  A callback is used to change the color of the
selected points

This is currently a proof-of-concept implementation (though it is
usable as is).  There will be some refinement of the API.
"""


from matplotlib.widgets import Lasso
from matplotlib.colors import colorConverter
from matplotlib.collections import RegularPolyCollection
from matplotlib import path


import matplotlib.pyplot as plt
import numpy as np

class Datum(object):
      colorin = colorConverter.to_rgba('red')
      colorout = colorConverter.to_rgba('blue')
      def __init__(self, x, y, include=False):
          self.x = x
          self.y = y
          if include: self.color = self.colorin
          else: self.color = self.colorout

class LassoManager(object):
    #class for highlighting region of points within a Lasso
      def __init__(self, ax, data):


          self.axes = ax
          self.canvas = ax.figure.canvas
          self.data = data
          self.Nxy = len(data)

          facecolors = [d.color for d in data]
          self.xys = [(d.x, d.y) for d in data]
          fig = ax.figure
          self.collection = RegularPolyCollection(
             fig.dpi, 6, sizes=(1,),
             facecolors=facecolors,
             offsets = self.xys,
             transOffset = ax.transData)

          ax.add_collection(self.collection)

          self.cid = self.canvas.mpl_connect('button_press_event', self.onpress)

      def callback(self, verts):
          facecolors = self.collection.get_facecolors()
          print "The id of this lasso is", id(self)


          p = path.Path(verts)
          ind = p.contains_points(self.xys)
          #ind prints boolean array of points in subplot where true means that the point is included

          for i in range(len(self.xys)):


              if ind[i]:

                # facecolors[i] = Datum.colorin
                axes[0][0].plot(x[i], y[i], 'ro',  ls='',  picker=3)
                axes[2][0].plot(x[i], y1[i], 'ro',  ls='',  picker=3)
                axes[1][0].plot(x[i], x1[i], 'ro',  ls='',  picker=3)
                axes[1][4].plot(y[i], x1[i], 'ro',  ls='',  picker=3)
                axes[2][5].plot(x1[i], y1[i], 'ro',  ls='',  picker=3)
                axes[2][6].plot(y[i], y1[i], 'ro',  ls='',  picker=3)
                # print ind[i], x[i], y[i], x1[i], y1[i]

              else:

                # facecolors[i] = Datum.colorout
                axes[0][0].plot(x[i], y[i], 'bo',  ls='',  picker=3)
                axes[2][0].plot(x[i], y1[i], 'bo',  ls='',  picker=3)
                axes[1][0].plot(x[i], x1[i], 'bo',  ls='',  picker=3)
                axes[1][7].plot(y[i], x1[i], 'bo',  ls='',  picker=3)
                axes[2][8].plot(x1[i], y1[i], 'bo',  ls='',  picker=3)
                axes[2][9].plot(y[i], y1[i], 'bo',  ls='',  picker=3)

          plt.draw()


          self.canvas.draw_idle()
          self.canvas.widgetlock.release(self.lasso)
          del self.lasso
          # noinspection PyArgumentList


      def onpress(self, event):
          if self.canvas.widgetlock.locked(): return
          if event.inaxes is None: return

          self.lasso = Lasso(event.inaxes, (event.xdata, event.ydata), self.callback)

          # acquire a lock on the widget drawing
          self.canvas.widgetlock(self.lasso)




if __name__ == '__main__':

   dat = np.loadtxt(r"parameters.txt")
   x, y = dat[:, 3], dat[:, 4]  #p1,p2
   x1, y1 = dat[:, 5], dat[:, 6]  #p3,p4

   a = np.array([x,y])  #p1,p2
   a = a.transpose()

   b = np.array([x,y1])  #p1,p4
   b = b.transpose()

   c = np.array([x,x1])  #p1,p3
   c = c.transpose()

   d = np.array([y,x1])  #p3,p2
   d = d.transpose()

   e = np.array([x1,y1])  #p3,p4
   e = e.transpose()

   f = np.array([y,y1])  ##p2, p4
   f = f.transpose()


   data = []

   data0 = [Datum(*xy) for xy in a]   #p1,p2
   data.append(data0)
   data1 = [Datum(*xy) for xy in b]   #p1,p4
   data.append(data1)
   data2 = [Datum(*xy) for xy in c]   #p1,p3
   data.append(data2)
   data3 = [Datum(*xy) for xy in d]   #p3,p2
   data.append(data3)
   data4 = [Datum(*xy) for xy in e]   #p3,p4
   data.append(data4)
   data5 = [Datum(*xy) for xy in f]   #p2, p4
   data.append(data5)

   #print data
   #print len(data)

   fig, axes = plt.subplots(ncols=3, nrows=3)

   axes[0][0].plot(x, y, 'bo',  ls='',  picker=3)
   axes[0][0].set_xlabel('p1')
   axes[0][0].set_ylabel('p2')
   axes[0][0].set_xlim((min(x)-50, max(x)+50))
   axes[0][0].set_ylim((min(y)-50, max(y)+50))
   selector1 = LassoManager(axes[0][0], data[0])
   print "selector1 is", id(selector1)      #lman.append(l1)

   #p1 vs p4
   axes[2][0].plot(x, y1, 'bo',  ls='',  picker=3)
   axes[2][0].set_xlabel('p1')
   axes[2][0].set_ylabel('p4')
   axes[2][0].set_xlim((min(x)-50, max(x)+50))
   axes[2][0].set_ylim((min(y1)-40, max(y1)+50))
   selector2 = LassoManager(axes[2][0], data[1])
   print "selector2 is", id(selector2)


   #p1 vs p3
   axes[1][0].plot(x, x1, 'bo',  ls='',  picker=3)
   axes[1][0].set_xlabel('p1')
   axes[1][0].set_ylabel('p3')
   axes[1][0].set_xlim((min(x)-50, max(x)+50))
   axes[1][0].set_ylim((min(x1)-40, max(x1)+50))
   selector3 = LassoManager(axes[1][0], data[2])
   print "selector3 is", id(selector3)

   #p2 vs p3
   axes[1][10].plot(y, x1, 'bo',  ls='',  picker=3)
   axes[1][11].set_xlabel('p2')
   axes[1][12].set_ylabel('p3')
   axes[1][13].set_xlim((min(y)-50, max(y)+50))
   axes[1][14].set_ylim((min(x1)-40, max(x1)+50))
   selector4 =  LassoManager(axes[1][15], data[3])
   print "selector4 is", id(selector4)




   #p2 vs p4
   axes[2][16].plot(y, y1, 'bo',  ls='',  picker=3)
   axes[2][17].set_xlabel('p2')
   axes[2][18].set_ylabel('p4')
   axes[2][19].set_xlim((min(y)-50, max(y)+50))
   axes[2][20].set_ylim((min(y1)-40, max(y1)+50))
   selector5 = LassoManager(axes[2][21], data[5])
   print "selector5 is", id(selector5)


   #p3 vs p4
   axes[2][22].plot(x1, y1, 'bo',  ls='',  picker=3)
   axes[2][23].set_xlabel('p3')
   axes[2][24].set_ylabel('p4')
   axes[2][25].set_xlim((min(x1)-50, max(x1)+50))
   axes[2][26].set_ylim((min(y1)-40, max(y1)+50))
   selector6 = LassoManager(axes[2][27], data[4])
   print "selector6 is", id(selector6)


   #non-visible subplots
   axes[0][28].plot(x,x)
   axes[0][29].set_visible(False)
   axes[0][30].plot(y,y)
   axes[0][31].set_visible(False)
   axes[1][32].plot(x1,x1)
   axes[1][33].set_visible(False)

   plt.subplots_adjust(left=0.1, right=0.95, wspace=0.6, hspace=0.7)

   plt.show()

为什么我的代码会出现这种情况?代码中没有错误,但它不能正常工作。任何帮助将不胜感激!

【问题讨论】:

  • 这是互动情节吗?如果不是,我不明白你为什么要使用套索管理器。
  • 是的,是的!首先,我在子图中绘制了一个套索,然后其中包含的点在所有子图中突出显示。

标签: python python-2.7 numpy matplotlib plot


【解决方案1】:

据我所知,问题是在每个init 上,您都将画布的button_press_event 替换为新的。

您很可能需要使用一个button_press_event 回调来处理所有轴(因为它们都通过同一个画布对象进行交互)。

修复

以下是一个功能示例,基于文档中的官方套索示例。

我尝试的方法是只创建一个LassoManager(因为它只与每个图的一个画布交互)但让轴、数据等成为每个子图的列表。

然后回调访问current_axis 成员以确定当前处于活动状态的轴。

"""
Show how to use a lasso to select a set of points and get the indices
of the selected points.  A callback is used to change the color of the
selected points

This is currently a proof-of-concept implementation (though it is
usable as is).  There will be some refinement of the API.
"""
from matplotlib.widgets import Lasso
from matplotlib.colors import colorConverter
from matplotlib.collections import RegularPolyCollection
from matplotlib import path

import matplotlib.pyplot as plt
from numpy import nonzero
from numpy.random import rand

class Datum(object):
    colorin = colorConverter.to_rgba('red')
    colorout = colorConverter.to_rgba('blue')
    def __init__(self, x, y, include=False):
        self.x = x
        self.y = y
        if include: self.color = self.colorin
        else: self.color = self.colorout


class LassoManager(object):
    def __init__(self, ax, data):
        self.axes = [ax]
        self.canvas = ax.figure.canvas
        self.data = [data]

        self.Nxy = [ len(data) ]

        facecolors = [d.color for d in data]
        self.xys = [ [(d.x, d.y) for d in data] ]
        fig = ax.figure
        self.collection = [ RegularPolyCollection(
            fig.dpi, 6, sizes=(100,),
            facecolors=facecolors,
            offsets = self.xys[0],
            transOffset = ax.transData)]

        ax.add_collection(self.collection[0])

        self.cid = self.canvas.mpl_connect('button_press_event', self.onpress)

    def callback(self, verts):

        axind = self.axes.index(self.current_axes)
        facecolors = self.collection[axind].get_facecolors()
        p = path.Path(verts)
        ind = p.contains_points(self.xys[axind])
        for i in range(len(self.xys[axind])):
            if ind[i]:
                facecolors[i] = Datum.colorin
            else:
                facecolors[i] = Datum.colorout

        self.canvas.draw_idle()
        self.canvas.widgetlock.release(self.lasso)
        del self.lasso

    def onpress(self, event):
        if self.canvas.widgetlock.locked(): return
        if event.inaxes is None: return
        self.current_axes = event.inaxes

        self.lasso = Lasso(event.inaxes, (event.xdata, event.ydata), self.callback)
        # acquire a lock on the widget drawing
        self.canvas.widgetlock(self.lasso)

    def add_axis(self, ax,  data):
        self.axes.append(ax)
        self.data.append(data)

        self.Nxy.append( len(data) )

        facecolors = [d.color for d in data]
        self.xys.append( [(d.x, d.y) for d in data] )
        fig = ax.figure
        self.collection.append( RegularPolyCollection(
            fig.dpi, 6, sizes=(100,),
            facecolors=facecolors,
            offsets = self.xys[-1],
            transOffset = ax.transData))

        ax.add_collection(self.collection[-1])



if __name__ == '__main__':

    data = [Datum(*xy) for xy in rand(100, 2)]
    data2 = [Datum(*xy) for xy in rand(100, 2)]

    ax = plt.subplot(1,2,1)
    lman = LassoManager(ax, data)
    ax2 = plt.subplot(1,2,2)
    lman.add_axis(ax2, data2)
    plt.show()

【讨论】:

  • add_axis 函数在您的示例代码中究竟做了什么?可能,您在答案中提到的几乎正确的事实与您绘制套索时在正确的子图中未突出显示正确点有关。
  • @user3204834:它将轴、集合等信息添加到 LassoManager,以便在单击特定轴时,Lasso 使用该轴的数据。
  • @user3204834:我解决了这个问题:我忘记访问xys 的最后一个元素,即add_axis 中的self.xys[-1]
  • 我根据我的要求调整了您的答案。它完美地工作。我被困了大约一个月,因为我不知道 matplotlib 的属性是如何工作的。非常感谢您的帮助。
猜你喜欢
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多