R语言用来做数据处理和分析很方便,借助ggplot2能够方便的画出比较漂亮且信息丰富的图形,但是由于历史原因,R对中文的支持并不是很好,尤其是作图的时候需要一些特殊的技巧才能完美的展示中文字体,同时对于中英文混合的情况,经过一番摸索之后也找到了比较完美的方案。
一句话总结:在
plot
或者ggplot2
的theme
中设定family
参数,如需要保存为showtext
包添加字体,如需分别指定中英文字体,就制作合适的中英文混合字体。
原生作图函数的中文显示
plot
函数中设置family
参数
在R中,使用plot
函数画图时如果不指定字体,中文会显示为乱码,比如下面的语句:
plot(c(1:10), xlab = '横轴', ylab = '纵轴',
main = '中文标题 Title')
得到的图形为:
此时可以通过设置family
属性来显示中文字体,如下所示:
plot(c(1:10), xlab = '横轴', ylab = '纵轴',
main = '中文标题 Title', family = 'SimSun')
得到的图形为:
可以发现中文根据设置显示为了宋体,family
的值可以设置为任何在电脑中已经安装的字体名称,只要该字体包含中文字符,那么就可以正常显示中文,此外,如果想要分别设置主标题、副标题、坐标轴等的字体样式,在plot
函数中设置font.main,font.sub,font.axis,font.lab
即可。
通过设置family
参数能够正常显示中文,但是如果想要将画好的图形保存下来还需要费一番周折,对于包含中文的图形,如果存储为png, jpg, tiff
等格式,由于这些都是栅格图形,均是有一定数目的像素点组成,因此不存在字体能否加载的问题,可以直接进行保存。
但是大部分情况下我们希望能够将图形保存为矢量格式,因为矢量格式的文件能够无限放大不失真,并且占用磁盘空间小,方便保存与后续处理,如果有需要的话可以导出为任意分辨率的栅格图像。然而通过设定family
参数得到的包含中文字符的图形在保存为pdf
格式的时候会出错,比如执行下面的语句:
pdf('test.pdf')
plot(c(1:10), xlab = '横轴', ylab = '纵轴',
main = '中文标题 Title', family = 'SimSun')
dev.off()
会得到如下输出:
Error in axis(side = side, at = at, labels = labels, ...) : 字体类别出错
此外: Warning messages:
1: In axis(side = side, at = at, labels = labels, ...) :
PostScript字体数据库里找不到'SimSun'这个字体系列
.........
错误原因提示的也很清楚,生成pdf
文件的图形设备的字体数据库中找不到指定的字体,解决方案是通过pdf('test.pdf', family = 'GB1')
设置pdf图形设备使用GB1字体,这会调用系统默认的中文字体,如果想要使用指定的中文字体,这种方法就不起作用了,这种情况下一种可能的方法是利用extrafont
包注册特定的字体,然后调用Cario
包的CarioPDF
来生成pdf
文件,但是这种方案比较繁琐,并且很容易失败。
比较好的解决方案是利用showtext
包,使用起来非常简单,在此感谢此包的作者,使用方法如下:
library(showtext)
showtext.auto(enable = TRUE)
font.add('SimSun', 'simsun.ttc')
pdf('test.pdf')
plot(c(1:10), xlab = '横轴', ylab = '纵轴',
main = '中文标题 Title', family = 'SimSun')
dev.off()
得到的pdf
文件如下:
![Uploading 62a1a74efedab3a3855c3a78d1b7a882_854328.png . . .]
showtext
包理论上能够调用系统已安装的任何字体,完整的字体列表可以通过font.files()
查看。
有强迫症的同学可能会注意到,标题中的中英文看起来很不协调,这是因为很多中文字体的英文部分不够美观,至于如何在同一行文字中同时使用中文字体和英文字体,会在最后介绍一种可行方法。
ggplot2显示中文字体
ggplot2
的画图功能非常强大,初期不太容易上手,但是习惯了之后会发现真的好用,在此不做过多介绍,只关注显示中文的方法,和plot
函数类似,如果只是希望显示中文或者保存为栅格图像,只需要在画图的时候设定family
参数即可,一种可用的方法如下:
library(ggplot2)
ggplot(data.frame(x = rnorm(100))) +
geom_histogram(aes(x), fill = 'purple', alpha = 0.6) +
labs(x = 'X 取值', y = '频数 Count') +
theme(text = element_text(family = 'SimSun'))
结果为:
如果不通过theme(text = element_text(family = 'SimSun'))
设定字体参数,则会出现中文乱码,同样,ggplot2
能够对标题、坐标轴、坐标刻度等分别设置不同的字体,具体使用方法请参考ggplot
的theme
函数。
如果需要将含有中文字体的图形保存为pdf
文件,同样可以利用前面讲到的方案,最新版的showtext
包已经支持了ggplot2
,推荐使用此种方案。
中英文字体混合显示
前面啰啰嗦嗦的说了一大推,其实总结起来无非是在plot
或者ggplot2
的theme
中设定family
参数,如需要保存为pdf
文件,则利用showtext
包添加字体即可。
但是有时候需要同时显示中文和英文,而plot
和ggplot2
对同一个元素的字体只能设置一种字体,因此可能的解决方案就是使用中英文混合字体,比如知名度很高的YaHei Consolas
混合字体,然而最近由于要写毕业论文,需要黑体和Times New Roman的混合字体,网上搜了一圈没搜到,只能动手制作一个了。Google后发现了一个开源项目:FontForge, 其安装方法见官网说明,该项目能够很方便的用来合并中英文字体,这个开源项目在处理字体上功能很强大,有GUI和命令行两种使用方式,在Mac上其GUI依赖于X11
,但是X11
在最新版的Mac OS上存在诸多问题,因此还是推荐使用命令行,使用起来也非常简单,比如说我们想要合并黑体和Times New Roman,先把这两个字体的字体文件找出来,放在同一个文件夹下面,我这里的文件名分别为simhei.ttf
和TimesNewRoman.ttf
,然后在同一个文件夹下面新建一个脚本文件mergefont.pe
,内容如下:
Open("simhei.ttf")
SelectAll()
ScaleToEm(1024)
Generate("temp.ttf", "", 0x14)
Close()
# Open English font and merge to the Chinese font
Open("TimesNewRoman.ttf")
SelectAll()
ScaleToEm(1024)
MergeFonts("temp.ttf")
SetFontNames("Broman", "Broman", "SimHei Times New Roman Hybrid", "Regular", "")
Generate("Broman.ttf", "", 0x14)
Close()
如果已经成功的安装了fontforge
,在命令行切换到当前目录并执行fontforge -script mergefont.pe
,即可生成Broman.ttf
字体文件,然后双击安装即可。
上述脚本中,第一段代码打开中文字体然后设置合适的缩放比例并保存到临时文件中,第二段代码打开英文字体并进行缩放,第三段代码将二者合并,然后设置合并后字体的名字,类别,描述等信息,并将其导出。FontForge
功能强大,使用方便,并且目前还支持python
脚本,http://fontforge.github.io/en-US/documentation/scripting/如有兴趣,可参考[FontForge Scripting](http://fontforge.github.io/en-US/documentation/scripting/)。
测试一下生成的Broman
混合字体,代码如下:
library(ggplot2)
library(showtext)
showtext.auto(enable = TRUE)
font.add('Broman', 'Broman.ttf')
pdf('test.pdf')
ggplot(data.frame(x = rnorm(100))) +
geom_histogram(aes(x), fill = 'purple', alpha = 0.6) +
labs(x = 'X 取值', y = '频数 Count', title = '标题 Title') +
theme(text = element_text(family = 'Broman'))
dev.off()
得到的pdf
文件如下:
可以看到中文字体显示为黑体,英文字体显示为Times New Roman, 至此,R画图的字体问题基本完美解决。