How to fill color x with varying alpha with axhspan

I would like to fill red between two lines with the alpha of the reds changing based on a list. Essentially a custom gradient of reds, but it’s the alpha that makes the red appear darker or lighter.

reproducible example:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(range(20))
y = [8,9,12,14] # respective y values
a = [0.3,0.1,0.5,0.7]
ax.axhspan(8, 14, alpha=0.5, color='red')

plt.show()

plot

this may be useful in determining the colours required as it turns rgb to rgba. Maybe there’s a way to map these colours between the two lines using axhspan?

from matplotlib.colors import to_rgb
r, g, b = to_rgb('red')
alpha_arr = a
c = [(r, g, b, alpha) for alpha in alpha_arr]

Answer

To get smooth gradients, you can use interpolation. To avoid problems with thin bands and alpha, you can use imshow (or maybe pcolor) instead:

import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import numpy as np

a = [0.3, 0.1, 0.5, 0.7]
y = [8, 9, 12, 14]

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(14, 5))

ax1.plot(range(20))
bounds = np.linspace(y[0], y[-1], 100)
for b0, b1 in zip(bounds[:-1], bounds[1:]):
    ax1.axhspan(b0, b1, alpha=np.interp((b0 + b1) / 2, y, a), facecolor='red', edgecolor='none')
ax1.set_title('Using many thin axhspans')

ax2.plot(range(20))
xmin, xmax = ax2.get_xlim()
ymin, ymax = ax2.get_ylim()
cmap = LinearSegmentedColormap.from_list('', ['white', 'red'])
ax2.imshow(np.interp(np.linspace(y[0], y[-1], 100), y, a).reshape(-1, 1),
           cmap=cmap, vmin=0, vmax=1,
           extent=[xmin, xmax, y[0], y[-1]], origin='lower',
           interpolation='bilinear', aspect='auto', zorder=-1)
ax2.set_xlim(xmin, xmax)
ax2.set_ylim(ymin, ymax)
ax2.set_title('Using imshow')
fig.tight_layout()
plt.show()

gradient with imshow vs axhspan