Pyplot Label Scatter Plot with Coincident Points / Overlapping Annotations
<p>This question is a follow on to <a href="http://stackoverflow.com/questions/5147112/matplotlib-how-to-put-individual-tags-for-a-scatter-plot">this</a>.  How can I layout the annotations so they are still readable when the labeled points are exactly or nearly coincident?  I need a programmatic solution, hand tuning the offsets is not an option.  Sample with ugly labels:</p>

<pre><code>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 = '-&gt;', connectionstyle = 'arc3,rad=0'))

plt.show()
</code></pre>

<p><a href="https://i.stack.imgur.com/d07k0.png" rel="nofollow"><img src="https://i.stack.imgur.com/d07k0.png" alt="enter image description here"></a></p>

<p>Take the distance between the last point, set a threshold hold, and then flip the x,y text accordingly.  See below.</p>

<pre><code>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 &lt; .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 = '-&gt;', connectionstyle = 'arc3,rad=0'))
    old_x = x
    old_y = y

plt.show()
</code></pre>

<p>which results in:</p>

<p><a href="https://i.stack.imgur.com/aR6js.png" rel="nofollow"><img src="https://i.stack.imgur.com/aR6js.png" alt="enter image description here"></a></p>

python matplotlib