График 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, это не оправдывает случаев, когда у вас есть наклонная линия. Желательно, чтобы заливка всегда была нормальной к линии. Вот почему запрашивается переменная ширина.
Ответы
Ответ 1
Используйте 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]()
Ответ 2
Альтернатива Giulio 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()
По моему опыту я обнаружил две проблемы с делением линии на сегменты:
-
По какой-то причине сегменты всегда делятся на очень тонкие белые линии. Цвета этих линий смешиваются с цветами сегментов при использовании очень большого количества сегментов. Из-за этого цвет линии не совпадает с цветом.
-
Он не очень хорошо справляется с очень резкими разрывами.
Ответ 3
Вы можете построить каждый сегмент линии отдельно, с отдельной шириной линии, что-то вроде:
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()
Ответ 4
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()