(数据科学学习手札87)利用adjustText解决matplotlib文字标签遮挡问题

本文示例代码、数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes

1 简介

  在进行数据可视化时我们常常需要在可视化作品上进行一些文字标注,譬如对散点图我们可以将每个散点对应的属性信息标注在每个散点旁边,但随着散点量的增多,或图像上的某个区域聚集了较多的散点时,叠加上的文字标注会挤在一起相互叠置,出现如图1所示的情况:

图1

  出现这种情况非常影响数据可视化作品的呈现效果,而我们下面要介绍的adjustText是一个辅助matplotlib所绘制的图像自动调整文字位置以缓解遮挡现象的库,其灵感来源于R中非常著名的辅助ggplot2解决文字遮挡问题的ggrepel

图2

  它通过算法迭代,在一轮轮的迭代过程中逐渐消除文字遮挡现象:

图3

  下面我们就来学习如何使用adjustText解决matplotlib图像文字遮挡问题。

2 使用adjustText解决文字遮挡问题

2.1 从一个简单的例子出发

  使用pip install adjustTextconda install -c conda-forge adjusttext 来安装adjustText。安装成功之后,首先生成随机示例数据以方便之后的演示:

import matplotlib.pyplot as plt
from adjustText import adjust_text
import numpy as np

#解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']

seed = np.random.RandomState(42) # 固定随机数水平
x, y = seed.uniform(0, 1, [2, 100]) # 产生固定的均匀分布随机数
texts = [f'文字{i}' for i in range(x.__len__())]

  接着我们先不使用adjustText调整图像,直接绘制出原始的散点+文字标签

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 绘制散点

# 绘制所有点对应的文字标签
for x_, y_, text in zip(x, y, texts):
    plt.text(x_, y_, text, fontsize=12)

# 美观起见隐藏顶部与右侧边框线
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

fig.savefig('图4.png', dpi=300, bbox_inches='tight', pad_inches=0) # 保存图像
图4

  可以看到,在通常的情况下,散点聚集的区域内文字标签非常容易重叠在一起,接下来我们使用adjustText的基础功能来消除文字重叠现象:

图5

  这时可以看到与图4相比,图5中的所有文字都没有出现彼此重叠现象,adjustText帮助我们自动微调了文字的摆放位置,并且距离原始散点偏移较大的文字还贴心的加上了连接线,至此,我们就初探了adjustText的强大功能,接下来我们来学习adjustText的更多功能。

2.2 adjust_text的用法

  adjustText中的核心功能都通过调用函数adjust_text来实现,其核心参数如下:

texts:List型,每个元素都是表示单个文字标签对应的matplotlib.text.Text对象

ax:绘制文字标签的目标axe对象,默认为最近一次的axe对象

lim:int型,控制迭代调整文本标签位置的次数,默认为500次

precision:float型,用于决定迭代停止的精度,默认为0.01,即所有标签相互遮挡部分的长和宽占所有标签自身长宽之和的比例,addjust_text会在精度达到precision和迭代次数超过lim这两个条件中至少有一个满足时停止迭代

only_move:字典型,用于指定文本标签与不同对象发生遮挡时的位移策略,键有'points''text''objects',对应的值可选'xy''x''y',分别代表竖直和水平方向均调整、只调整水平方向以及只调整竖直方向

arrowprops:字典型,用于设置偏移后的文字标签与原始位置之间的连线样式,下文会作具体演示

save_steps:bool型,用于决定是否保存记录迭代过程中各轮的帧图像,默认为False

save_prefix:str型,当save_steps设置为True时,用于指定中间帧保存的路径,默认为'',即当前工作路径

  下面我们来演示一下这些参数的使用效果,首先我们来看看only_move参数的效果,在图6的基础上,我们设置only_move={'text': 'x'},即当文字出现遮挡时,只在水平方向上进行偏移,这里将save_steps设置为True以直观地查看偏移过程:

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 绘制散点

# 使用adjustText修正文字重叠现象
new_texts = [plt.text(x_, y_, text, fontsize=12) for x_, y_, text in zip(x, y, texts)]
adjust_text(new_texts, 
            only_move={'text': 'x'},
            arrowprops=dict(arrowstyle='-', color='grey'),
            save_steps=True)

# 美观起见隐藏顶部与右侧边框线
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
图6

  可以看到在整个迭代微调的过程中,每个标签只在水平方向发生位移,你可以根据自己作图的实际需要灵活调整这里的平移策略。接下来我们来看看arrowprops对可视化结果的影响,在之前的例子里我们设置了arrowprops={arrowstyle='-', color='grey'},其中arrowstyle用于设定连线的线型,color不用多说,接下来我们添加参数lw用于控制线的宽度,并对线型与颜色进行修改:

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 绘制散点

# 使用adjustText修正文字重叠现象
new_texts = [plt.text(x_, y_, text, fontsize=12) for x_, y_, text in zip(x, y, texts)]
adjust_text(new_texts, 
            arrowprops=dict(arrowstyle='->', 
                            color='red',
                            lw=1))

# 美观起见隐藏顶部与右侧边框线
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

fig.savefig('图7.png', dpi=300, bbox_inches='tight', pad_inches=0) # 保存图像

  这时连线随着我们自定义的设置改变到相应的样式:

图7

  有关adjustText的更多参数设置信息和示例可以去官方文档(https://adjusttext.readthedocs.io/en/latest/ )查看。

  以上就是本文的全部内容,如有疑问欢迎在评论区与我们讨论。

posted @ 2020-06-13 16:05  费弗里  阅读(4253)  评论(3编辑  收藏  举报