Phlya Phlya - 22 days ago 7
Python Question

"Repel" annotations in matplotlib?

I recently saw this package for R/ggplot2, which lets one to have multiple annotations on a plot and automatically adjust their position to minimize overlap, and this way improve the readability. Is there anything
similar available for python/matplotlib?

EDIT:
I've found Matplotlib overlapping annotations / text and it looks promising, but seems like the result is inferior to the R package.

Example:

from matplotlib import pyplot as plt
import numpy as np
xs = np.arange(10, step=0.1)+np.random.random(100)*3
ys = np.arange(10, step=0.1)+np.random.random(100)*3
labels = np.arange(100)
plt.scatter(xs, ys)
for x, y, s in zip(xs, ys, labels):
plt.text(x, y, s)
plt.show()


enter image description here

You can see that even such short labels create a crazy mess when the data density is high.

Answer

[12-11-2016 updated the code and second figure again since the library has been significantly improved since then]

ANSWER COMPLETELY REWRITTEN

I've made a small library for this purpose, which works similarly to above mentioned ggrepel: https://github.com/Phlya/adjustText

With switched off repelling from points it produces something decent even for this difficult example:

from matplotlib import pyplot as plt
from adjustText import adjust_text
import numpy as np

np.random.seed(2016)
xs = np.arange(10, step=0.1)+np.random.random(100)*3
ys = np.arange(10, step=0.1)+np.random.random(100)*3
labels = np.arange(100)

f = plt.figure()
scatter = plt.scatter(xs, ys, s=15, c='r', edgecolors='w')
texts = []
for x, y, s in zip(xs, ys, labels):
    texts.append(plt.text(x, y, s))

plt.show()

enter image description here

adjust_text(texts, force_points=0.2, force_text=0.2, expand_points=(1,1), expand_text=(1,1),
            arrowprops=dict(arrowstyle="-", color='black', lw=0.5))
plt.show()

enter image description here

Comments