2013-03-16 96 views
4

我正在开发一个交互式绘图应用程序,该应用程序要求用户从matplotlib散点图中选择数据点。为了清楚起见,我希望能够在点击(或通过任何方式选择)时改变绘制点的颜色和形状。使用matplotlib更新散点图中的标记样式

由于matplotlib.collections.PathCollection类具有set_facecolors方法,所以改变点的颜色相对简单。但是,我看不到更新标记形状的类似方法。

有没有办法做到这一点?

问题的准系统说明:

import numpy as np 
import matplotlib.pyplot as plt 

x = np.random.normal(0,1.0,100) 
y = np.random.normal(0,1.0,100) 

scatter_plot = plt.scatter(x, y, facecolor="b", marker="o") 

#update the colour 
new_facecolors = ["r","g"]*50 
scatter_plot.set_facecolors(new_facecolors) 

#update the marker? 
#new_marker = ["o","s"]*50 
#scatter_plot.???(new_marker) #<--how do I access the marker shapes? 

plt.show() 

任何想法?

回答

5

如果您真的要突出显示用户所选的点,那么您可以在选定的点的顶部叠加另一个点(与dot = ax.scatter(...))。之后,为了响应用户点击,您可以使用dot.set_offsets((x, y))更改点的位置。

Joe Kington编写了一个wonderful example (DataCursor)关于如何在用户点击艺术家时(例如散点图)添加显示数据坐标的注释。

这里是一个衍生物例(FollowDotCursor)其中突出,并且当用户将鼠标在一个点标注的数据点。

随着DataCursor数据显示的坐标是在用户点击 - 这可能不是完全相同的坐标的基础数据。

随着FollowDotCursor数据显示坐标总是在其最靠近鼠标底层数据的点。


import numpy as np 
import matplotlib.pyplot as plt 
import scipy.spatial as spatial 

def fmt(x, y): 
    return 'x: {x:0.2f}\ny: {y:0.2f}'.format(x=x, y=y) 

class FollowDotCursor(object): 
    """Display the x,y location of the nearest data point. 
    """ 
    def __init__(self, ax, x, y, tolerance=5, formatter=fmt, offsets=(-20, 20)): 
     try: 
      x = np.asarray(x, dtype='float') 
     except (TypeError, ValueError): 
      x = np.asarray(mdates.date2num(x), dtype='float') 
     y = np.asarray(y, dtype='float') 
     self._points = np.column_stack((x, y)) 
     self.offsets = offsets 
     self.scale = x.ptp() 
     self.scale = y.ptp()/self.scale if self.scale else 1 
     self.tree = spatial.cKDTree(self.scaled(self._points)) 
     self.formatter = formatter 
     self.tolerance = tolerance 
     self.ax = ax 
     self.fig = ax.figure 
     self.ax.xaxis.set_label_position('top') 
     self.dot = ax.scatter(
      [x.min()], [y.min()], s=130, color='green', alpha=0.7) 
     self.annotation = self.setup_annotation() 
     plt.connect('motion_notify_event', self) 

    def scaled(self, points): 
     points = np.asarray(points) 
     return points * (self.scale, 1) 

    def __call__(self, event): 
     ax = self.ax 
     # event.inaxes is always the current axis. If you use twinx, ax could be 
     # a different axis. 
     if event.inaxes == ax: 
      x, y = event.xdata, event.ydata 
     elif event.inaxes is None: 
      return 
     else: 
      inv = ax.transData.inverted() 
      x, y = inv.transform([(event.x, event.y)]).ravel() 
     annotation = self.annotation 
     x, y = self.snap(x, y) 
     annotation.xy = x, y 
     annotation.set_text(self.formatter(x, y)) 
     self.dot.set_offsets((x, y)) 
     bbox = ax.viewLim 
     event.canvas.draw() 

    def setup_annotation(self): 
     """Draw and hide the annotation box.""" 
     annotation = self.ax.annotate(
      '', xy=(0, 0), ha = 'right', 
      xytext = self.offsets, textcoords = 'offset points', va = 'bottom', 
      bbox = dict(
       boxstyle='round,pad=0.5', fc='yellow', alpha=0.75), 
      arrowprops = dict(
       arrowstyle='->', connectionstyle='arc3,rad=0')) 
     return annotation 

    def snap(self, x, y): 
     """Return the value in self.tree closest to x, y.""" 
     dist, idx = self.tree.query(self.scaled((x, y)), k=1, p=1) 
     try: 
      return self._points[idx] 
     except IndexError: 
      # IndexError: index out of bounds 
      return self._points[0] 

x = np.random.normal(0,1.0,100) 
y = np.random.normal(0,1.0,100) 
fig, ax = plt.subplots() 

cursor = FollowDotCursor(ax, x, y, formatter=fmt, tolerance=20) 
scatter_plot = plt.scatter(x, y, facecolor="b", marker="o") 

#update the colour 
new_facecolors = ["r","g"]*50 
scatter_plot.set_facecolors(new_facecolors)  

plt.show() 

enter image description here

+0

我正在寻找改变一个或多个点的风格(同时使用套索或选择器),因此添加并跟踪多个添加的散点并不理想。不过,我喜欢跟踪注释的想法,snipet的代码非常好。 – ebarr 2013-03-17 12:39:20

1

很确定没有办法做到这一点。 scatter已将您的数据转换为路径集合,并且不再具有需要执行此操作的元数据(即,它不知道为什么要绘制形状的语义,它只是具有要绘制的形状列表)。

您也可以使用set_array更新颜色(因为PathCollectionScalerMappable的子类)。

如果你想这样做(并有一个相当小的点数),你可以手工管理的路径。

其他更简单的选项是使用Line2D对象(因为您不在此示例中缩放标记的大小)使用两个(或多个,每个对应于每个形状/颜色组合)linestyle='none'Line2D对象上的选取器事件会让您回到离您最近的点。

对不起,这是漫不经心。

+0

谢谢,我已经有了一个烂摊子各地使用多个Line2D中的物体,但对于功能我在寻找这种解决方案是不是真的可行的(或者更确切地说,它是可行但比它更值得付出)。 – ebarr 2013-03-16 20:12:01

+0

你如何用'set_array'更新颜色? – endolith 2014-05-31 02:17:21