2013-10-15 29 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