2013-03-16 171 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