2012-07-19 14 views
9

jest możliwe wypełnienie między wierszami o kolorze:matplotlib: ciągły colormap wypełnienia pomiędzy dwiema liniami

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

Możliwe jest również użycie ciągłego colormap dla linii:

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

Czy jest możliwe (i rozsądnie łatwe) używanie ciągłej mapy kolorów dla kolorowych wypełnień między dwiema liniami? Na przykład wypełnienie kolorem może zmieniać się wzdłuż x w oparciu o różnicę między dwiema liniami w punkcie x (lub w oparciu o inny zestaw danych).

Odpowiedz

6

Znalazłem rozwiązanie tego problemu. Opiera się na świetnym, ale hacky solution @Hooked. Tworzysz siatkę 2D złożoną z wielu małych pudełek. Nie jest to najszybsze rozwiązanie, ale powinno być dość elastyczne (bardziej niż rozwiązania, które stosują się do tych poprawek).

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() 

Rezultatem jest enter image description here

+0

Nicea przykład, być może można dodać 'rect() Funkcja' w celu zapewnienia samodzielnego consistant przykład. Dzięki – Ger

+0

Dzięki @Ger, nie zdawałem sobie sprawy, że o tym zapomniałem, a 'rect' nie jest tak naprawdę trywialny –

2

Twoje rozwiązanie jest wielki i elastyczny! W szczególności przypadek 2D jest naprawdę miły. Taka funkcja mogłaby zostać dodana do fill_between, może jeśli kolory kwargs funkcji przyjmą tablicę o tej samej długości x i y?

Oto prostszy przypadek dla przypadku 1D z funkcją fill_between. Robi to samo, ale ponieważ używa trapezów zamiast prostokąta, wynik jest płynniejszy.

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

Powiązane problemy