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

하나 또는 여러 포인트의 스타일을 같은 시간 (올가미 또는 선택 도구 사용)으로 변경하려는 경우 여러 추가 된 분산 점을 추가하고 추적하는 것이 이상적이지 않습니다. 그러나 주석을 추적한다는 아이디어가 좋고 코드 스 니펫이 매우 잘 작동합니다. – ebarr

1

확실히 할 방법이 없습니다. scatter은 데이터를 경로 모음으로 바꾸 었으며 더 이상이 작업을 수행하는 데 필요한 메타 데이터가 없습니다 (즉, 왜 모양을 그리는 지에 대한 의미를 알지 못합니다. 그려야 할 모양 목록 만 있습니다.).

set_array으로 색을 업데이트 할 수도 있습니다 (PathCollectionScalerMappable의 하위 클래스 임).

이 작업을 수행하려는 경우 (합리적으로 적은 수의 포인트가있는 경우) 수동으로 경로를 관리 할 수 ​​있습니다.

Line2D 개체는 linestyle='none'으로 2 개 (또는 원하는 모양/색상 조합 각각에 하나씩) Line2D 개의 개체 (이 예에서는 마커 크기를 조정하지 않기 때문에)를 사용하는 것입니다. Line2D 개체의 피커 이벤트는 가장 가까운 지점을 알려줍니다.

죄송합니다.

+0

덕분에, 나는 이미 여러 Line2D의 양쪽 모두의 객체를 사용하여 주위를 엉망으로 했어,하지만 기능을 정말 가능하지 않습니다이 솔루션을 찾고 있어요 (또는 오히려, 그것은이다 실현 가능하지만 노력보다 더 가치 있음). – ebarr

+0

'set_array'로 어떻게 색을 업데이트하겠습니까? – endolith

관련 문제