pythonmatplotlib

Matplotlib Stackplot Gradient


Is it possible to fill the "Odds" area with a gradient from left (green) to right (transparent)? I would like to do this in a plot to indicate uncertainty.

import numpy as np
import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y1 = [1, 1, 2, 3, 5]
y2 = [0, 4, 2, 6, 8]
y3 = [1, 3, 5, 7, 9]

y = np.vstack([y1, y2, y3])

labels = ["Fibonacci ", "Evens", "Odds"]

fig, ax = plt.subplots()
ax.stackplot(x, y1, y2, y3, labels=labels)
ax.legend(loc='upper left')
plt.show()

fig, ax = plt.subplots()
ax.stackplot(x, y)
plt.show()

enter image description here


Solution

  • Based on this answer, you could do:

    import numpy as np
    import matplotlib.pyplot as plt
    
    x = [1, 2, 3, 4, 5]
    y1 = [1, 1, 2, 3, 5]
    y2 = [0, 4, 2, 6, 8]
    y3 = [1, 3, 5, 7, 9]
    
    y = np.vstack([y1, y2, y3])
    
    labels = ["Fibonacci ", "Evens", "Odds"]
    
    fig, ax = plt.subplots()
    stacks = ax.stackplot(x, y1, y2, y3, labels=labels)
    ax.legend(loc='upper left')
    
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()
    
    stacks[-1].set_facecolor([1, 1, 1, 0])  # set original face color to transparent
    
    verts = np.vstack([p.vertices for p in stacks[-1].get_paths()])
    gradient = ax.imshow(
        np.linspace(0, 1, 256).reshape(1, -1), 
        cmap='Greens_r',  # reversed Green colormap
        aspect='auto',
        extent=[verts[:, 0].min(), verts[:, 0].max(), verts[:, 1].min(), verts[:, 1].max()]
    )
    
    gradient.set_clip_path(stacks[-1].get_paths()[0], transform=ax.transData)
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)
    
    fig.show()
    

    enter image description here