I would like to have an "annotation track" in Matplotlib, similar to similar functionality as in audio editing software - here an example from Audacity:
Basically, there are markers for regions that are labeled with text - and they are placed in a separate track, below - which is the "annotation track".
I came up with an example for something similar in Matplotlib:
import matplotlib as mpl
import matplotlib.pyplot as plt
from scipy import signal
import numpy as np
fig = plt.figure()
subplotpars = dict(left = 0.05, right=0.98, top=0.84, bottom=0.05, wspace=0.1)
gs = mpl.gridspec.GridSpec(2, 1, height_ratios=[9, 1], **subplotpars)
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
fig.subplots_adjust(hspace=0) # Remove horizontal space between axes
time_end = 100
num_elems = 10000
t_x = np.linspace(0, time_end, num_elems, endpoint=True)
sig_y_first = np.random.normal(0,1,num_elems)
# via https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.butter.html:
sos = signal.butter(N=10, Wn=15, btype='lowpass', fs=4000, output='sos') #
sig_y_filt = signal.sosfilt(sos, sig_y_first)
scalerA = np.linspace(0, 0.5, num_elems, endpoint=True)
scalerB = np.linspace(8, 0, num_elems, endpoint=True)
sig_y = np.multiply(sig_y_filt, scalerB) + np.multiply(sig_y_first, scalerA)
ax1.plot(t_x, sig_y)
xranges_bounds = [10, 100]
prev_xbound = 0
for ix, xbound in enumerate(xranges_bounds):
ax2.annotate("", (prev_xbound, 0.5), (xbound, 0.5), arrowprops={'arrowstyle':'<->'})
ax2.text(prev_xbound + (xbound-prev_xbound)/2, 0.5, "the {:02d} range".format(ix+1))
prev_xbound = xbound
plt.show()
The code results with:
I could find a better algo for centering the text labels, but in particular, what I am looking for, and quite really figure out, is:
- The textual labels should be "boxed" (with a line/stroke outline around the words, and possibly a different background color) - as well as centered
- In case the zoom level is such, that the visible region is too narrow to allow the entire text in the label to be printed (as is the case with "the 01 range"), I should just get a narrow box (with either no text; just the first few character(s) of the text; or "...") that fits - and the full text would be shown once the zoom level is large enough, so the width of the range is enough for the entire text.
Is there something like this already for Matplotlib?