已保存的“matplotlib”/“seaborn”图中的左右对齐文本
Right and left justified text in a saved `matplotlib`/`seaborn` figure
我在 seaborn
中制作了一个热图,我需要在角落里有文字。
有:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(2021)
data = pd.DataFrame(np.random.randint(0, 5, (3, 5)))\
.rename({0: "Supercalifragilisticexpialidocious",
1: "Humuhumunukunukuapua'a",
2: "Hippopotomonstrosesquipedaliophobia"
})
sns.heatmap(data)
plt.savefig("dave.png")
想要:
plt.figtext
命令过去曾为此工作过,但我对格式化左右对齐(以及与顶部和底部的距离)感到沮丧,所以我只想有一个标准距离从边缘,这听起来像是正当的。听起来这是在 figtext
中更改坐标的问题,我还没有弄清楚该怎么做,但我认为这还不够。由于绘图可以向左延伸很远,我需要 GHI 和 JKL 位于已保存图像的左侧,而不仅仅是绘图区域。左边那些词的长度因情节而异,无论如何,我希望 GHI 和 JKL 左对齐,无论长词是“Hippopotomonstrosesquipedaliophobia”还是“Dave”(但它不应该是left,超过单词的左边缘,当这些单词很短时)。
执行这个的方法是什么?
我想知道如何让这样的图像出现在 Jupyter Notebook 中或当我从命令行 运行 我的脚本时弹出会很好,但我主要关心的是保存图像那些 ABC、DEF、GHI 和 JKL 评论。
一种方法是使用 Gridspec。定义一个包含 4 行和 4 列的网格,并为网格中的每个绘图位置创建轴。
在网格的中心绘制热图,在网格的边界绘制标签。
边框将定义一个 1x1 图,您可以使用 axis.text() 调整每个单独图内的标签位置,或者通过向网格添加更多行和列来为标签提供更多 space。
最后用ax.axis('off')
隐藏每个绘图标签的轴,你得到想要的输出
完整示例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(2021)
data = pd.DataFrame(np.random.randint(0, 5, (3, 5)))\
.rename(
{
0: "Supercalifragilisticexpialidocious",
1: "Humuhumunukunukuapua'a",
2: "Hippopotomonstrosesquipedaliophobia"
}
)
gs = gridspec.GridSpec(4, 4)
# Upper left label
ax1 = plt.subplot(gs[:1, :1])
ax1.text(0.5, 0.5, 'GHI', fontsize='xx-large')
ax1.axis('off')
# Lower left label
ax2 = plt.subplot(gs[3:4, :1])
ax2.text(0.5, 0.5, 'JKL', fontsize='xx-large')
ax2.axis('off')
# Upper right label
ax3 = plt.subplot(gs[:1, 3:4])
ax3.text(0, 0.5, 'ABC', fontsize='xx-large')
ax3.axis('off')
# Lower Right label
ax4 = plt.subplot(gs[3:4, 3:4])
ax4.text(0, 0.5, 'DEF', fontsize='xx-large')
ax4.axis('off')
ax5 = plt.subplot(gs[1:3, 1:3])
sns.heatmap(data, ax=ax5)
参考资料
- Hiding axis text in matplotlib plots
- Putting text in top left corner of matplotlib plot
要将文本放在情节的角落,您需要
- 在你的情节周围为文本腾出一些空间
- 获取文本位置总图的范围(边界框)。
对于 1),您可以指定一个 constrained layout
,其中一些垫子足够大以容纳文本。对于 2),您需要获取热图本身的边界框及其图形坐标中的颜色条。然后将这两个框合并,并将相应对齐方式的文本放置在这个统一边界框的四个角上。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(2021)
data = pd.DataFrame(np.random.randint(0, 5, (3, 5)))\
.rename({0: "Supercalifragilisticexpialidocious",
1: "Humuhumunukunukuapua'a",
2: "Hippopotomonstrosesquipedaliophobia"
})
fig, ax = plt.subplots(constrained_layout={'w_pad': .5, 'h_pad': .5})
sns.heatmap(data, ax=ax)
fig.draw_without_rendering()
cb = fig.axes[1]
ax_extent = fig.transFigure.inverted().transform_bbox(ax.get_tightbbox(fig.canvas.get_renderer()))
cb_extent = fig.transFigure.inverted().transform_bbox(cb.get_tightbbox(fig.canvas.get_renderer()))
total_extent = ax_extent.union([ax_extent, cb_extent])
# uncomment the following to show axes and colorbar extents
#from matplotlib.patches import Rectangle
#for extent in (ax_extent, cb_extent):
# fig.add_artist(Rectangle(extent.p0, extent.width, extent.height, ec='.6', ls='dotted', fill=False))
fig.text(total_extent.x1, total_extent.y1, 'ABC', ha='center', va='bottom', fontsize=20)
fig.text(total_extent.x1, total_extent.y0, 'DEF', ha='center', va='top', fontsize=20)
fig.text(total_extent.x0, total_extent.y1, 'GHI', ha='center', va='bottom', fontsize=20)
fig.text(total_extent.x0, total_extent.y0, 'KLM', ha='center', va='top', fontsize=20)
这也适用于短 y 标签,无需 fiddle 文本位置:
根据您的喜好,您可以将文本的水平对齐方式分别从 'center'
更改为 'right'
和 'left'
。
为了更好地理解它的工作原理,您可以取消注释用于可视化边界框的三行。从这里你会看到我们需要两个框的结合,以便将文本整齐地放在相同的 y 位置,因为颜色条延伸到热图之外:
我在 seaborn
中制作了一个热图,我需要在角落里有文字。
有:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(2021)
data = pd.DataFrame(np.random.randint(0, 5, (3, 5)))\
.rename({0: "Supercalifragilisticexpialidocious",
1: "Humuhumunukunukuapua'a",
2: "Hippopotomonstrosesquipedaliophobia"
})
sns.heatmap(data)
plt.savefig("dave.png")
想要:
plt.figtext
命令过去曾为此工作过,但我对格式化左右对齐(以及与顶部和底部的距离)感到沮丧,所以我只想有一个标准距离从边缘,这听起来像是正当的。听起来这是在 figtext
中更改坐标的问题,我还没有弄清楚该怎么做,但我认为这还不够。由于绘图可以向左延伸很远,我需要 GHI 和 JKL 位于已保存图像的左侧,而不仅仅是绘图区域。左边那些词的长度因情节而异,无论如何,我希望 GHI 和 JKL 左对齐,无论长词是“Hippopotomonstrosesquipedaliophobia”还是“Dave”(但它不应该是left,超过单词的左边缘,当这些单词很短时)。
执行这个的方法是什么?
我想知道如何让这样的图像出现在 Jupyter Notebook 中或当我从命令行 运行 我的脚本时弹出会很好,但我主要关心的是保存图像那些 ABC、DEF、GHI 和 JKL 评论。
一种方法是使用 Gridspec。定义一个包含 4 行和 4 列的网格,并为网格中的每个绘图位置创建轴。
在网格的中心绘制热图,在网格的边界绘制标签。
边框将定义一个 1x1 图,您可以使用 axis.text() 调整每个单独图内的标签位置,或者通过向网格添加更多行和列来为标签提供更多 space。
最后用ax.axis('off')
隐藏每个绘图标签的轴,你得到想要的输出
完整示例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(2021)
data = pd.DataFrame(np.random.randint(0, 5, (3, 5)))\
.rename(
{
0: "Supercalifragilisticexpialidocious",
1: "Humuhumunukunukuapua'a",
2: "Hippopotomonstrosesquipedaliophobia"
}
)
gs = gridspec.GridSpec(4, 4)
# Upper left label
ax1 = plt.subplot(gs[:1, :1])
ax1.text(0.5, 0.5, 'GHI', fontsize='xx-large')
ax1.axis('off')
# Lower left label
ax2 = plt.subplot(gs[3:4, :1])
ax2.text(0.5, 0.5, 'JKL', fontsize='xx-large')
ax2.axis('off')
# Upper right label
ax3 = plt.subplot(gs[:1, 3:4])
ax3.text(0, 0.5, 'ABC', fontsize='xx-large')
ax3.axis('off')
# Lower Right label
ax4 = plt.subplot(gs[3:4, 3:4])
ax4.text(0, 0.5, 'DEF', fontsize='xx-large')
ax4.axis('off')
ax5 = plt.subplot(gs[1:3, 1:3])
sns.heatmap(data, ax=ax5)
参考资料
- Hiding axis text in matplotlib plots
- Putting text in top left corner of matplotlib plot
要将文本放在情节的角落,您需要
- 在你的情节周围为文本腾出一些空间
- 获取文本位置总图的范围(边界框)。
对于 1),您可以指定一个 constrained layout
,其中一些垫子足够大以容纳文本。对于 2),您需要获取热图本身的边界框及其图形坐标中的颜色条。然后将这两个框合并,并将相应对齐方式的文本放置在这个统一边界框的四个角上。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(2021)
data = pd.DataFrame(np.random.randint(0, 5, (3, 5)))\
.rename({0: "Supercalifragilisticexpialidocious",
1: "Humuhumunukunukuapua'a",
2: "Hippopotomonstrosesquipedaliophobia"
})
fig, ax = plt.subplots(constrained_layout={'w_pad': .5, 'h_pad': .5})
sns.heatmap(data, ax=ax)
fig.draw_without_rendering()
cb = fig.axes[1]
ax_extent = fig.transFigure.inverted().transform_bbox(ax.get_tightbbox(fig.canvas.get_renderer()))
cb_extent = fig.transFigure.inverted().transform_bbox(cb.get_tightbbox(fig.canvas.get_renderer()))
total_extent = ax_extent.union([ax_extent, cb_extent])
# uncomment the following to show axes and colorbar extents
#from matplotlib.patches import Rectangle
#for extent in (ax_extent, cb_extent):
# fig.add_artist(Rectangle(extent.p0, extent.width, extent.height, ec='.6', ls='dotted', fill=False))
fig.text(total_extent.x1, total_extent.y1, 'ABC', ha='center', va='bottom', fontsize=20)
fig.text(total_extent.x1, total_extent.y0, 'DEF', ha='center', va='top', fontsize=20)
fig.text(total_extent.x0, total_extent.y1, 'GHI', ha='center', va='bottom', fontsize=20)
fig.text(total_extent.x0, total_extent.y0, 'KLM', ha='center', va='top', fontsize=20)
这也适用于短 y 标签,无需 fiddle 文本位置:
根据您的喜好,您可以将文本的水平对齐方式分别从 'center'
更改为 'right'
和 'left'
。
为了更好地理解它的工作原理,您可以取消注释用于可视化边界框的三行。从这里你会看到我们需要两个框的结合,以便将文本整齐地放在相同的 y 位置,因为颜色条延伸到热图之外: