matplotlib:如何在散点图上自动放置箭头注释点?

时间:2012-01-31 05:46:02

标签: python numpy matplotlib scipy

如果我使用matplotlib制作散点图:

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

我想用指向它的箭头和标签来注释给定的点(x, y)。我知道这可以用annotate来完成,但是我希望箭头和它的标签能够“以最佳方式”放置,如果有可能(给定当前的轴比例/限制)箭头和标签与其他点不重叠。例如,如果你想标记异常点。有没有办法做到这一点?它不必是完美的,而只是智能放置箭头/标签,只给出要标记的点的(x,y)坐标。感谢。

3 个答案:

答案 0 :(得分:34)

基本上,不,不,没有。

处理放置与此类似的地图标签的布局引擎非常复杂,超出了matplotlib的范围。 (边界框交叉点实际上是决定标记放置位置的一种相当差的方式。编写大量代码的重点是什么,只能在1000个单例中起作用?)

除此之外,由于matplotlib所做的复杂文本渲染量(例如乳胶),如果没有先完全呈现文本(这是相当慢的),就不可能确定文本的范围。

但是,在许多情况下,您会发现使用带有注释的标签后面的透明框是一种合适的解决方法。

E.g。

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

答案 1 :(得分:4)

使用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()

enter image description here 它并不理想,但这里的点非常密集,有时候没有办法将文本放在目标附近而不重叠任何一个。但它全部自动且易于使用,也不会让标签相互重叠。

PS 它使用了边界框交叉点,但我说成功了!

答案 2 :(得分:0)

另一个使用基于PhlyaadjustText_mtcars软件包的示例:

from adjustText import adjust_text
import matplotlib.pyplot as plt
                                                                                                                                
mtcars = pd.read_csv(
    "https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv"
)
                                                                                                                                
def plot_mtcars(adjust=False, force_points=1, *args, **kwargs):
    # plt.figure(figsize=(9, 6))
    plt.scatter(mtcars["wt"], mtcars["mpg"], s=15, c="r", edgecolors=(1, 1, 1, 0))
    texts = []
    for x, y, s in zip(mtcars["wt"], mtcars["mpg"], mtcars["model"]):
        texts.append(plt.text(x, y, s, size=9))
    plt.xlabel("wt")
    plt.ylabel("mpg")
    if adjust:
        plt.title(
            "force_points: %.1f\n adjust_text required %s iterations"
            % (
                force_points,
                adjust_text(
                    texts,
                    force_points=force_points,
                    arrowprops=dict(arrowstyle="-", color="k", lw=0.5),
                    **kwargs,
                ),
            )
        )
    else:
        plt.title("Original")
    return plt
                                                                                                                                
fig = plt.figure(figsize=(12, 12))
                                                                                                                                
force_points = [0.5, 1, 2, 4]
for index, k in enumerate(force_points):
    fig.add_subplot(2, 2, index + 1)
    plot_mtcars(adjust=True, force_points=k)

enter image description here