0

I have an anomaly threshold, annotated by an axhline in my plot. I wish to add markers and/or change the color of the line above this threshold. I have followed the following matplotlib tutorial:

https://matplotlib.org/3.1.1/gallery/lines_bars_and_markers/multicolored_line.html

As well as this utilized this question/answer here on SO:

How to plot multi-color line if x-axis is date time index of pandas

To produce this plot:

enter image description here

That looks pretty good, until you zoom in on a subset of the data:

enter image description here

Unfortunately, this solution doesn't seem to work for my purposes. I'm not sure if this is an error on my part or not, but clearly the lines are red below the threshold. An additional problem in my view is how clunky and long the code is:

import matplotlib.dates as mdates
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm

fig, ax = plt.subplots(figsize=(15,4))

inxval = mdates.date2num(dates.to_pydatetime())
points = np.array([inxval, scores]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)#[-366:]

cmap = ListedColormap(['b', 'r'])
norm = BoundaryNorm([0, thresh, 40], cmap.N)
lc = LineCollection(segments, cmap=cmap, norm=norm)

lc.set_array(scores)
ax.add_collection(lc)

monthFmt = mdates.DateFormatter("%Y")
ax.xaxis.set_major_formatter(monthFmt)
ax.xaxis.set_major_locator(mdates.YearLocator())

ax.autoscale_view()
# ax.axhline(y=thresh, linestyle='--', c='r')
plt.show()

dates and scores, and thresh generation aren't shown here, but can be re-produced with random numbers to make this code run

Question:

Why are the red lines in my chart sometimes falling below the threshold value? And is there a way to abbreviate the amount of code required for this purpose?

rocksNwaves
  • 5,331
  • 4
  • 38
  • 77
  • You'd need to include the intersection points with the horizontal line into the curve drawn. These changes clearly will make the code "more clunky". If you'd like simpler code, instead of changing the color of the curve, you could change the color of the background via `ax.axhspan()`. – JohanC Dec 21 '20 at 17:19
  • @JohanC, that's a great idea, thank you. I've seen that done too. I'll try it out! – rocksNwaves Dec 21 '20 at 17:21

1 Answers1

2

One option would be to draw two lines with the same data then use an invisible axhspan object to clip one of the lines under the threshold:

f, ax = plt.subplots()
x = np.random.exponential(size=500)
line_over, = ax.plot(x, color="b")
line_under, = ax.plot(x, color="r")
poly = ax.axhspan(0, 1, color="none")
line_under.set_clip_path(poly)

enter image description here

mwaskom
  • 46,693
  • 16
  • 125
  • 127
  • Nice. Haven't seen `set_clip_path` before. – Mr. T Dec 21 '20 at 17:53
  • Nice. Exactly as advertised except I had to tweak the `linewidth` parameter of the lower line so that the underlying line doesn't peak through. – rocksNwaves Dec 21 '20 at 19:25
  • 1
    @rocksNwaves You could also extend the idea to add a `poly_over` going from `thresh` to `x.max()` and use that to clip `line_over`. – mwaskom Dec 21 '20 at 19:33