matplotlib 调整刻度以适合图形

koaltpgm  于 2023-05-23  发布在  其他
关注(0)|答案(4)|浏览(225)

我有下面的matplotlib代码,它所做的就是在x轴上绘制0-20,在y轴上绘制0-100

import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(20))
ax.set_yticks(range(100))
labels = ax.set_yticklabels(range(100))

我遇到的问题是,并非所有的标签实际上都在y轴内。
我想做的是将最大标签大小划分为图形的总面积,以计算我可以放置多少标签,然后我可以将数据分组为部分0-10,10-20,30-40并绘制这些,而不是显示单个标签1,2,3,4,5,6,7,8......
我在查找标签的标签大小和图的标签大小时遇到了麻烦(如果我错了请纠正我,也许是像素轴的大小。第一次使用matplotlib)
我怎样才能知道图形/轴占用的像素数(宽度+高度)以及每个标签的像素数(宽度+高度)?

py49o6xq

py49o6xq1#

只需添加以下行:
plt.tight_layout()

w8f9ii69

w8f9ii692#

你可以用ax.get_position()来获取坐标轴的范围,参见axes的文档(我会发布一个链接,但我目前的声誉不允许)。
刻度标签大小是在matplotlibrc文件中相对于字体大小定义的。详细信息here默认大小为12 pt,可以使用

plt.rcParams['ytick.labelsize'] = small

从标准的matplotibrc文件:
可以使用以下值相对于font.size定义特殊文本大小:xx-小,x-小,小,中,大,x-大,xx-大,更大,或更小
解决问题的最简单方法可能是使用

fig = plt.figure(figsize=(10,30))

在您的情况下使用以下result(注意,该示例有点极端)。如果有太多的ticklabel,你必须增加总的数字大小这么多,你应该考虑减少ticklabel大小如上所示或减少ticklabel的数量。
请注意,此过程涉及手动调整总图和/或标记的大小,直到获得所需的输出。如果有更好的方法,我很乐意看到。
您可以使用plt.savefig(output.png)将绘图保存在当前工作目录中。如果你使用plt.show(),并且图像比弹出窗口大,标签总是会被弄乱,因为总的图形缩小到窗口大小,但标签大小保持不变。
我希望这个答案对你有帮助。

zxlwwiss

zxlwwiss3#

此答案适用于Matplotlib版本3.5.2
我怎样才能知道图/轴所占的像素数(宽度+高度)
全图大小(像素):

f = plt.gcf()
print(f.get_size_inches() * f.dpi)

该图也有一个bbox,这是由__repr__方法打印的图的大小。没有弄清楚这两者之间的全部区别,但这是如何获得bbox的像素大小:

print(f.bbox.size)

以及每个标签的像素数(宽度+高度)?
可以使用get_window_extent方法来获取边界框的大小。因此,要获取轴和标签的像素大小和左下角位置(X向右正,Y向上正):

ax = plt.gca()
print("Axes bounding box:", ax.get_window_extent().bounds)
yticklables = ax.get_ymajorticklabels()
for i, ylb in enumerate:
     print("Y label", i, ylb.get_text(), ylb.get_window_extent().bounds)

作为一个动态的解决方案,使tick标签显示为过度拥挤的tick标签,我们探索了两种方法:
1.减小字体大小以使所有刻度可见->这具有对于刻度数量和图形大小的特定组合具有难以辨认的刻度标签的缺点
1.跳过记号标签以防止重叠->这具有并非所有Y值都将具有标签的缺点
对于这两个方法,我们需要以下代码(从OP的示例开始):

pix2pt = 3/4
pt2pix = 1/pix2pt

get_label_bounds = lambda lab: lab.get_window_extent().bounds

bottom_label_bounds = get_label_bounds(labels[0])
top_label_bounds = get_label_bounds(labels[-1])

top_label_y = top_label_bounds[1]
top_label_height = top_label_bounds[3]
bottom_label_y = bottom_label_bounds[1]

# we add the height for top label because we get the bottom left position
ylabels_pix_length = top_label_y + top_label_height - bottom_label_y

cur_font_size = labels[0].get_size()

减小字体大小,使所有刻度可见

desired_ylab_pix_length = ylabels_pix_length/len(labels)
desired_ylab_font_size = desired_ylab_pix_length*pix2pt

ax.set_yticklabels(labels, fontdict={"fontsize": desired_ylab_font_size})

以OP为例的结果;标签难以辨认:

跳过勾号标签,防止重叠

我认为使用matplotlib.ticker可以完成与这里描述的相同的事情。在不方便的情况下,仍然可以期望使用这个。我的用例是使用一个seaborn热图,其中自定义格式化的日期时间作为y tick标签。

yt_pos = ax.get_yticks()
cur_font_size_pix = cur_font_size*pt2pix + 1
desired_number_of_labels = int(ylabels_pix_length/cur_font_size_pix)

label_step = int(len(labels)/desired_number_of_labels) + 1
desired_labels = labels[::label_step]
desired_label_pos = yt_pos[::label_step]

ax.set_yticks(desired_label_pos)
desired_labels = ax.set_yticklabels(desired_labels)

跳过标签的结果:

m3eecexj

m3eecexj4#

另一种选择是将每隔一个标签稍微向左推。除了一个更小的yticklabel大小,这可以看起来很好:

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

# Generate some random data
labels = 'abcdefghijklmnopqrstuvwxyz'
data = np.random.random((len(labels), 100))

# Plot it, setting the ticklabel size temporarily
# this is helpful for adjusting your plot until it's right
# without having an influence on other plots outside the context
with mpl.rc_context(rc={'ytick.labelsize': 6}):
    fig, ax = plt.subplots(1, 1, figsize=(6.5,5))
    im = ax.imshow(data)

    # Plot the labels on the yaxis
    ax.set_yticks(range(len(labels)))

    # Every second label gets a bit of whitespace to the right
    # thus effectively it is pushed to the left
    labels_formatted = [label if i%2==0 else label+' '*3 for i,label in enumerate(labels)]

    ax.set_yticklabels(labels_formatted)

相关问题