我正在向散点图的点添加文本注解,如下所示:
我事先不知道文本注解的长度。上图显示了文本注解延伸到绘图区域之外。
我想在数据坐标中找到每个文本注解的长度,以便通过设置ylim来动态调整绘图区域中所有文本注解的大小。
我尝试了三种基于以下堆栈溢出答案的解决方案。
我使用两种设置进行了测试:
- macOS Catalina - 10。15.7
- Python - 3.8。14
- matplotlib - 3。3.1
和
- Windows 11 - 22H2
- Python - 3.10.2
- matplotlib - 3.5.1
import matplotlib.pyplot as plt
from matplotlib.transforms import TransformedBbox, Bbox
def get_text_object_height_1(text_obj, _ax):
# Based on
# https://stackoverflow.com/questions/24581194/matplotlib-text-bounding-box-dimensions
# to get the text bounding box we need to draw the plot
_fig = _ax.get_figure()
_fig.canvas.draw()
# get bounding box of the text in the data coordinates
bb = text_obj.get_window_extent(renderer=_fig.canvas.get_renderer())
transform = _ax.transData.inverted()
trans_box = TransformedBbox(bb, transform)
return trans_box.height
def get_text_object_height_2(text_obj, _ax):
# Based on
# https://stackoverflow.com/a/35419796/2912349
# https://stackoverflow.com/questions/58854335/how-to-label-y-ticklabels-as-group-category-in-seaborn-clustermap/58915100#58915100
# Must move the plot to see the last update. When the plot is saved, the last update is included.
# the figure needs to have been drawn once, otherwise there is no renderer
plt.ion()
plt.show()
plt.pause(1) # Can make the pause smaller. Kept it lager to see the action.
# get bounding box of the text in the data coordinates
bb = text_obj.get_window_extent(renderer=_ax.get_figure().canvas.get_renderer())
transform = _ax.transData.inverted()
trans_box = TransformedBbox(bb, transform)
plt.ioff()
return trans_box.height
def get_text_object_height_3(text_obj, _ax):
# Based on
# https://stackoverflow.com/questions/5320205/matplotlib-text-dimensions
# to get the text bounding box we need to draw the plot
_fig = _ax.get_figure()
# get text bounding box in figure coordinates
renderer = _fig.canvas.get_renderer()
bbox_text = text_obj.get_window_extent(renderer=renderer)
# transform bounding box to data coordinates
trans_box = Bbox(_ax.transData.inverted().transform(bbox_text))
return trans_box.height
x = [1, 2, 3]
y = [5, 8, 7]
labels = ['mid length', 'short', 'this is a long label']
fig, ax = plt.subplots(dpi=300, figsize=(5, 3))
ax.scatter(x=x, y=y)
y_lim_max = 0
gap = 0.3
for idx, label in enumerate(labels):
label_y_start = y[idx] + gap
txt = ax.text(x[idx], label_y_start, label, rotation='vertical', fontdict=dict(color='black', alpha=1, size=8),
transform=ax.transData)
# Can change to _2, _3 to see how other two methods work
label_length = get_text_object_height_1(txt, ax)
label_y_end = label_y_start + label_length
# Just to show the computed label length
plt.plot([x[idx], x[idx]], [label_y_start, label_y_end])
y_lim_max = label_y_end if y_lim_max < label_y_end else y_lim_max
print(f'\t{y_lim_max:8.2f}')
plt.ylim(0, y_lim_max)
plt.tight_layout()
plt.show()
# plt.savefig('scaled_plot.png')
但是,计算的文本注解长度比文本注解短。请注意,我在每个文本注解旁边绘制了一条线,以演示相应文本注解的计算长度。
我做错了什么吗?
是否有一种方法可以获得文本注解的正确尺寸?
是否有不同的方法来调整绘图区域的大小,以包括所有的文本注解?
1条答案
按热度按时间kx5bkwkv1#
这本身并不是一个答案,而是证明了清洁解决方案的不可能性。
基于特伦顿MaKinney的评论,我创建了以下迭代解决方案:多次重绘该图,直到生成OK图。
在我测试代码的系统中,循环迭代了14次,这意味着它为标签提供了14种不同的高度。
我对未来的直觉
坐标系变换矩阵可以取决于xlim和ylim值。我们使用当前的变换矩阵来计算标签的维数,然后更新ylim。这会使我们用来计算标签尺寸的变换矩阵无效,使其在更新后的坐标系中不正确。