web-dev-qa-db-fra.com

Matplotlib plot avec une largeur de ligne variable

Est-il possible de tracer une ligne avec une largeur de ligne variable dans matplotlib? Par exemple:

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)

Cela ne fonctionne pas car linewidth attend un scalaire.

Remarque: Je connais * fill_between () * et * fill_betweenx () *. Comme ils ne remplissent que les directions x ou y, ils ne rendent pas justice aux cas où vous avez une ligne oblique. Il est souhaitable que le remplissage soit toujours normal à la ligne. C'est pourquoi une ligne de largeur variable est recherchée. 

28
Hamid

Utilisez LineCollections. Une manière de le faire dans le sens de this exemple de Matplotlib est

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

65
gg349

Une alternative à la réponse de Giulio Ghirardo qui divise les lignes en segments, vous pouvez utiliser la fonction de dispersion intégrée de matplotlib qui construit la ligne en utilisant des cercles:

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

D'après mon expérience, j'ai rencontré deux problèmes pour diviser la ligne en segments:

  1. Pour une raison quelconque, les segments sont toujours divisés par des lignes blanches très fines. Les couleurs de ces lignes sont mélangées avec les couleurs des segments lorsque vous utilisez une très grande quantité de segments. De ce fait, la couleur de la ligne n’est pas la même que celle souhaitée.

  2. Il ne gère pas très bien les discontinuités très nettes. 

3
J.Bet

Vous pouvez tracer chaque segment de la ligne séparément, avec sa largeur de ligne distincte, quelque chose comme:

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
piokuc

la réponse de gg349 fonctionne bien mais coupe la ligne en plusieurs morceaux, ce qui peut souvent créer un mauvais rendu.

Voici un exemple alternatif qui génère des lignes continues lorsque la largeur est homogène:

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
kingjr