2016-10-13 2 views
0

이 질문은 this에 이어집니다. 레이블이 지정된 지점이 정확히 일치하거나 거의 일치 할 때 주석을 여전히 읽을 수 있도록 배치하려면 어떻게해야합니까? 프로그래밍 방식의 솔루션이 필요합니다. 오프셋 튜닝은 옵션이 아닙니다. 추한 라벨 샘플 :일치점/겹치는 주석이있는 Pyplot Label Scatter Plot

import numpy as np 
import matplotlib.pyplot as plt 

np.random.seed(0) 
N = 10 
data = np.random.random((N, 4)) 
data[1, :2] = data[0, :2] 
data[-1, :2] = data[-2, :2] + .01 
labels = ['point{0}'.format(i) for i in range(N)] 
plt.subplots_adjust(bottom = 0.1) 
plt.scatter(
    data[:, 0], data[:, 1], marker = 'o', c = data[:, 2], s = data[:, 3]*1500, 
    cmap = plt.get_cmap('Spectral')) 
for label, x, y in zip(labels, data[:, 0], data[:, 1]): 
    plt.annotate(
     label, 
     xy = (x, y), xytext = (-20, 20), 
     textcoords = 'offset points', ha = 'right', va = 'bottom', 
     bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5), 
     arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0')) 

plt.show() 

enter image description here

+0

나는하기 matplotlib가 자동으로이 작업을 수행 할 것입니다 확신 해요. 레이블이 교차하지 않도록 레이블의 각도를 반복적으로 결정해야합니다. 이것은 어려운 작업입니다. 특히 유효한 솔루션에 대한 보장이 없기 때문입니다. 점이 다른 점으로 둘러싸여 있으면 특정 반경 내에 레이블을 실제로 배치 할 수 없습니다. 나는 당신이 처음부터 경험적으로 만들어야한다고 믿는다. –

답변

1

이 마지막 점 사이의 거리를 가지고, 임계 값 홀드를 설정 한 다음 그에 따라 X, Y 텍스트를 뒤집습니다. 아래를 참조하십시오. 결과

import numpy as np 
import matplotlib.pyplot as plt 

np.random.seed(0) 
N = 10 
data = np.random.random((N, 4)) 
data[1, :2] = data[0, :2] 
data[-1, :2] = data[-2, :2] + .01 
labels = ['point{0}'.format(i) for i in range(N)] 
plt.subplots_adjust(bottom = 0.1) 
plt.scatter(
    data[:, 0], data[:, 1], marker = 'o', c = data[:, 2], s = data[:, 3]*1500, 
    cmap = plt.get_cmap('Spectral')) 

old_x = old_y = 1e9 # make an impossibly large initial offset 
thresh = .1 #make a distance threshold 

for label, x, y in zip(labels, data[:, 0], data[:, 1]): 
    #calculate distance 
    d = ((x-old_x)**2+(y-old_y)**2)**(.5) 

    #if distance less than thresh then flip the arrow 
    flip = 1 
    if d < .1: flip=-2 

    plt.annotate(
     label, 
     xy = (x, y), xytext = (-20*flip, 20*flip), 
     textcoords = 'offset points', ha = 'right', va = 'bottom', 
     bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5), 
     arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0')) 
    old_x = x 
    old_y = y 

plt.show() 

:

enter image description here

+0

나는 확실히 그것이 올바른 방향이라고 생각한다. 근접성에 대한 테스트는 근처에있는 점이 데이터 배열에서 연속적으로 작동하는 경우에만 작동합니다. 예를 들어 데이터 [1, : 2] = 데이터 [0, : 2]를 데이터 [5, : 2] = 데이터 [0, : 2]로 변경하십시오. 이 솔루션은 방탄하지 않아도됩니다. 서로 위에 10 점이 있다면 아무리 뜨거운 엉망으로 보일 것입니다. – user2133814

+0

나는 scipy spatial KDTree를 사용하여 솔루션을 연구 중이다. – user2133814

0

는 여기에 내가 함께 끝난거야. 모든 상황에 완벽하지는 않지만이 예제 문제에서는 원활하게 작동하지 않지만 내 요구 사항에 충분히 적합하다고 생각합니다. 당신의 대답을 위해 Dan이 올바른 방향으로 나를 가리켜 주셔서 감사합니다.

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


def get_label_xy(tree, thresh, data, i): 
    neighbors = tree.query_ball_point([data[i, 0], data[i, 1]], thresh) 
    if len(neighbors) == 1: 
     xy = (-30, 30) 
    else: 
     mean = np.mean(data[:, :2][neighbors], axis=0) 

     if mean[0] == data[i, 0] and mean[1] == data[i, 1]: 
      if i < np.max(neighbors): 
       xy = (-30, 30) 
      else: 
       xy = (30, -30) 
     else: 
      angle = np.arctan2(data[i, 1] - mean[1], data[i, 0] - mean[0]) 

      if angle > np.pi/2: 
       xy = (-30, 30) 
      elif angle > 0: 
       xy = (30, 30) 
      elif angle > -np.pi/2: 
       xy = (30, -30) 
      else: 
       xy = (-30, -30) 
    return xy 


def labeled_scatter_plot(data, labels): 
    plt.subplots_adjust(bottom = 0.1) 
    plt.scatter(
     data[:, 0], data[:, 1], marker = 'o', c = data[:, 2], s = data[:, 3]*1500, 
     cmap = plt.get_cmap('Spectral')) 

    tree = cKDTree(data[:, :2]) 
    thresh = .1 

    for i in range(data.shape[0]): 
     xy = get_label_xy(tree, thresh, data, i) 

     plt.annotate(
      labels[i], 
      xy = data[i, :2], xytext = xy, 
      textcoords = 'offset points', ha = 'center', va = 'center', 
      bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5), 
      arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0')) 


np.random.seed(0) 
N = 10 
data = np.random.random((N, 4)) 
data[1, :2] = data[0, :2] 
data[-1, :2] = data[-2, :2] + .01 
data[5, :2] = data[4, :2] + [.05, 0] 
data[6, :2] = data[4, :2] + [.05, .05] 
data[7, :2] = data[4, :2] + [0, .05] 
labels = ['point{0}'.format(i) for i in range(N)] 

labeled_scatter_plot(data, labels) 

plt.show() 

enter image description here

관련 문제