01. 引言
在进行数据可视化作品绘制时,我们需要在相应位置添加文本标签进行标注或者解释说明使用,少量数据点进行标注时相对简单,也比较明确,当需要标注的数据较多、或集中在一个区域时,标注文本就会产生相互叠加,影响标注内容和美观。如下:
(图中红色圆框内产生文本叠加问题)
本期推文将分别介绍使用R-ggrepel和python-adjustText解决此类问题,最后通过一个可视化作品比较两种方法的优缺点。
02. R-ggrepel简介
R-ggrepel(https://ggrepel.slowkow.com/)是R绘图包中专门解决文本重叠的第三方包,完美兼容ggplot2 ,使得绘制大数据标注重叠问题得以解决。官网简介如下:
这里就放下官网的例子,更多功能强大的绘制方法,大家可自行去官网查看哦
geom_text_repel()
geom_label_repel()
geom_label_repel(fill = "white", xlim = c(-Inf, Inf), ylim = c(-Inf, Inf))
此外,还有更多功能强大的函数,部分可视化效果如下:
03. Python-adjustText简介
adjustText(https://github.com/Phlya/adjustText)作为matpotlib 文本标注的辅助库,其设计灵感来源于 R-ggrepel包,算是 Python绘图体系中解决文本重叠问题较好的第三方库。具体的例子也在对应的github上,adjustText库中的核心功能可通过adjust_text()方法调用,核心参数如下:
texts
:List型,每个元素都是表示单个文字标签对应的matplotlib.text.Text对象。
ax
:绘制文字标签的目标axe对象,默认为最近一次的axe对象。
lim
:int型,控制迭代调整文本标签位置的次数,默认为500次。
precision
:float型,用于决定迭代停止的精度,默认为0.01。
only_move
:字典型,用于指定文本标签与不同对象发生遮挡时的位移策略,可选择'points'、'text'和'objects',对应值可选'xy'、'x'、'y',分别代表竖直和水平方向均调整、只调整水平方向以及只调整竖直方向。
arrowprops
:字典型,用于设置偏移后的文字标签与原始位置之间的连线样式。和matplotlib 的ax.annotate()使用相似。
save_steps
:bool型,用于决定是否保存记录迭代过程中各轮的帧图像,默认为False。
save_prefix
:str型,当save_steps设置为True时,用于指定中间帧保存的路径,默认为'',即当前工作路径。
04. 可视化案例
我们使用一个具体涉及到文本重叠的可视化作品制作过程来对比两种方法,下面直接给出代码
(1)使用 ggplot2 ggrepel 进行绘制
代码语言:javascript复制p <- df_ghibli_unique %>%
ggplot(aes(score, scored_by))
geom_point(aes(size = members), color = "#F4C59D", alpha = 0.6)
geom_text_repel(data = filter(df_ghibli_unique, scored_by > 120000), aes(label = title), size = 1.75, family = "Poppins",
color = "#F4C59D", segment.size = 0.3, xlim = c(9.25, 10), box.padding = 0.5, force = 5)
scale_x_continuous(limits = c(5, 10))
scale_y_continuous(labels = scales::comma, limits = c(0, 600000))
scale_size_continuous(name = "Times listed by MAL users:",
breaks = c(1000, 10000, 100000, 250000, 500000),
labels = c(" 1,000", " 10,000", "100,000", "250,000", "500,000"))
guides(size = guide_legend(override.aes = list(alpha = 1)))
labs(x = "Average MAL user score", y = "Number of ratings")
annotation_custom(b, xmin = 5, xmax = 8, ymin = 400000, ymax = 600000)
theme(text = element_text(family = "Roboto_Mono",colour = "#ffffff"),
axis.text = element_text(family = "Roboto_Mono",colour = "#ffffff"),
axis.title.x = element_text(margin = unit(c(3.5, 0, 0, 0), "mm"), vjust = 1, size = 15, face = "bold",
color = '#ffffff'),
axis.title.x.top = element_text(margin = margin(b = 6), vjust = 0),
axis.title.y = element_text(angle = 90, margin = unit(c(0, 3.5, 0, 0), "mm"), vjust = 1, size = 15,
face = "bold",color = '#ffffff'),
axis.title.y.right = element_text(angle = -90, margin = margin(l = 6), vjust = 0),
axis.ticks.length = unit(3, "pt"),
axis.ticks = element_line(colour = "grey85", size = .3),
panel.grid = element_blank(),
panel.border = element_rect(colour = "grey85", fill = NA, size = rel(1)),
panel.background = element_rect(fill = '#333333', colour = '#333333'),
plot.background = element_rect(fill = '#333333',colour = '#333333'),
legend.position = c(0.32, 0.4),
legend.background = element_rect(fill = "#333333"),
legend.key = element_rect(fill = "#333333"),
legend.title = element_text(size = 9),
legend.text = element_text(family = "Roboto_Mono", size = 5))
p
文本重叠处理代码:
代码语言:javascript复制geom_text_repel(data = filter(df_ghibli_unique, scored_by > 120000), aes(label = title), size = 1.75,
family = "Poppins", color = "#F4C59D", segment.size = 0.3, xlim = c(9.25, 10),
box.padding = 0.5, force = 5)
可视化效果如下:
(图中红框为使用geom_text_repel()后的效果)
(2)使用 Seaborn adjustText 进行绘制
代码语言:javascript复制import matplotlib.ticker as ticker
import seaborn as sns
#文本自动排序库
from adjustText import adjust_text
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
img = "01.png"
fig, ax = plt.subplots(figsize=(8,5),dpi=200,facecolor='#323332',edgecolor='#323332')
ax.set_facecolor("#323332")
image = plt.imread(img)
scatter_s = sns.scatterplot(x='score',y='scored_by',size='members',
data=df_ghibli_unique,color='#F4C59D',
alpha=.6,ec='#F4C59D',
sizes=(20,200),
ax=ax)
for spine in ['top','bottom','left','right']:
ax.spines[spine].set_color("#FFFFFF")
ax.spines[spine].set_linewidth(1)
ax.tick_params(labelsize = 15,colors="#FFFFFF")
#定制图例标签
labels=[' 1000', ' 10000', ' 200000','500000']
labels = ['{:,.0f}'.format(int(label)) for label in labels]
scatter_s.legend_.get_texts()[0].set_text('Times listed by MAL users:')
for i, label in enumerate(labels):
scatter_s.legend_.get_texts()[i 1].set_text(label)
#设置图例字体颜色及背景
scatter_s.legend_.get_frame().set_facecolor('#858A8D')
scatter_s.legend_.get_title().set_color('#FFFFFF')
scatter_s.legend_.set_bbox_to_anchor((.5, .65))
#scatter_s.legend_.
for text in scatter_s.legend_.get_texts():
text.set_color('#FFFFFF')
ax.set_xlim(left=4.5,right=10.5)
#ax.set_ylim(bottom=-100000,top=650000,200000)
ax.set_yticks(np.arange(0,700000,200000))
ax.set_ylim(bottom=-50000,top=650000)
ax.set_xlabel('Average MAL user score',fontweight='bold',c="#FFFFFF",fontsize=17)
ax.set_ylabel(ylabel='Numbers of ratings',fontweight='bold',c="#FFFFFF",fontsize=17)
#修改刻度显示
ax.yaxis.set_major_formatter(ticker.StrMethodFormatter("{x:,.0f}"))
#绘制文本不重合
text_data = df_ghibli_unique.loc[df_ghibli_unique.scored_by>120000,:]
texts = []
for i, j ,t in zip(text_data.score,text_data.scored_by,text_data.title):
texts.append(ax.annotate(t,xy=(i, j),xytext=(i 2.5,j-10),
arrowprops=dict(arrowstyle="-", color="#F4C59D",lw=.5),
family = "MS Gothic",color='#F4C59D',size=8))
adjust_text(texts,only_move={'text': 'y','objects':'x','point':'y'})
#添加图片:在adjust_text之后,否则会出错
image = plt.imread(img)
aximins = inset_axes(ax,width=3,height=4,
bbox_to_anchor=(.05, .85, .5, .5),
bbox_transform=ax.transAxes)
im = aximins.imshow(image,zorder=0)
aximins.axis('off')
ax.text(.87,-.2,'nVisualization by DataCharm',transform = ax.transAxes,
ha='center', va='center',fontsize = 7,color='white',fontweight='bold',family='Roboto Mono')
plt.show()
文本重叠处理代码:
代码语言:javascript复制#绘制文本不重合
text_data = df_ghibli_unique.loc[df_ghibli_unique.scored_by>120000,:]
texts = []
for i, j ,t in zip(text_data.score,text_data.scored_by,text_data.title):
texts.append(ax.annotate(t,xy=(i, j),xytext=(i 2.5,j-10),
arrowprops=dict(arrowstyle="-", color="#F4C59D",lw=.5),
family = "MS Gothic",color='#F4C59D',size=8))
adjust_text(texts,only_move={'text': 'y','objects':'x','point':'y'})
最终可视化效果如下:
(别问我为啥这个图例和上面的图不一样啊
,没办法啊!seaborn修改图例标题颜色我查了好久资料,愣是没找到
,也希望小伙伴们在文末的问题讨论中给出答案或参考资料啊
)
05. 总结
本次推文对比了两种解决绘图文本重叠的解决方法,并结合实例进行可视化结果对比,纠结过而言,还是 R-ggrepel 包的效果更好。