matplotlib 在定义的像素位置绘制均匀间隔的垂直线

lsmepo6l  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(103)

问题描述

我试图绘制一系列(长度n)的元组(低,高)作为垂直线(长度=高-低)的绘图区域,必须是3 n像素宽。每条垂直线必须只有1个像素的宽度,并且线之间的间距必须正好是2个像素。然而,垂直线不是等间距的。
到目前为止,我能做的是:

  • 根据我的系统的dpi(documentation),使用所需的像素宽度和高度绘图
  • 以相同的像素尺寸保存图形
  • 在给定的x位置上绘制宽度为1 px的垂直线

问题出现在最后一点。线之间的间距不总是2像素,可能是因为定义的绘图大小定义了整个图形的大小,而不仅仅是主绘图区域的大小。
我已经试过以下文章:

示例

给定7个元组(n=7)的序列,每个元组具有(低,高)的形式,我想绘制21(3 n)像素宽的图。因此,该图应包含7条垂直线,每条垂直线在y轴上从低值延伸到高值。在最终图中应排除轴。线的x位置应该在像素2、5、8、11、14、17和20处。
问题是,matplotlib似乎没有保留定义的间距,可能是由于图周围隐式添加的边界(见下图)。
最后的代码生成了这个图(缩放的png):

图中显示了需要解决的问题:

  • 钢筋间距不一致(即行2和3之间的像素间隔应该是2而不是1)。
  • 主绘图区域必须为3 n像素宽,而不是整个图形。

示例代码:

from pathlib import Path
import matplotlib.pyplot as plt
from ctypes import windll
import numpy as np

plot_path = Path("./plots/testing/")
plot_path.mkdir(parents=True, exist_ok=True)
SYS_DPI = windll.user32.GetDpiForSystem()
print(f"Recognized DPI: {SYS_DPI}")

# create example dataset
example_series = [(1.3, 4.2), (5.3, 8.9), (4.0, 5.5),
                  (6, 7.8), (3.2, 4.5), (5.1, 8.1), (3, 8)]
width_pixel = len(example_series)*3
height_pixel = 10

# define xlims
y_min_vals = [x[0] for x in example_series]
y_vals_vals = [x[1] for x in example_series]
min_y, max_y = min(y_min_vals), max(y_vals_vals)

# define x positions and xlims
x_pos = np.arange(0, width_pixel, step=3)+2  # 2,5,8,11,14,17,20
min_x, max_x = 0, width_pixel

# create plot of needed size
fix, ax = plt.subplots(
    figsize=(width_pixel/SYS_DPI, height_pixel/SYS_DPI), dpi=SYS_DPI)
ax.set_facecolor("white")
ax.set_axis_off()
ax.set_xlim((min_x, max_x))
ax.set_ylim(min_y, max_y)
for idx, val in enumerate(example_series):
    print(f"{idx}: values: {val}")
    ax.plot([x_pos[idx], x_pos[idx]], [val[0], val[1]],
            color="black", linewidth=1)

plt.savefig(plot_path/"test.png",
            bbox_inches=None,
            pad_inches=0,
            transparent=False,
            dpi=SYS_DPI,
            facecolor="white")
fzsnzjdm

fzsnzjdm1#

我通过改变轴的长宽比来获得你想要的图片,并将相对轴的位置设置为(0,0,1,1)。
放大截图我得到的(分辨率21*10像素)

example_series = [(1.3, 4.2), (5.3, 8.9), (4.0, 5.5),
                  (6, 7.8), (3.2, 4.5), (5.1, 8.1), (3, 8)]
width_pixel = len(example_series)*3
height_pixel = 10
dpi = 72 # random dpi because i don't have windows. Work with other values too.

bottom=np.array([x[0] for x in example_series])
top = np.array([x[1] for x in example_series])
height = top - bottom

min_y, max_y = min(bottom), max(top)
min_x, max_x = 0, width_pixel

x_pos = np.array([2,5,8,11,14,17,20])-1

fig_width = width_pixel/dpi
fig_height = height_pixel/dpi
fig, ax = plt.subplots(figsize=(fig_width, fig_height), dpi=dpi)

ax.set_xlim(min_x, max_x)
ax.set_ylim(min_y, max_y)

# WHY IT WORK
ax.set_box_aspect(fig_height/fig_width)
ax.set_position([0.0,0.0,1.0,1.0])
ax.set_axis_off()

# Bar instead of lines. It looks more clean for me
ax.bar(x_pos, height, width=1.0, bottom=bottom, align='edge', color='black')

plt.savefig('test.png')

相关问题