【问题标题】:Python matplotlib - how to add annotation to vertical linePython matplotlib - 如何向垂直线添加注释
【发布时间】:2020-04-26 12:21:19
【问题描述】:

我绘制了一个时间序列图并添加了一条垂直线。 这条垂直线表示发生了一些事情,我想在这条垂直线上添加注释。但我不知道该怎么做:/

这是绘制数据和垂直线的代码:

我调整了现在包含数据的示例代码(虽然它们有点奇怪,但显示了问题:垂直线处的符号应该在绘图区域上方,并且两个文本框现在重叠。应该通过以不同方式排列它们并使用虚线或其他东西指向文本所属的垂直线来避免这种重叠

%matplotlib inline
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

data = np.array([['2017-07-01 16:30:00', '2017-07-02 16:30:00', '2017-07-03 16:30:00', '2017-07-04 16:30:00', '2017-07-05 16:30:00', '2017-07-06 16:30:00'],
                [1.4, 1.3, 2, 0.5, 0.002337, 3 ]])

fig2 = plt.figure(figsize=(16,9))

# TRAIN NORTH
ax0 = fig2.add_subplot(111)
ax0.scatter(x=data[0],y=data[1], color='green', marker='.')
#ax0.legend(df.iloc[0,0],loc='best')
#ax0.set_ylim(0,0.006)
# vertical lines when something happened
ax0.axvline('2017-07-02 16:30:00', color='green')
ax0.axvline('2017-07-02 16:30:00', color='green')
ax0.axvline('2017-07-05 16:30:00', color='green')
ax0.text('2017-07-02 16:30:00',0.005,'BigNews1',rotation=90,va='top')
ax0.text('2017-07-02 16:30:00',0.005,'BlaBlaBla2',rotation=90,va='top')
ax0.text('2017-07-05 16:30:00',0.005,'BigNews3',rotation=90,va='top')

I would like to have something like shown in this picture

他们使用了可以在here找到的包lineid_plot

但是,这个包在某种程度上存在日期时间格式的问题。 对于这里显示的这个糟糕的示例代码,我真的很抱歉,但我真的是一个初学者,我希望 somebaody 可以帮助解决我的问题。

【问题讨论】:

  • 可能是this post?
  • 帮助不大。注释重叠,看起来不太好。我找到了这个link,但它的日期格式有问题...
  • 在不知道您的数据是什么样子的情况下,很难提供帮助。也许您需要使用 date= [pd.to_datetime(t) for t in date] 之类的东西将您的日期数组转换为 matplotlib 格式。如果您尝试在内部绘图区域之外编写文本,则需要 txt = ax.text(..., clip_on=False) 之类的内容。
  • @JohanC 我用数据调整了示例代码,希望能以更好的方式解释我遇到的问题。

标签: python matplotlib annotations time-series data-annotations


【解决方案1】:

这里是一些显示annotation 的代码,类似于请求的代码。

代码说明:

  • ymin, ymax = plt.ylim()查找当前y轴范围,ymax用于定位文字
  • arrowprops = {'width': 1, 'headwidth': 1, 'headlength': 1, 'shrink':0.05} 从行的顶部到文本本身绘制了一个“箭头”。将 'headwidth' 设置为 1 使其成为一条线而不是箭头。可以在没有重叠的地方画出真正的箭头。
  • ax0.annotate('BigNews1',
    • xy=('2017-07-02 16:30:00', ymax),竖线顶端
    • xytext=(10, 25), textcoords='offset points', 文本相对于顶部的位置,以“点”为单位(与字体大小为 12 点相比)
    • rotation=90, va='bottom', ha='center', 文字旋转 90 度
    • annotation_clip=False,允许在剧情框外写字
    • arrowprops=arrowprops)设置“箭头”的属性

仍然缺少的是在有重叠时移动 xytext 的好算法。

import matplotlib.pyplot as plt
import numpy as np

data = np.array([['2017-07-01 16:30:00', '2017-07-02 16:30:00', '2017-07-03 16:30:00', '2017-07-04 16:30:00', '2017-07-05 16:30:00', '2017-07-06 16:30:00'],
                [1.4, 1.3, 2, 0.5, 0.002337, 3 ]])

fig2 = plt.figure(figsize=(16,9))
ax0 = fig2.add_subplot(111)
ax0.scatter(x=data[0],y=data[1], color='green', marker='.')
ymin, ymax = plt.ylim()
# vertical lines when something happened
ax0.axvline('2017-07-02 16:30:00', color='green')
ax0.axvline('2017-07-02 16:30:00', color='green')
ax0.axvline('2017-07-05 16:30:00', color='green')

ymin, ymax = plt.ylim()
arrowprops = {'width': 1, 'headwidth': 1, 'headlength': 1, 'shrink':0.05 }
ax0.annotate('BigNews1', xy=('2017-07-02 16:30:00', ymax), xytext=(-10, 25), textcoords='offset points',
             rotation=90, va='bottom', ha='center', annotation_clip=False, arrowprops=arrowprops)
ax0.annotate('BlaBlaBla2', xy=('2017-07-02 16:30:00', ymax), xytext=(10, 25), textcoords='offset points',
             rotation=90, va='bottom', ha='center', annotation_clip=False, arrowprops=arrowprops)
ax0.annotate('BigNews3', xy=('2017-07-05 16:30:00', ymax), xytext=(0, 25), textcoords='offset points',
             rotation=90, va='bottom', ha='center', annotation_clip=False, arrowprops=arrowprops)
plt.tight_layout()
plt.show()

这是带有这些注释的顶部的样子:

【讨论】:

  • 非常感谢@JohanC!这正是我想要的。当有重叠时,这样一种算法如何移动 xytext?这很难吗? lineid_plot 包似乎包含了这个,但是 x 轴坐标有问题,因为这是日期......
  • 似乎并不太难。根据具体要求,让它为您的案例运行是一些工作。
  • 没有真正的具体要求。只是一种水平移动注释框以使其不再重叠的算法。它应该与所使用的显示大小或绘图大小无关,因此我猜基于数据坐标系会很好。