3

I made a plot of a time-series and added a vertical line. This vertical line indicates that something happened and I would like to add an annotation to that vertical line. But I have no idea how to do this :/

Here is the code to plot the data and the vertical line:

I adjusted the example code which now contains data (although they are somehow weird, but show the problem: The notations at the vertical lines should be above the plot area, and two of the text boxes are now overlapping. This overlapping should be avoided by aranging them differently and using dashed lines or something to point to the vertical line where the text belongs to

%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

They used the package lineid_plot which can be found here

However, this package has problems with the datetime format somehow. I am really sorry for this bad example code shown here, but I am really a beginner and I hope somebaody can help to solve my problem.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Heiko104
  • 31
  • 1
  • 3
  • Maybe [this post](https://stackoverflow.com/questions/13413112/creating-labels-where-line-appears-in-matplotlib-figure)? – JohanC Jan 08 '20 at 22:58
  • Helps not really. The annotations overlap and it looks not good. I found this [link](https://github.com/phn/lineid_plot) but it has problems with the dates format... – Heiko104 Jan 14 '20 at 22:22
  • Without knowing how your data looks like, it's hard to help. Maybe you need to convert your date array to matplotlib format with something like `date= [pd.to_datetime(t) for t in date]`. If you try to write text outside of the inner plot area, you'd need something like `txt = ax.text(..., clip_on=False)`. – JohanC Jan 14 '20 at 22:40
  • @JohanC I adjusted the example code with data that hopefully explains the problem I have in a better way. – Heiko104 Jan 16 '20 at 23:33

1 Answers1

4

Here is some code showing annotation similar to the requested ones.

Explanation of the code:

  • ymin, ymax = plt.ylim() find the current limits of the y-axis, ymax is used to position the text
  • arrowprops = {'width': 1, 'headwidth': 1, 'headlength': 1, 'shrink':0.05} an "arrow" is drawn from the top of the line to the text itself. Setting 'headwidth' to 1 makes it a line instead of an arrow. One could draw a real arrow at place without overlap.
  • ax0.annotate('BigNews1',
    • xy=('2017-07-02 16:30:00', ymax), the top of the vertical line
    • xytext=(10, 25), textcoords='offset points', the position of the text relative to the top, measured in "points" (compare to 12 point as fontsize)
    • rotation=90, va='bottom', ha='center', text 90 degrees rotated
    • annotation_clip=False, allow to write outside the box of the plot
    • arrowprops=arrowprops) set the properties of the "arrows"

What's still missing, is a good algorithm to move the xytext when there are overlaps.

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

This is how the top looks like with these annotations:

top of the plot

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • Thanks a lot @JohanC! This is exactly what I wanted. How could such an algorithm to move the xytext when there are overlaps look like? Is this very difficult? The package [lineid_plot](https://github.com/phn/lineid_plot) seems to have this included but has problems with the x-axis coordinates since this are dates... – Heiko104 Jan 17 '20 at 13:52
  • It doesn't seem too difficult. It's some work to get it running for your case, depending on the specific requirements. – JohanC Jan 17 '20 at 15:12
  • There are not really specific requirements. Just an algorithm that horizontally shifts the annotation boxes such that they do not overlapp anymore. It should be independent from the display size or plot size used, so based on the data coordinate system would be good I guess. – Heiko104 Jan 18 '20 at 08:38