wake-up-neo.com

Matplotlib-Plot mit variabler Linienbreite

Kann eine Linie mit variabler Linienbreite in Matplotlib gezeichnet werden? Zum Beispiel:

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)

Dies funktioniert nicht, weil linewidth einen Skalar erwartet.

Hinweis: Ich kenne * fill_between () * und * fill_betweenx () *. Da diese nur in x- oder y-Richtung ausgefüllt werden, werden sie nicht den Fällen gerecht, in denen Sie eine schräge Linie haben. Es ist wünschenswert, dass die Füllung immer normal zur Linie ist. Deshalb wird nach einer Linie mit variabler Breite gesucht. 

28
Hamid

LineCollections verwenden. Eine Möglichkeit, dies zu tun, ist das Beispiel this 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

65
gg349

Eine Alternative zu Giulio Ghirardos Antwort, bei der die Linien in Segmente unterteilt werden, können Sie die eingebaute Streufunktion von matplotlib verwenden, die die Linie stattdessen mit Kreisen erstellt:

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

Nach meiner Erfahrung habe ich zwei Probleme bei der Unterteilung der Linie in Segmente gefunden:

  1. Aus irgendeinem Grund sind die Segmente immer durch sehr dünne weiße Linien unterteilt. Die Farben dieser Linien werden mit den Farben der Segmente gemischt, wenn sehr viele Segmente verwendet werden. Aus diesem Grund stimmt die Farbe der Linie nicht mit der beabsichtigten überein.

  2. Es behandelt nicht sehr gut sehr scharfe Diskontinuitäten. 

3
J.Bet

Sie können jedes Segment der Linie mit der separaten Linienstärke separat zeichnen, etwa:

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

die Antwort von gg349 funktioniert gut, teilt die Linie jedoch in viele Teile auf, was oft zu schlechtem Rendering führt.

Hier ist ein alternatives Beispiel, das bei homogener Breite durchgehende Linien erzeugt:

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