我的问题有点类似于这个问题,它绘制了数据坐标中给出的宽度线.让我的问题更具挑战性的是,与链接问题不同,我希望扩展的片段是随机的.
假设线段从哪里(0, 10)开始(10, 10),我希望将其扩展为宽度6.那就简单了
x = [0, 10]
y = [10, 10]
ax.fill_between(x, y - 3, y + 3)
Run Code Online (Sandbox Code Playgroud)
但是,我的线段是随机的.也就是说,它不一定沿x轴或y轴.它有一定的坡度.
线段s定义为其起点和终点的列表:[(x1, y1), (x2, y2)].
现在我希望将线段扩展到一定的宽度w.该解决方案有望用于任何方向的线段.这该怎么做?
更新: plt.plot(x, y, linewidth=6.0)无法做到这一点,因为我希望我的宽度与我的数据在同一个单位.
小智 9
只是添加到上一个答案(还不能评论),这是一个自动执行此过程的功能,无需等轴或标签的启发值为0.8.轴的数据限制和大小需要固定,并且在调用此函数后不会更改.
def linewidth_from_data_units(linewidth, axis, reference='y'):
"""
Convert a linewidth in data units to linewidth in points.
Parameters
----------
linewidth: float
Linewidth in data units of the respective reference-axis
axis: matplotlib axis
The axis which is used to extract the relevant transformation
data (data limits and size must not change afterwards)
reference: string
The axis that is taken as a reference for the data width.
Possible values: 'x' and 'y'. Defaults to 'y'.
Returns
-------
linewidth: float
Linewidth in points
"""
fig = axis.get_figure()
if reference == 'x':
length = fig.bbox_inches.width * axis.get_position().width
value_range = np.diff(axis.get_xlim())
elif reference == 'y':
length = fig.bbox_inches.height * axis.get_position().height
value_range = np.diff(axis.get_ylim())
# Convert length to points
length *= 72
# Scale linewidth to value range
return linewidth * (length / value_range)
Run Code Online (Sandbox Code Playgroud)
说明:
设置具有已知高度的图形并使两个轴的比例相等(否则"数据坐标"的概念不适用).确保图形的比例与x和y轴的预期比例相匹配.
计算整个图的高度point_hei在的单元(包括边缘)的点,通过72英寸乘以
手动指定y轴范围yrange(您可以先绘制"虚拟"系列,然后查询绘图轴以获得y和y的上限.)
以数据单位提供您想要的行宽 linewid
在调整边距时计算这些单位的分数 pointlinewid.在单帧图中,图是整个图像高度的80%.
绘制线条,使用不填充线条末端的capstyle(在这些大线条尺寸上有很大影响)
瞧?(注意:这应该在保存的文件中生成正确的图像,但如果您调整绘图窗口的大小,则无法保证.)
import matplotlib.pyplot as plt
rez=600
wid=8.0 # Must be proportional to x and y limits below
hei=6.0
fig = plt.figure(1, figsize=(wid, hei))
sp = fig.add_subplot(111)
# # plt.figure.tight_layout()
# fig.set_autoscaley_on(False)
sp.set_xlim([0,4000])
sp.set_ylim([0,3000])
plt.axes().set_aspect('equal')
# line is in points: 72 points per inch
point_hei=hei*72
xval=[100,1300,2200,3000,3900]
yval=[10,200,2500,1750,1750]
x1,x2,y1,y2 = plt.axis()
yrange = y2 - y1
# print yrange
linewid = 500 # in data units
# For the calculation below, you have to adjust width by 0.8
# because the top and bottom 10% of the figure are labels & axis
pointlinewid = (linewid * (point_hei/yrange)) * 0.8 # corresponding width in pts
plt.plot(xval,yval,linewidth = pointlinewid,color="blue",solid_capstyle="butt")
# just for fun, plot the half-width line on top of it
plt.plot(xval,yval,linewidth = pointlinewid/2,color="red",solid_capstyle="butt")
plt.savefig('mymatplot2.png',dpi=rez)
Run Code Online (Sandbox Code Playgroud)

以下代码是有关如何使用数据坐标作为线宽在matplotlib中制作线图的通用示例。有两种解决方案;一种使用回调,一种使用子类Line2D。
它被暗示为data_linewidth_plot可以用与正常plt.plot命令非常接近的签名来调用的类,
l = data_linewidth_plot(x, y, ax=ax, label='some line', linewidth=1, alpha=0.4)
Run Code Online (Sandbox Code Playgroud)
ax要绘制到的轴在哪里。ax当图中仅存在一个子图时,可以忽略该自变量。该linewidth参数被解释在(y轴)的数据单元。
其他功能:
这是完整的代码。
import matplotlib.pyplot as plt
class data_linewidth_plot():
def __init__(self, x, y, **kwargs):
self.ax = kwargs.pop("ax", plt.gca())
self.fig = self.ax.get_figure()
self.lw_data = kwargs.pop("linewidth", 1)
self.lw = 1
self.fig.canvas.draw()
self.ppd = 72./self.fig.dpi
self.trans = self.ax.transData.transform
self.linehandle, = self.ax.plot([],[],**kwargs)
if "label" in kwargs: kwargs.pop("label")
self.line, = self.ax.plot(x, y, **kwargs)
self.line.set_color(self.linehandle.get_color())
self._resize()
self.cid = self.fig.canvas.mpl_connect('draw_event', self._resize)
def _resize(self, event=None):
lw = ((self.trans((1, self.lw_data))-self.trans((0, 0)))*self.ppd)[1]
if lw != self.lw:
self.line.set_linewidth(lw)
self.lw = lw
self._redraw_later()
def _redraw_later(self):
self.timer = self.fig.canvas.new_timer(interval=10)
self.timer.single_shot = True
self.timer.add_callback(lambda : self.fig.canvas.draw_idle())
self.timer.start()
fig1, ax1 = plt.subplots()
#ax.set_aspect('equal') #<-not necessary
ax1.set_ylim(0,3)
x = [0,1,2,3]
y = [1,1,2,2]
# plot a line, with 'linewidth' in (y-)data coordinates.
l = data_linewidth_plot(x, y, ax=ax1, label='some 1 data unit wide line',
linewidth=1, alpha=0.4)
plt.legend() # <- legend possible
plt.show()
Run Code Online (Sandbox Code Playgroud)
(由于这个问题,我更新了代码以使用计时器重绘画布)
上述解决方案具有一些缺点。它需要一个计时器和回调来根据不断变化的轴限制或图形尺寸进行更新。以下是没有这种需求的解决方案。它将使用动态属性始终动态地根据数据坐标中的所需线宽来计算以点为单位的线宽。它比上面的短得多。这里的缺点是需要通过代理人手动创建图例。
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
class LineDataUnits(Line2D):
def __init__(self, *args, **kwargs):
_lw_data = kwargs.pop("linewidth", 1)
super().__init__(*args, **kwargs)
self._lw_data = _lw_data
def _get_lw(self):
if self.axes is not None:
ppd = 72./self.axes.figure.dpi
trans = self.axes.transData.transform
return ((trans((1, self._lw_data))-trans((0, 0)))*ppd)[1]
else:
return 1
def _set_lw(self, lw):
self._lw_data = lw
_linewidth = property(_get_lw, _set_lw)
fig, ax = plt.subplots()
#ax.set_aspect('equal') # <-not necessary, if not given, y data is assumed
ax.set_xlim(0,3)
ax.set_ylim(0,3)
x = [0,1,2,3]
y = [1,1,2,2]
line = LineDataUnits(x, y, linewidth=1, alpha=0.4)
ax.add_line(line)
ax.legend([Line2D([],[], linewidth=3, alpha=0.4)],
['some 1 data unit wide line']) # <- legend possible via proxy artist
plt.show()
Run Code Online (Sandbox Code Playgroud)