2013-10-15 69 views
24

是否可以在matplotlib中繪製一條線寬可變的線?例如:可變線寬的Matplotlib繪圖

from pylab import * 
x = [1, 2, 3, 4, 5] 
y = [1, 2, 2, 0, 0] 
width = [.5, 1, 1.5, .75, .75] 

plot(x, y, linewidth=width) 

這不起作用,因爲線寬預計標。

注意:我知道* fill_between()*和* fill_betweenx()*。因爲這些只能填寫x或y方向,所以這些對於你有斜線的情況並不公平。填料始終與生產線垂直是理想的。這就是尋求可變寬度線的原因。

回答

54

使用LineCollections。要做到這一點沿着this Matplotlib例子的線的方法是

import numpy as np 
from matplotlib.collections import LineCollection 
x=np.linspace(0,4*np.pi,10000) 
y=np.cos(x) 
lwidths=1+x[:-1] 
points = np.array([x, y]).T.reshape(-1, 1, 2) 
segments = np.concatenate([points[:-1], points[1:]], axis=1) 
lc = LineCollection(segments, linewidths=lwidths,color='blue') 
fig,a = plt.subplots() 
a.add_collection(lc) 
a.set_xlim(0,4*pi) 
a.set_ylim(-1.1,1.1) 
fig.show() 

output

+1

不錯!因此,您將該線切割爲一系列部分,並使用「LineCollection」指定每個部分的屬性。 – Hamid

+5

根本不是我想找的東西,但是這很酷,所以我投了贊成票:) –

+0

學習曲線陡峭的matplotlib - 試圖找出如何適應這個圖形,其中'x'軸包含'時間戳「,顯然段預計'浮動',而不是'時間戳'......任何線索?這是否正是我正在尋找的,除了無法真正產生一個圖... – dwanderson

0

可以單獨繪製線的每個部分,其獨立的線寬,是這樣的:

from pylab import * 
x = [1, 2, 3, 4, 5] 
y = [1, 2, 2, 0, 0] 
width = [.5, 1, 1.5, .75, .75] 

for i in range(len(x)-1): 
    plot(x[i:i+2], y[i:i+2], linewidth=width[i]) 
show() 
+0

雖然此方法有效,它有兩個問題: 1)對於大數據集(例如10000點),這產生了大約相同數量的線對象,這是渲染的負擔。 2)連接看起來不好,因爲它們由重疊的矩形角組成。 – Hamid

3

朱利奧Ghirardo的答案的替代,其將在部分線路可以使用matplotlib的內置分散功能,構造線用圓圈代替:

from matplotlib import pyplot as plt 
import numpy as np 

x = np.linspace(0,10,10000) 
y = 2 - 0.5*np.abs(x-4) 
lwidths = (1+x)**2 # scatter 'o' marker size is specified by area not radius 
plt.scatter(x,y, s=lwidths, color='blue') 
plt.xlim(0,9) 
plt.ylim(0,2.1) 
plt.show() 

根據我的經驗,我發現兩個問題分割線成段:

  1. 由於某些原因,細分總是被非常細的白線分開。當使用大量的線段時,這些線的顏色會與線段的顏色混合。正因爲如此,線條的顏色與預期的不一樣。

  2. 它不能很好地處理非常尖銳的不連續性。

0

gg349的答案效果很好,但將線切割成許多片段,這往往會造成不良的渲染。

這裏是生成連續的線時的寬度是均勻的可替代的示例:

import numpy as np 
import matplotlib.pyplot as plt 

fig, ax = plt.subplots(1) 
xs = np.cos(np.linspace(0, 8 * np.pi, 200)) * np.linspace(0, 1, 200) 
ys = np.sin(np.linspace(0, 8 * np.pi, 200)) * np.linspace(0, 1, 200) 
widths = np.round(np.linspace(1, 5, len(xs))) 

def plot_widths(xs, ys, widths, ax=None, color='b', xlim=None, ylim=None, 
       **kwargs): 
    if not (len(xs) == len(ys) == len(widths)): 
     raise ValueError('xs, ys, and widths must have identical lengths') 
    fig = None 
    if ax is None: 
     fig, ax = plt.subplots(1) 

    segmentx, segmenty = [xs[0]], [ys[0]] 
    current_width = widths[0] 
    for ii, (x, y, width) in enumerate(zip(xs, ys, widths)): 
     segmentx.append(x) 
     segmenty.append(y) 
     if (width != current_width) or (ii == (len(xs) - 1)): 
      ax.plot(segmentx, segmenty, linewidth=current_width, color=color, 
        **kwargs) 
      segmentx, segmenty = [x], [y] 
      current_width = width 
    if xlim is None: 
     xlim = [min(xs), max(xs)] 
    if ylim is None: 
     ylim = [min(ys), max(ys)] 
    ax.set_xlim(xlim) 
    ax.set_ylim(ylim) 

    return ax if fig is None else fig 

plot_widths(xs, ys, widths) 
plt.show() 
+0

這個實現可能在一些好但是,如果您嘗試使用gg349給出的正弦曲線示例,它會遭受損失。兩者看起來不太好,而且速度很慢,因爲它爲每個分段添加了一個新的線對象,因爲寬度不斷變化。 – Hamid