2012-07-19 20 views
9

E 'possibile riempire tra le linee con un colore:matplotlib: mappa colori continuo riempire tra due linee

http://matplotlib.sourceforge.net/examples/pylab_examples/fill_between_demo.html

È anche possibile usare una colormap continuo per una linea:

http://matplotlib.sourceforge.net/examples/pylab_examples/multicolored_line.html

E 'possibile (e ragionevolmente facile) usare una mappa colori continua per il riempimento colorato tra due linee? Ad esempio, il riempimento colore può cambiare lungo x in base alla differenza tra le due linee in x (o in base a un altro insieme di dati).

risposta

6

Ho trovato una soluzione a questo problema. Si basa sul brillante ma hacky solution di @Hooked. Crei una griglia 2D archiviata da molte piccole scatole. Non è la soluzione più veloce, ma dovrebbe essere abbastanza flessibile (molto più delle soluzioni che applicano imshow alle patch).

import numpy as np 
import pylab as plt 

#Plot a rectangle 
def rect(ax, x, y, w, h, c,**kwargs): 
    #Varying only in x 
    if len(c.shape) is 1: 
     rect = plt.Rectangle((x, y), w, h, color=c, ec=c,**kwargs) 
     ax.add_patch(rect) 
    #Varying in x and y 
    else: 
     #Split into a number of bins 
     N = c.shape[0] 
     hb = h/float(N); yl = y 
     for i in range(N): 
      yl += hb 
      rect = plt.Rectangle((x, yl), w, hb, 
           color=c[i,:], ec=c[i,:],**kwargs) 
      ax.add_patch(rect) 

#Fill a contour between two lines 
def rainbow_fill_between(ax, X, Y1, Y2, colors=None, 
         cmap=plt.get_cmap("Reds"),**kwargs): 
    plt.plot(X,Y1,lw=0) # Plot so the axes scale correctly 

    dx = X[1]-X[0] 
    N = X.size 

    #Pad a float or int to same size as x 
    if (type(Y2) is float or type(Y2) is int): 
     Y2 = np.array([Y2]*N) 

    #No colors -- specify linear 
    if colors is None: 
     colors = [] 
     for n in range(N): 
      colors.append(cmap(n/float(N))) 
    #Varying only in x 
    elif len(colors.shape) is 1: 
     colors = cmap((colors-colors.min()) 
         /(colors.max()-colors.min())) 
    #Varying only in x and y 
    else: 
     cnp = np.array(colors) 
     colors = np.empty([colors.shape[0],colors.shape[1],4]) 
     for i in range(colors.shape[0]): 
      for j in range(colors.shape[1]): 
       colors[i,j,:] = cmap((cnp[i,j]-cnp[:,:].min()) 
            /(cnp[:,:].max()-cnp[:,:].min())) 

    colors = np.array(colors) 

    #Create the patch objects 
    for (color,x,y1,y2) in zip(colors,X,Y1,Y2): 
     rect(ax,x,y2,dx,y1-y2,color,**kwargs) 


# Some Test data  
X = np.linspace(0,10,100) 
Y1 = .25*X**2 - X 
Y2 = X 
g = np.exp(-.3*(X-5)**2) 

#Plot fill and curves changing in x only 
fig, axs =plt.subplots(1,2) 
colors = g 
rainbow_fill_between(axs[0],X,Y1,Y2,colors=colors) 
axs[0].plot(X,Y1,'k-',lw=4) 
axs[0].plot(X,Y2,'k-',lw=4) 

#Plot fill and curves changing in x and y 
colors = np.outer(g,g) 
rainbow_fill_between(axs[1],X,Y1,Y2,colors=colors) 
axs[1].plot(X,Y1,'k-',lw=4) 
axs[1].plot(X,Y2,'k-',lw=4) 
plt.show() 

Il risultato è, enter image description here

+0

Nizza esempio, forse si potrebbe aggiungere il 'rect() funzione' al fine di dare un esempio di auto consistente. Grazie – Ger

+0

Grazie a @Ger, non ho capito che ho dimenticato questo e 'rect' non è in realtà banale –

2

La soluzione è grande e flessibile! In particolare il caso 2D è davvero bello. Tale caratteristica potrebbe essere aggiunta a fill_between forse se i colori kwargs della funzione accetterebbero un array della stessa lunghezza di x e y?

Ecco un caso più semplice per il caso 1D utilizzando la funzione fill_between. Fa lo stesso, ma poiché usa i trapezi invece del rettangolo, il risultato è più fluido.

import matplotlib as mpl 
import matplotlib.pyplot as plt 

import numpy as np 
from scipy.stats import norm 

# Select a color map 
cmap = mpl.cm.bwr 

# Some Test data 
npts = 100 
x = np.linspace(-4, 4, npts) 
y = norm.pdf(x) 
z = np.sin(2 * x) 
normalize = mpl.colors.Normalize(vmin=z.min(), vmax=z.max()) 

# The plot 
fig = plt.figure() 
ax = fig.add_axes([0.12, 0.12, 0.68, 0.78]) 
plt.plot(x, y, color="gray") 
for i in range(npts - 1): 
    plt.fill_between([x[i], x[i+1]], [y[i], y[i+1]], color=cmap(normalize(z[i]))) 

cbax = fig.add_axes([0.85, 0.12, 0.05, 0.78]) 
cb = mpl.colorbar.ColorbarBase(cbax, cmap=cmap, norm=normalize, orientation='vertical') 
cb.set_label("Sin function", rotation=270, labelpad=15) 
plt.show() 

enter image description here

Problemi correlati