Matplotlib plot with variable line width

前端 未结 4 1144
隐瞒了意图╮
隐瞒了意图╮ 2020-11-27 14:00

Is it possible to plot a line with variable line width in matplotlib? For example:

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


        
相关标签:
4条回答
  • 2020-11-27 14:18

    gg349's answer works nicely but cuts the line into many pieces, which can often creates bad rendering.

    Here is an alternative example that generates continuous lines when the width is homogeneous:

    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 讨论(0)
  • 2020-11-27 14:24

    You can plot each segment of the line separately, with its separate line width, something like:

    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 讨论(0)
  • 2020-11-27 14:33

    An alternative to Giulio Ghirardo's answer which divides the lines in segments you can use matplotlib's in-built scatter function which construct the line by using circles instead:

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

    In my experience I have found two problems with dividing the line into segments:

    1. For some reason the segments are always divided by very thin white lines. The colors of these lines get blended with the colors of the segments when using a very large amount of segments. Because of this the color of the line is not the same as the intended one.

    2. It doesn't handle very well very sharp discontinuities.

    0 讨论(0)
  • 2020-11-27 14:40

    Use LineCollections. A way to do it along the lines of this Matplotlib example is

    import numpy as np
    from matplotlib.collections import LineCollection
    import matplotlib.pyplot as plt
    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*np.pi)
    a.set_ylim(-1.1,1.1)
    fig.show()
    

    output

    0 讨论(0)
提交回复
热议问题