Подтвердить что ты не робот

Matplotlib: как комментировать точку на разброс автоматически помещенной стрелки?

если я создаю график рассеяния с matplotlib:

plt.scatter(randn(100),randn(100))
# set x, y lims
plt.xlim([...])
plt.ylim([...])

Я хотел бы аннотировать заданную точку (x, y) стрелкой, указывающей на нее, и меткой. Я знаю, что это можно сделать с помощью annotate, но я бы хотел, чтобы стрелка и ее метка были размещены "оптимально" таким образом, чтобы, если это возможно (учитывая текущие масштабы/пределы оси), стрелка и метка не пересекаются с другими точками. например, если вы хотите обозначить точку выброса. Есть ли способ сделать это? он не должен быть совершенным, а просто интеллектуальным размещением стрелки/метки, учитывая только координаты (x,y) точки, которую нужно пометить. спасибо.

4b9b3361

Ответ 1

В принципе, нет, нет.

Механизмы компоновки, которые обрабатывают размещение меток карт, подобных этому, на удивление сложны и выходят за рамки matplotlib. (Пересечение перекрещиваемых ящиков на самом деле довольно плохой способ решить, где разместить метки. Какой смысл писать тонну кода для чего-то, что будет работать только в одном случае из 1000?)

Кроме того, из-за объема сложного текстового рендеринга, который делает matplotlib (например, латекс), невозможно определить объем текста без полного его первого (что довольно медленно).

Однако во многих случаях вы обнаружите, что использование прозрачной рамки за вашей меткой, помещенной в аннотате, является подходящим решением.

например.

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(1)
x, y = np.random.random((2,500))

fig, ax = plt.subplots()
ax.plot(x, y, 'bo')

# The key option here is `bbox`. I'm just going a bit crazy with it.
ax.annotate('Something', xy=(x[0], y[0]), xytext=(-20,20), 
            textcoords='offset points', ha='center', va='bottom',
            bbox=dict(boxstyle='round,pad=0.2', fc='yellow', alpha=0.3),
            arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.5', 
                            color='red'))

plt.show()

enter image description here

Ответ 2

Используйте adjustText (полное раскрытие, я написал его).

Пусть обозначают первые 10 точек. Единственным параметром, который я изменил, было снижение силы отталкивания от точек, поскольку их так много, и мы хотим, чтобы алгоритм занимал немного больше времени и размещал аннотации более тщательно.

import numpy as np
import matplotlib.pyplot as plt
from adjustText import adjust_text
np.random.seed(1)
x, y = np.random.random((2,500))

fig, ax = plt.subplots()
ax.plot(x, y, 'bo')
ts = []
for i in range(10):
    ts.append(plt.text(x[i], y[i], 'Something'+str(i)))
adjust_text(ts, x=x, y=y, force_points=0.1, arrowprops=dict(arrowstyle='->', 
color='red'))
plt.show()

введите описание изображения здесь Это не идеально, но точки здесь очень плотные, и иногда нет возможности разместить текст рядом с его целью, не перекрывая ни одного из них. Но все это автоматическое и простое в использовании, а также не позволяет меток перекрывать друг друга.

PS Он использует пересечения ограничивающих ящиков, но, скорее, я бы сказал!