英文:
How to determine the size of the longest ticklabels to use in the startposition of a line
问题
在这个图表中,我想要在水平详细栏和总栏之间绘制一条水平线。
这条线的起点需要与最长标签的yticklabel的第一个字符的位置相同。
在下面的示例中,我尝试了不同的值,并视觉上确定了在这种情况下使用的值是合适的,但如果最长的标签不是"Second",而是"twentyfirst"之类的话,那么黑线的起始位置会更靠左。
如何确定或自动计算这个位置呢?
我尝试搜索解决方案,但这似乎是一个非常特定的主题。
import matplotlib.pyplot as plt
# Bar data
bartitles = ["First", "Second", "Third"]
barvalues = [4, 3, 5]
total_value = sum(barvalues)
# Figure and axes
fig, ax = plt.subplots()
# Plot bars
y_pos = list(range(1, 4))
ax.barh(y_pos, barvalues, align='center', color='darkgrey')
# Totalbar
y_pos_total = 0 # Position of the totalbar
ax.barh(y_pos_total, total_value, align='center', color='blue')
bartitles.append("Total")
y_pos.append(y_pos_total)
# Plot ticks and titles
ax.set_yticks(y_pos)
ax.set_yticklabels(bartitles)
# Line which starts outside the horizontal barchart
start_position = -0.115 # Value after optical trying different values to get the start equal to the 'S' of 'Second'
# How can this start_position be calculated when the barnames have other lengths?
end_position = 1 # with transformed coordinates, 1 is to the end of the drawing canvas
y_coordinate_line = 0.5
trans = ax.get_yaxis_transform()
ax.plot([start_position, end_position], [y_coordinate_line, y_coordinate_line], color="black", transform=trans, clip_on=False)
plt.show()
这是位于蓝色条之上的黑线。
英文:
In this graph I want a horizontal line between the horizontal detailbars and the totalbar.
The line needs to start at the same position as the first character of the yticklabel of the longest label.
In the example below, I've tried different values myself and optically determined that the used value was ok in this case, but if the longest label was not 'second', but for example 'twentyfirst', then the startposition of the black line would start even more on the left.
How can this be determined or calculated automatically?
I've tried searching for solutions, but this seems a very specific subject.
import matplotlib.pyplot as plt
# Gegevens voor de balken
bartitles = ["First", "Second", "Third"]
barvalues = [4, 3, 5]
total_value = sum(barvalues)
# Figure and axes
fig, ax = plt.subplots()
# Plot bars
y_pos = list(range(1,4))
ax.barh(y_pos, barvalues, align='center', color='darkgrey')
# Totalbar
y_pos_total = 0 # Position of the totalbar
ax.barh(y_pos_total, total_value, align='center', color='blue')
bartitles.append("Total")
y_pos.append(y_pos_total)
# plot ticks and titles
ax.set_yticks(y_pos)
ax.set_yticklabels(bartitles)
# Line which starts outside the horizontal barchart
start_position = -0.115 # Value after optical trying different values to get the start equal to the 'S' of 'Second'
# How can this start_position be calculated when the barnames have other lenghts?
end_position = 1 # with transformed coordinates, 1 is to the end of the drawing canvas
y_coordinate_line = 0.5
trans = ax.get_yaxis_transform()
ax.plot([start_position, end_position], [y_coordinate_line, y_coordinate_line], color="black", transform=trans, clip_on=False)
plt.show()
答案1
得分: 1
以下是代码的翻译部分:
# 获取y轴刻度标签的边界框
ytickboxes = [l.get_window_extent() for l in ax.get_yticklabels()]
# 转换边界框到数据坐标系
ytickboxesdatacoords = [l.transformed(ax.transAxes.inverted()) for l in ytickboxes]
# 找到最左边的值作为`start_position`
start_position = min([l.x0 for l in ytickboxesdatacoords])
# 图形需要在找到坐标之前绘制,所以使用`fig.draw_without_rendering()`来实现。
# 由于您还使用了`fig.tight_layout()`,因此需要在绘图后立即调用它,因为在调用`tight_layout()`后坐标会发生变化。
fig.draw_without_rendering()
fig.tight_layout()
# 获取y轴刻度标签的边界框
ytickboxes = [l.get_window_extent() for l in ax.get_yticklabels()]
# 转换边界框到数据坐标系
ytickboxesdatacoords = [l.transformed(ax.transAxes.inverted()) for l in ytickboxes]
# 找到最左边的值作为`start_position`
start_position = min([l.x0 for l in ytickboxesdatacoords])
# 其他部分...
# 创建图形和轴
fig, ax = plt.subplots()
# 绘制条形图
y_pos = list(range(1, 4))
ax.barh(y_pos, barvalues, align='center', color='darkgrey')
# 绘制总条
y_pos_total = 0 # 总条的位置
ax.barh(y_pos_total, total_value, align='center', color='blue')
bartitles.append("Total")
y_pos.append(y_pos_total)
# 绘制刻度和标题
ax.set_yticks(y_pos)
ax.set_yticklabels(bartitles)
# 绘制坐标轴上的线
end_position = 1
y_coordinate_line = 0.5
trans = ax.get_yaxis_transform()
ax.plot([start_position, end_position], [y_coordinate_line, y_coordinate_line], color="black", transform=trans, clip_on=False)
plt.show()
这些是代码的翻译部分,其他内容不会被翻译。
英文:
Following this answer to get the bounding boxes, you can convert them to the data coordinates using this answer and then find the leftmost value as your start_position
.
ytickboxes = [l.get_window_extent() for l in ax.get_yticklabels()]
ytickboxesdatacoords = [l.transformed(ax.transAxes.inverted()) for l in ytickboxes]
start_position = min([l.x0 for l in ytickboxesdatacoords])
The figure needs to be drawn before the coordinates can be found, so using fig.draw_without_rendering()
does that. Since you are also using fig.tight_layout()
you will need to call that just after the drawing, since the coordinates change after calling tight_layout()
.
In your code, with a longer label, we get:
import matplotlib.pyplot as plt
bartitles = ["First", "0123456789", "Third"]
barvalues = [4, 3, 5]
total_value = sum(barvalues)
# Figure and axes
fig, ax = plt.subplots()
# Plot bars
y_pos = list(range(1,4))
ax.barh(y_pos, barvalues, align='center', color='darkgrey')
# Totalbar
y_pos_total = 0 # Position of the totalbar
ax.barh(y_pos_total, total_value, align='center', color='blue')
bartitles.append("Total")
y_pos.append(y_pos_total)
# plot ticks and titles
ax.set_yticks(y_pos)
ax.set_yticklabels(bartitles)
fig.draw_without_rendering()
fig.tight_layout()
ytickboxes = [l.get_window_extent() for l in ax.get_yticklabels()]
ytickboxesdatacoords = [l.transformed(ax.transAxes.inverted()) for l in ytickboxes]
start_position = min([l.x0 for l in ytickboxesdatacoords])
end_position = 1
y_coordinate_line = 0.5
trans = ax.get_yaxis_transform()
ax.plot([start_position, end_position], [y_coordinate_line, y_coordinate_line], color="black", transform=trans, clip_on=False)
plt.show()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论