Python 具有可变线宽的 Matplotlib 图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19390895/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 13:38:58  来源:igfitidea点击:

Matplotlib plot with variable line width

pythonmatplotlibplot

提问by Hamid

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

是否可以在 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)

This doesn't work because linewidthexpects a scalar.

这不起作用,因为linewidth需要标量。

Note:I'm aware of *fill_between()* and *fill_betweenx()*. Because these only fill in x or y direction, these do not do justice to cases where you have a slanted line. It is desirable for the fill to always be normal to the line. That is why a variable width line is sought.

注意:我知道 *fill_between()* 和 *fill_betweenx()*。因为这些只填充 x 或 y 方向,所以这些对你有一条斜线的情况不公平。填充始终与线垂直是可取的。这就是寻求可变宽度线的原因。

采纳答案by gg349

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

使用 LineCollections。按照这个Matplotlib 示例的方法来做的一种方法是

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

输出

回答by piokuc

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

回答by J.Bet

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:

Giulio Ghirardo 的答案的另一种方法是将线分成几段,您可以使用 matplotlib 的内置 scatter 函数,该函数通过使用圆圈来构造线:

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.

  1. 出于某种原因,这些段总是被非常细的白线分开。当使用大量线段时,这些线的颜色会与线段的颜色混合。因此,线条的颜色与预期的颜色不同。

  2. 它不能很好地处理非常尖锐的不连续性。

回答by kingjr

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

gg349 的答案效果很好,但将线切割成许多部分,这通常会造成糟糕的渲染。

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