1

I am trying to fill an area under my graph using fill_between. I can get it to work with a single colour (say red), but I cannot get a gradient under the graph using a colourmap. From the fill_between documentation I understand that I should be able to fill the area with a gradient using a colourmap but I cannot make it work. The colourmap is loaded properly, because I can access the separate colours from the loaded colourmap and fill the area with said colour.

Minimal working example:

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

colourmap = mpl.cm.get_cmap('jet')
singlecolor = mpl.cm.get_cmap('jet')(500)

xx = np.arange(0,10,0.1)
yy = xx*np.exp(-xx)

plt.figure()
plt.plot(xx,yy)
plt.fill_between(xx,yy,cmap=colourmap,alpha=0.3)
# plt.fill_between(xx,yy,color=singlecolor,alpha=0.3)
plt.show()

I get no error messages, and I am using Python 3.8. How can I fill using a colourmap?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
quantum girl
  • 53
  • 2
  • 8
  • [This answer](https://stackoverflow.com/questions/18215276/how-to-fill-rainbow-color-under-a-curve-in-python-matplotlib) is outdated, so there may be a simpler solution method. This is a method of stacking gradient squares. – r-beginners Jun 16 '21 at 13:14

2 Answers2

5

You can extract the polygon of the fill, and then use that as a clip polygon for an image. As imshow() sets tight x and y limits, you can reapply the limits. Use np.linspace(0, 1, 256).reshape(-1, 1) for a horizontal gradient, or ...resphape(1,-1) for a vertical gradient.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 200)
y1 = np.sin(x)
y2 = x * (10 - x) / 25

polygon = plt.fill_between(x, y1, y2, lw=0, color='none')
xlim = plt.xlim()
ylim = plt.ylim()
verts = np.vstack([p.vertices for p in polygon.get_paths()])
gradient = plt.imshow(np.linspace(0, 1, 256).reshape(1, -1), cmap='turbo', aspect='auto',
                      extent=[verts[:, 0].min(), verts[:, 0].max(), verts[:, 1].min(), verts[:, 1].max()])
gradient.set_clip_path(polygon.get_paths()[0], transform=plt.gca().transData)
plt.xlim(xlim)
plt.ylim(ylim)
plt.show()

fill_between with gradient

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • Excellent answer, thanks much. Note for users with logarithmic plots: be sure to set your scales to log [e.g., plt.yscale('log')] before this code. If you call ylim = plt.ylim() first, the final lines of code will revert you to linear bounds, which will likely make your data impossible to see. – barriboy Oct 13 '22 at 11:44
2

You can use the following code which will fill 100 times a small trapezoid in order to fill the area under the curve with the whole colormap palette.

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

colourmap = mpl.cm.get_cmap('jet')

xx = np.arange(0,10,0.1)
yy = xx*np.exp(-xx)

plt.plot(xx,yy)
normalize = mpl.colors.Normalize(vmin=yy.min(), vmax=yy.max())
npts = 100
for i in range(npts - 1):
    plt.fill_between([xx[i], xx[i+1]],
                     [yy[i], yy[i+1]],
                     color=colourmap(normalize(yy[i]))
                     ,alpha=0.6)
plt.show()

This will result in the following graph:

enter image description here

Antoine Dubuis
  • 4,974
  • 1
  • 15
  • 29