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>Here's what I ended up with.  Not perfect for all situations, and it doesn't even work smoothly for this example problem, but I think it is good enough for my needs.  Thanks Dan for your answer pointing me in the right direction.</p>

<pre><code>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 &lt; 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 &gt; np.pi / 2:
                xy = (-30, 30)
            elif angle &gt; 0:
                xy = (30, 30)
            elif angle &gt; -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 = '-&gt;', 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()
</code></pre>

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

python matplotlib