2011-12-14 33 views
24

Để trình bày nó dưới dạng chung, tôi đang tìm cách để nối một số điểm với đường màu gradient gradient bằng cách sử dụng matplotlib và tôi không tìm thấy nó ở bất kỳ đâu. Để cụ thể hơn, tôi đang vẽ một bước đi ngẫu nhiên 2D với một đường màu. Nhưng, khi các điểm có một chuỗi liên quan, tôi muốn xem xét cốt truyện và xem dữ liệu đã di chuyển ở đâu. Một dòng màu gradient sẽ làm các trick. Hoặc một dòng với dần dần thay đổi minh bạch.Làm thế nào để vẽ một đường màu gradient trong matplotlib?

Tôi chỉ đang cố gắng cải thiện việc hóa đơn dữ liệu của mình. Kiểm tra hình ảnh đẹp này được sản xuất bởi gói ggplot2 của R. Tôi đang tìm kiếm giống nhau trong matplotlib. Cảm ơn.

enter image description here

+1

Tôi không chắc chắn những gì bạn có ý nghĩa bởi một 'dòng màu gradient': làm bạn có nghĩa là (ví dụ) đi bộ bắt đầu với một đường màu xanh và dần dần thay đổi thành màu đỏ ở cuối? Bạn có thể cung cấp một ví dụ tối thiểu về mã hiện tại của bạn để vẽ đi bộ bằng một dòng màu không? –

+0

Tôi không biết bất kỳ cách nào để vẽ các gradient theo đường thẳng với matplotlib, mặc dù nó sẽ đẹp. Tôi có thể đề nghị bạn sử dụng pycairo thay vào đó, ở đó bạn có thể sử dụng gradient cho chắc chắn và nhận được rất nhiều kiểm soát hơn về cốt truyện. Mặc dù bạn sẽ mất một số tiện lợi từ matplotlib, như trục và phạm vi dữ liệu tự động :-( – dsign

+0

bạn có chắc chắn không ?:(bạn có biết kế hoạch nào kết hợp tính năng đó không? Tôi chưa bao giờ nghe nói về pycairo, bạn có thể cho tôi một số gợi ý không? ? @ math.coffee: vâng, đó là ý của tôi, mã có thể là một tập dữ liệu đơn giản (một số điểm và đó là tất cả) – PDRX

Trả lời

18

Gần đây tôi đã trả lời một câu hỏi với một yêu cầu tương tự (creating over 20 unique legend colors using matplotlib). Ở đó tôi đã chỉ ra rằng bạn có thể lập bản đồ chu kỳ màu sắc mà bạn cần để vẽ các đường thẳng của bạn tới một bản đồ màu. Bạn có thể sử dụng cùng một quy trình để có được một màu cụ thể cho mỗi cặp điểm.

Bạn nên chọn bản đồ màu một cách cẩn thận, bởi vì quá trình chuyển đổi màu dọc theo đường của bạn có thể xuất hiện quyết liệt nếu bản đồ màu sắc đầy màu sắc.

Ngoài ra, bạn có thể thay đổi alpha của mỗi đoạn thẳng, dao động từ 0 đến 1.

Trong ví dụ mã dưới đây là một thói quen (highResPoints) để mở rộng số điểm bước đi ngẫu nhiên của bạn có, bởi vì nếu bạn có quá ít điểm, quá trình chuyển đổi có thể có vẻ quyết liệt. chút mã này được lấy cảm hứng từ một câu trả lời gần đây tôi cung cấp: https://stackoverflow.com/a/8253729/717357

import numpy as np 
import matplotlib.pyplot as plt 

def highResPoints(x,y,factor=10): 
    ''' 
    Take points listed in two vectors and return them at a higher 
    resultion. Create at least factor*len(x) new points that include the 
    original points and those spaced in between. 

    Returns new x and y arrays as a tuple (x,y). 
    ''' 

    # r is the distance spanned between pairs of points 
    r = [0] 
    for i in range(1,len(x)): 
     dx = x[i]-x[i-1] 
     dy = y[i]-y[i-1] 
     r.append(np.sqrt(dx*dx+dy*dy)) 
    r = np.array(r) 

    # rtot is a cumulative sum of r, it's used to save time 
    rtot = [] 
    for i in range(len(r)): 
     rtot.append(r[0:i].sum()) 
    rtot.append(r.sum()) 

    dr = rtot[-1]/(NPOINTS*RESFACT-1) 
    xmod=[x[0]] 
    ymod=[y[0]] 
    rPos = 0 # current point on walk along data 
    rcount = 1 
    while rPos < r.sum(): 
     x1,x2 = x[rcount-1],x[rcount] 
     y1,y2 = y[rcount-1],y[rcount] 
     dpos = rPos-rtot[rcount] 
     theta = np.arctan2((x2-x1),(y2-y1)) 
     rx = np.sin(theta)*dpos+x1 
     ry = np.cos(theta)*dpos+y1 
     xmod.append(rx) 
     ymod.append(ry) 
     rPos+=dr 
     while rPos > rtot[rcount+1]: 
      rPos = rtot[rcount+1] 
      rcount+=1 
      if rcount>rtot[-1]: 
       break 

    return xmod,ymod 


#CONSTANTS 
NPOINTS = 10 
COLOR='blue' 
RESFACT=10 
MAP='winter' # choose carefully, or color transitions will not appear smoooth 

# create random data 
np.random.seed(101) 
x = np.random.rand(NPOINTS) 
y = np.random.rand(NPOINTS) 

fig = plt.figure() 
ax1 = fig.add_subplot(221) # regular resolution color map 
ax2 = fig.add_subplot(222) # regular resolution alpha 
ax3 = fig.add_subplot(223) # high resolution color map 
ax4 = fig.add_subplot(224) # high resolution alpha 

# Choose a color map, loop through the colors, and assign them to the color 
# cycle. You need NPOINTS-1 colors, because you'll plot that many lines 
# between pairs. In other words, your line is not cyclic, so there's 
# no line from end to beginning 
cm = plt.get_cmap(MAP) 
ax1.set_color_cycle([cm(1.*i/(NPOINTS-1)) for i in range(NPOINTS-1)]) 
for i in range(NPOINTS-1): 
    ax1.plot(x[i:i+2],y[i:i+2]) 


ax1.text(.05,1.05,'Reg. Res - Color Map') 
ax1.set_ylim(0,1.2) 

# same approach, but fixed color and 
# alpha is scale from 0 to 1 in NPOINTS steps 
for i in range(NPOINTS-1): 
    ax2.plot(x[i:i+2],y[i:i+2],alpha=float(i)/(NPOINTS-1),color=COLOR) 

ax2.text(.05,1.05,'Reg. Res - alpha') 
ax2.set_ylim(0,1.2) 

# get higher resolution data 
xHiRes,yHiRes = highResPoints(x,y,RESFACT) 
npointsHiRes = len(xHiRes) 

cm = plt.get_cmap(MAP) 

ax3.set_color_cycle([cm(1.*i/(npointsHiRes-1)) 
        for i in range(npointsHiRes-1)]) 


for i in range(npointsHiRes-1): 
    ax3.plot(xHiRes[i:i+2],yHiRes[i:i+2]) 

ax3.text(.05,1.05,'Hi Res - Color Map') 
ax3.set_ylim(0,1.2) 

for i in range(npointsHiRes-1): 
    ax4.plot(xHiRes[i:i+2],yHiRes[i:i+2], 
      alpha=float(i)/(npointsHiRes-1), 
      color=COLOR) 
ax4.text(.05,1.05,'High Res - alpha') 
ax4.set_ylim(0,1.2) 



fig.savefig('gradColorLine.png') 
plt.show() 

Con số này cho thấy bốn trường hợp:

enter image description here

+0

! Tôi sẽ thêm điều này vào bộ sưu tập các đoạn mã của tôi. –

+0

@Yann: Đó là một tính năng tuyệt vời Yann! Tôi không biết về bản đồ màu. Nhưng nếu bạn loại bỏ các dòng "ax.plot (x, y)" nó được tốt hơn (ít tiếng ồn). Tuy nhiên, tôi có thể thấy 2 màu chồng chéo trong mỗi phân đoạn. Bạn có thể sửa nó không? Loại hình ảnh này có thể giúp ích một chút nếu tôi sử dụng bản đồ màu 2 chẳng hạn. Tuy nhiên, những gì tôi đang tìm kiếm là một sự chuyển đổi màu trơn tru ngay cả giữa các điểm. Thậm chí tốt hơn (nhưng có thể hỏi quá nhiều) sẽ chỉ là một màu nhưng với một yếu tố alpha cho tính minh bạch cũng thay đổi. Tôi vừa chỉnh sửa câu hỏi của mình. Kiểm tra hình ảnh. – PDRX

+1

@Yann Cảm ơn bạn! Câu trả lời của bạn là một trợ giúp lớn. Người ta có thể thấy bạn cảm thấy thoải mái với matplotlib. Cách tiếp cận "highres" là thông minh. Khi tôi nhìn thấy câu trả lời đầu tiên của bạn, tôi ngay lập tức mặc dù tạo ra các điểm bổ sung để làm cho việc chấm điểm tiến bộ là tốt, nhưng bạn đã làm nó trong một giây! Tôi đã nhảy có một số chức năng buit-in trong matplotlib có thể xử lý vấn đề và tiết kiệm cho tôi từ writting mã và có nó luôn luôn xung quanh! Đoán tôi sẽ phải sống với nó. – PDRX

13

Lưu ý rằng nếu bạn có nhiều điểm, gọi plt.plot cho mỗi dòng phân khúc có thể khá chậm. Sử dụng đối tượng LineCollection hiệu quả hơn.

Sử dụng colorline recipe bạn có thể làm như sau:

import matplotlib.pyplot as plt 
import numpy as np 
import matplotlib.collections as mcoll 
import matplotlib.path as mpath 

def colorline(
    x, y, z=None, cmap=plt.get_cmap('copper'), norm=plt.Normalize(0.0, 1.0), 
     linewidth=3, alpha=1.0): 
    """ 
    http://nbviewer.ipython.org/github/dpsanders/matplotlib-examples/blob/master/colorline.ipynb 
    http://matplotlib.org/examples/pylab_examples/multicolored_line.html 
    Plot a colored line with coordinates x and y 
    Optionally specify colors in the array z 
    Optionally specify a colormap, a norm function and a line width 
    """ 

    # Default colors equally spaced on [0,1]: 
    if z is None: 
     z = np.linspace(0.0, 1.0, len(x)) 

    # Special case if a single number: 
    if not hasattr(z, "__iter__"): # to check for numerical input -- this is a hack 
     z = np.array([z]) 

    z = np.asarray(z) 

    segments = make_segments(x, y) 
    lc = mcoll.LineCollection(segments, array=z, cmap=cmap, norm=norm, 
           linewidth=linewidth, alpha=alpha) 

    ax = plt.gca() 
    ax.add_collection(lc) 

    return lc 


def make_segments(x, y): 
    """ 
    Create list of line segments from x and y coordinates, in the correct format 
    for LineCollection: an array of the form numlines x (points per line) x 2 (x 
    and y) array 
    """ 

    points = np.array([x, y]).T.reshape(-1, 1, 2) 
    segments = np.concatenate([points[:-1], points[1:]], axis=1) 
    return segments 

N = 10 
np.random.seed(101) 
x = np.random.rand(N) 
y = np.random.rand(N) 
fig, ax = plt.subplots() 

path = mpath.Path(np.column_stack([x, y])) 
verts = path.interpolated(steps=3).vertices 
x, y = verts[:, 0], verts[:, 1] 
z = np.linspace(0, 1, len(x)) 
colorline(x, y, z, cmap=plt.get_cmap('jet'), linewidth=2) 

plt.show() 

enter image description here

3

Quá dài cho một chú thích, vì vậy chỉ muốn xác nhận rằng LineCollection là rất nhanh hơn một cho vòng trên đường subsegments.

Phương pháp LineCollection nhanh hơn rất nhiều trong tay tôi.

# Setup 
x = np.linspace(0,4*np.pi,1000) 
y = np.sin(x) 
MAP = 'cubehelix' 
NPOINTS = len(x) 

Chúng tôi sẽ kiểm tra âm mưu lặp lại theo phương pháp LineCollection ở trên.

%%timeit -n1 -r1 
# Using IPython notebook timing magics 
fig = plt.figure() 
ax1 = fig.add_subplot(111) # regular resolution color map 
cm = plt.get_cmap(MAP) 
for i in range(10): 
    ax1.set_color_cycle([cm(1.*i/(NPOINTS-1)) for i in range(NPOINTS-1)]) 
    for i in range(NPOINTS-1): 
     plt.plot(x[i:i+2],y[i:i+2]) 

1 loops, best of 1: 13.4 s per loop

%%timeit -n1 -r1 
fig = plt.figure() 
ax1 = fig.add_subplot(111) # regular resolution color map 
for i in range(10): 
    colorline(x,y,cmap='cubehelix', linewidth=1) 

1 loops, best of 1: 532 ms per loop

upsampling dòng của bạn cho một gradient màu tốt hơn, là câu trả lời hiện đang được chọn cung cấp, vẫn còn là một ý tưởng tốt nếu bạn muốn một gradient mịn và bạn chỉ có một vài điểm.

1

Tôi đã thêm giải pháp của mình bằng cách sử dụng pcolormesh Mỗi đoạn đường được vẽ bằng hình chữ nhật có nội suy giữa các màu ở mỗi đầu. Vì vậy, nó thực sự là nội suy màu sắc, nhưng chúng ta phải vượt qua một độ dày của dòng.

import numpy as np 
import matplotlib.pyplot as plt 

def colored_line(x, y, z=None, linewidth=1, MAP='jet'): 
    # this uses pcolormesh to make interpolated rectangles 
    xl = len(x) 
    [xs, ys, zs] = [np.zeros((xl,2)), np.zeros((xl,2)), np.zeros((xl,2))] 

    # z is the line length drawn or a list of vals to be plotted 
    if z == None: 
     z = [0] 

    for i in range(xl-1): 
     # make a vector to thicken our line points 
     dx = x[i+1]-x[i] 
     dy = y[i+1]-y[i] 
     perp = np.array([-dy, dx]) 
     unit_perp = (perp/np.linalg.norm(perp))*linewidth 

     # need to make 4 points for quadrilateral 
     xs[i] = [x[i], x[i] + unit_perp[0] ] 
     ys[i] = [y[i], y[i] + unit_perp[1] ] 
     xs[i+1] = [x[i+1], x[i+1] + unit_perp[0] ] 
     ys[i+1] = [y[i+1], y[i+1] + unit_perp[1] ] 

     if len(z) == i+1: 
      z.append(z[-1] + (dx**2+dy**2)**0.5)  
     # set z values 
     zs[i] = [z[i], z[i] ] 
     zs[i+1] = [z[i+1], z[i+1] ] 

    fig, ax = plt.subplots() 
    cm = plt.get_cmap(MAP) 
    ax.pcolormesh(xs, ys, zs, shading='gouraud', cmap=cm) 
    plt.axis('scaled') 
    plt.show() 

# create random data 
N = 10 
np.random.seed(101) 
x = np.random.rand(N) 
y = np.random.rand(N) 
colored_line(x, y, linewidth = .01) 

enter image description here

Các vấn đề liên quan