Shade/Fill between Arbitrary Matplotlib Region Based On Axes, Not Data

Viewed 196

Is it possible to fill between arbitrary regions of a chart not based upon the plotted data but the x-axis range?

In the time series plot below, I shade the region between 8pm and 8am using this fill_between call:

ax3.fill_between(d[0], 0, ax3.get_ylim()[1], where=fill, facecolor=face_color )

Where fill is determined based upon the data being recorded during those hours:

fill = [False if dt.time(8,0,0) < t.time() < dt.time(20,0,0) else True for t in d[0]]

This works fine for the first two shaded regions.

enter image description here

The issue is that sometimes, the recorded data will not be present for those hours and so the shaded region is too small and does not represent the appropriate hours (see the 3rd shaded region in the chart)

I realise I could do this by creating another data set with the appropriate x value range and then set it to invisible but I wanted to know if there is another way.

1 Answers

You can construct an array of datetime objects which span the same range as your x data and use that to populate the fill array,

import numpy as np
# ...
fill = [False if dt.time(8, 0, 0) < t.time() < dt.time(20, 0, 0) else True for t in 
        np.arange(x[0], x[-1]+60, 60, dtype='datetime64').astype(dt.datetime)]

Complete example:

import matplotlib.pyplot as plt
import datetime as dt
import numpy as np

plt.figure(figsize=(12,3))
x = np.arange('2020-04-03T00:00Z', '2020-04-05T00:30Z', np.random.randint(50, 70), 
              dtype='datetime64[m]')
x = np.delete(x, np.s_[4:10])
x = np.delete(x, np.s_[25:32])
y = np.sin(np.linspace(0, np.pi*2, len(x))*2 + np.random.random(len(x)) - 0.5)
plt.plot(x, y)

dx = np.arange(x[0], x[-1]+60, 60, dtype='datetime64[m]').astype(dt.datetime)
fill = [False if dt.time(8, 0, 0) < t.time() < dt.time(20, 0, 0) else True for t in dx]
plt.fill_between(dx, plt.ylim()[0], plt.ylim()[1], where=fill, fc='grey', alpha=0.2)

plt.tick_params(axis='x', rotation=90)
plt.subplots_adjust(bottom=0.3)
plt.show()

enter image description here

Related