1

I would like to adapt the python code here: Is it possible to get color gradients under curve in matplotlb? so that instead of vertical gradient in color, the gradient is a function of the vertical difference between the 2 curves. So, if the curves are diverging, the color gets darker.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon
np.random.seed(1977)

def main():
    for _ in range(2):
        gradient_fill(*generate_data(100))
    plt.show()

def generate_data(num):
    x = np.linspace(0, 100, num)
    y = np.random.normal(0, 1, num).cumsum()
    return x, y

def gradient_fill(x, y, fill_color=None, ax=None, **kwargs):
    """
    Plot a line with a linear alpha gradient filled beneath it.

    Parameters
    ----------
    x, y : array-like
        The data values of the line.
    fill_color : a matplotlib color specifier (string, tuple) or None
        The color for the fill. If None, the color of the line will be used.
    ax : a matplotlib Axes instance
        The axes to plot on. If None, the current pyplot axes will be used.
    Additional arguments are passed on to matplotlib's ``plot`` function.

    Returns
    -------
    line : a Line2D instance
        The line plotted.
    im : an AxesImage instance
        The transparent gradient clipped to just the area beneath the curve.
    """
    if ax is None:
        ax = plt.gca()

    line, = ax.plot(x, y, **kwargs)
    if fill_color is None:
        fill_color = line.get_color()

    zorder = line.get_zorder()
    alpha = line.get_alpha()
    alpha = 1.0 if alpha is None else alpha

    z = np.empty((100, 1, 4), dtype=float)
    rgb = mcolors.colorConverter.to_rgb(fill_color)
    z[:,:,:3] = rgb
    z[:,:,-1] = np.linspace(0, alpha, 100)[:,None]

    xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max()
    im = ax.imshow(z, aspect='auto', extent=[xmin, xmax, ymin, ymax],
                   origin='lower', zorder=zorder)

    xy = np.column_stack([x, y])
    xy = np.vstack([[xmin, ymin], xy, [xmax, ymin], [xmin, ymin]])
    clip_path = Polygon(xy, facecolor='none', edgecolor='none', closed=True)
    ax.add_patch(clip_path)
    im.set_clip_path(clip_path)

    ax.autoscale(True)
    return line, im

main()

This is what the program results in:

enter image description here

Community
  • 1
  • 1
user308827
  • 21,227
  • 87
  • 254
  • 417

1 Answers1

3

A simple way would be to create an irregular mesh between the x1,y1 and x2,y2 coordinates with a z value equal to the difference y1-y2 at a given x. Here I'm using scipy's griddata but there's a few different techniques.

Something like this...

# some data..

x = np.array([[np.linspace(0,100,200)],[np.linspace(0,100,200)]])
y1 = np.linspace(0,100,200)
y2 = [2*i + 2 for i in y1]
y = np.array([y1,y2])
z = np.array([y2 - y1,y2 - y1]) # distance value

# Method

x=x.ravel()              #Flat input into 1d vector
x=(x[x!=np.isnan])   #eliminate any NaN
y=y.ravel()
y=(y[y!=np.isnan])
z=z.ravel()
z=(z[z!=np.isnan])

znew = griddata((x, y), z, (x[None,:], y[:,None]), method='linear') # grid the data and interpolate z so z will be equivalent at all equal x coords.

levels = np.linspace(z.min(), z.max(), 100)
plt.ylabel('Y', size=15)
plt.xlabel('X', size=15)
cmap = plt.cm.Blues
cs = plt.contourf(x , y, znew, levels=levels, cmap=cmap) # colour according to z
cbar = plt.colorbar(cs)
cbar.set_label('distance', rotation=90, fontsize=15) 
cbar.set_ticks([50,100])

plt.show()

enter image description here

smashbro
  • 1,094
  • 1
  • 9
  • 19
  • thanks @smashbro, will your solution work in case of the curves in my example? i.e. they are not monotonically increasing – user308827 Dec 23 '15 at 04:26
  • Should do, x and y arrays are just coordinates so as long as you correctly create the 2d mesh the assignment of z values is straight forward. – smashbro Dec 23 '15 at 06:20