2016-10-13 34 views
0

この質問は、thisの続きです。ラベル付きの点が正確に一致していたり​​、ほぼ一致していても、注釈を読み込み可能にするにはどうすればよいですか?私はプログラム的な解決策が必要です、手を調整する手はオプションではありません。醜いラベル付きサンプル:一致点/重複するアノテーションを含むPyplot Label散布図

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

は、ここで私がなってしまったものです。すべての状況には完璧ではなく、このサンプルの問題ではうまく機能しませんが、私のニーズには十分だと思います。あなたの答えが正しい方向に私を指してくれてありがとうダン。

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