(八)高级绘图——初窥ggplot2

ggplot2包的目的是提供一个全面的、基于语法的、连贯一致的图形生成系统,允许用户创建新颖的、有创新性的数据可视化图形。

1、简介ggplot2语法

下面将从一个例子来看看ggplot2的基本语法。

library(ggplot2)    # 载入ggplot2包   
ggplot(data=mtcars, aes(x=wt,y=mpg)) +    # 指定绘图的数据   (1)
  geom_point(pch=17,color='red',size=2) +   # 绘图函数及其参数  (2)
  geom_smooth(method='lm',color='green',linetype=2) +   # 绘图函数(拟合曲线)(3)
  labs(title='ggplot2', x='Weight', y='MPG') +    # 添加注释信息(标题)(4)
  theme(plot.title = element_text(hjust = 0.5))    # 调整标题位置   (5)

现在我们来一步步解析上面的代码:
(1)初始化图形并指定要用到的数据来源(mtcars)和变量(wt、mpg)。aes()函数指定每个变量扮演的角色,这里的wt映射到x轴数值,mpg映射到y轴数值。当需要对数据进行分类时,还可将分类因子映射到图形性状或颜色上面;
(2)绘图函数(也称几何函数)使用一个或几个函数向图中添加几何对象(简写为geom),包括点、线、条、箱线图和阴影区域,这里向图中添加了散点图以及一条拟合曲线(代码3)。当需要在一个图中使用多个数据来源进行画图时,可以单独在每个几何函数中指定数据源;
(4)labs()函数是可选的,用来添加注释(包括轴标签和标题信息);
(5)theme()函数用来设置全局配置,这里设置了标题的位置为中间;


除了上面的一些基本设置外,ggplot2还提供了分组和小面化(faceting)以及图形组合的方法,这些较为高级的方法将在下面进行介绍。

2、几何函数

ggplot()函数可用来指定要绘制的数据源和变量,也可不在ggplot()中设置而在每个几何函数中单独设置所用的数据源及变量,几何函数则指定了这些变量如何在视觉上进行展示(点、线、条、箱线图等)。使用命令ls(pattern = '^geom_', env = as.environment('package:ggplot2'))可以查看所有可用的几何函数,常用的几何函数如下表:

函数 图形 常用选项
geom_bar() 条形图 color、fill、alpha
geom_boxplot() 箱线图 color、fill、alpha、notch、width
geom_density() 密度图 color、fill、alpha、linetype
geom_hist0gram() 直方图 color、fill、alpha、linetype、binwidth
geom_hline() 水平线 color、alpha、linetype、size
geom_jitter() 抖动图 color、size、alpha、shape
geom_line() 线图 colorvalpha、linetype、size
geom_point() 散点图 color、alpha、shape、size
geom_rug() 地毯图(轴须图) color、size
geom_smooth() 拟合曲线 method、formula、color、fill、linetype、size
geom_text() 文字注解 很多,参加函数帮助
geom_violin() 小提琴图 color、fill、alpha、linetype
geom_vline() 垂线 color、alpha、linetype、size

对于几何函数中每个选项的意义,详见下表:

选项 详述
color 对点、线和填充区域的边界进行着色
fill 对填充区域着色,如条形和密度区域
alpha 颜色的透明度,从0(完全透明)到1(不透明)
linetype 线条类型,1=实线,2=虚线,3=点,4=破折号,5=长破折号,6=双破折号
size 点的尺寸和线的宽度
shape 点的性状(和pch一样,0=方形,1=圆形,2=三角形,等等)
position 绘制诸如条形图和点等对象的位置。对于条形图来说,‘dodge’将分组条形图并排,‘stacked’则为堆叠条形图,‘fill’标准化堆叠条形图。对于点来说,‘jitter’抖动减少点重叠
binwidth 直方图的宽度
notch 表示方块图是否为缺口(TRUE、FALSE),常见于箱线图
sides 地毯图的安置('b'=底部,'l'=左部,'t'=顶部,'r'=右部,'bl'=左下部,等等)
width 箱线图的宽度
library(ggplot2)
mtcars$am <- factor(mtcars$am, levels=c(0,1), labels=c('Automatic', 'Manual'))
ggplot() +
  geom_point(data=mtcars, aes(x=wt,y=mpg,shape=am, color=am),size=2) +  # 性状和颜色都使用am进行分组
  geom_smooth(data=mtcars, aes(x=wt,y=mpg),method='lm',color='green',linetype=2) +
  labs(title='ggplot2', x='Weight', y='MPG') +
  theme(plot.title = element_text(hjust = 0.5))

下图是对例子的一个改进,在上面的代码中可以看到,我们将数据分别放在了各个几何函数中,之所以这么做是为了对所有的数据进行拟合曲线,若将数据源一起放在ggplot()中进行设置的话,后面进行数据拟合的话会分别对am的分组数据进行拟合。


下面再看一个箱线图的简单例子。

> library(ggplot2)
> mtcars$am <- factor(mtcars$am, levels=c(0,1), labels=c('Automatic', 'Manual'))
> mtcars$cyl <- factor(mtcars$cyl, levels=c(4,6,8), labels=c('cyl-4', 'cyl-6','cyl-8'))
> ggplot(data=mtcars, aes(x=cyl, y=mpg, fill=am)) +  # 使用am填充分组
+     geom_boxplot() +
+     labs(title='ggplot2', x='cyl', y='MPG') +
+     theme(plot.title = element_text(hjust = 0.5))

在这里我们使用箱线图展示了cyl和mpg的关系,并使用am进行了二次分组,分组方式为填充颜色表示(fill=am)。


最后我们再来看一个使用了多个几何函数的图形,该图形使用的是Salaries数据集。

> data(Salaries,package='car')
> ggplot(Salaries, aes(x=rank, y=salary)) +
+     geom_boxplot(fill='cornflowerblue', color='black',notch=TRUE) +  # 添加箱线图
+     geom_point(position='jitter',color='blue',alpha=0.5) +   # 添加打散(jitter)了的散点图
+     geom_rug(sides='l',color='green')  # 在左侧添加轴须图
+     labs(title='ggplot2')  # 添加标题,默认添加在左上方

3、分组

通常为了更好地理解数据,在一个图中画出两个或更多组的观察值通常都是很有帮助的。ggplot2中的分组是通过一个或多个带有诸如性状、颜色、填充、尺寸和线条类型的视觉特征的分组变量来完成的。ggplot2中的aes()函数负责分配变量(图形的视觉特征),所以这是一个分配分组变量的自然地方。
这里我们将继续使用Salaries数据集,首先来查看下薪水是如何随学术等级而变化的。代码:

> ggplot(data=Salaries, aes(x=salary, fill=rank)) +  # 根据学术等级rank进行分类
+     geom_density(alpha=0.3)   # 添加密度图

从图中可以看到不同学术等级的薪水虽然有重叠,但仍能看到随着学术水平的增加薪水提高了。接下来,我们通过性别和学术等级分组,绘制获得博士学位年数与薪水的关系,代码:

> ggplot(Salaries, aes(x=yrs.since.phd, y=salary, color=rank, shape=sex)) +  
                      # 这里使用了两个分组变量sex和rank,在图中分别通过不同的颜色和不同的形状表示
+     geom_point()


最后,选项可以通过不同的方式使用,这取决于他们发生在aes()函数内部还是外部。让我们比较下面的一段代码绘制的图形:

> p1<-ggplot(Salaries, aes(x=rank, fill=sex)) + geom_bar()
> p2<-ggplot(Salaries, aes(x=rank)) + geom_bar()
> p3<-ggplot(Salaries, aes(x=rank,fill='red')) + geom_bar()
> p4<-ggplot(Salaries, aes(x=rank)) + geom_bar(fill='red')
> library(gridExtra)
> grid.arrange(p1,p2,p3,p4,ncol=4)  # 组合图形

在第一个图(p1)中,sex变量通过条形图中的填充颜色来展示。在第二个图(p2)中,没有设置分组变量也没有设置填充颜色,只绘制了各个rank的数量。第三个图(p3)中,ggplot2假定‘red’是变量名,结果得到的结果与图2基本相同。第四个图(p4),使用红色来填充图形。

4、刻面

如果组在图中并排出现而不是重叠为单一的图形,关系就是清晰的。我们可以使用facet_wrap()facet_grid()函数创建网格图形(也称刻面图)。这些函数的使用方法如下,其中var,rowvar,colvar是因子

语法 结果
facet_wrap(~var, ncol=n) 将每个var水平排列成n列的独立图
facet_wrap(~var, nrow=n) 将每个var水平排列成n行的独立图
facet_grid(rowvar~colvar) rowvar和colvar组合的独立图,其中rowvar表示行,colvar表示列
facet_grid(rowvar~.) 每个rowvar水平的独立图,配置成一个单列
facet_grid(.~colvar) 每个colvar水平的独立图,配置成一个单行

接下来我们使用mtcars数据,以am和cyl作为分组变量对mpg进行分组绘制直方图。

library(ggplot2)
mtcars$am <- factor(mtcars$am, levels=c(0,1), labels=c('Automatic', 'Manual'))
mtcars$cyl <- factor(mtcars$cyl, levels=c(4,6,8), labels=c('cyl-4', 'cyl-6','cyl-8'))

ggplot(data=mtcars, aes(x=mpg)) + 
  geom_histogram(fill='green') +
  facet_wrap(~cyl, nrow=1) # 使用cyl作为分组变量将mpg分组绘制
library(ggplot2)
mtcars$am <- factor(mtcars$am, levels=c(0,1), labels=c('Automatic', 'Manual'))
mtcars$cyl <- factor(mtcars$cyl, levels=c(4,6,8), labels=c('cyl-4', 'cyl-6','cyl-8'))

ggplot(data=mtcars, aes(x=mpg)) + 
  geom_histogram(fill='green') +
  facet_grid(am~cyl)   # 同时使用am和cyl作为分组变量,交叉分组

5、拟合曲线

选项 描述
method 使用的平滑函数。可选的值包括lm, glm, smooth,rlm和gam,分别对应线性,广义线性,loess,健壮线性和广义相加模型。默认为smooth
formula 在光滑曲线中使用的公式,例如y~x ,y~log(x) ,y~poly(x, n)
se 绘制置信区间。默认为TRUE
level 使用的置信区间水平(默认为95%)
fullrange 指定拟合应涵盖全图(TRUE)或仅仅是数据(FALSE),默认为FALSE

本部分我们将分析一下添加平滑曲线(线性,非线性和非参数)到散点图中的方法。我们可以使用geom_smooth()函数来添加一系列平滑曲线和置信区域。函数的参数见下表:

选项 描述
method 使用的平滑函数。可选的值包括lm, glm, smooth,rlm和gam,分别对应线性,广义线性,loess,健壮线性和广义相加模型。默认为smooth
formula 在光滑曲线中使用的公式,例如y~x ,y~log(x) ,y~poly(x, n)
se 绘制置信区间。默认为TRUE
level 使用的置信区间水平(默认为95%)
fullrange 指定拟合应涵盖全图(TRUE)或仅仅是数据(FALSE),默认为FALSE

这里使用Salaries数据集,我们先检验博士毕业年数和薪水之间的关系。在这个例子中,我们使用带有95%置信区间的非参数光滑曲线(loess)。暂时忽略学术等级和性别,代码:

> data(Salaries, package='car')
> library(ggplot2)
> ggplot(data=Salaries, aes(x=yrs.since.phd, y=salary)) +
+     geom_point() + 
+     geom_smooth(color='red')

图形显示,随着博士毕业年数的增加,薪水先增加后来又会降低。
接下来,我们按性别拟合一个二次多项式回归(一个弯曲):

> ggplot(data=Salaries, aes(x=yrs.since.phd, y=salary, linetype=sex, color=sex, shape=sex)) + 
+     geom_smooth(method=lm, formula=y~poly(x,2),se=FALSE,size=1) +
+     geom_point(size=2)

正如前面第一部分我们说的那样,当使用全局的数据源(在ggplot中设置)时,我们会拟合出分类数条曲线。这里我们分别对女性和男性的薪水随博士毕业年限的变化拟合了两条曲线。

6、修改ggplot2图形的外观

在R基本绘图中,我们可以使用par()函数定义全局参数。在ggplot2中提供了特定的函数来改变图形外观。

6.1 坐标轴

ggplot2在创建图形时会自动创建刻度线、刻度标记和坐标轴标签,有时我们可能想自定义这些图形。前面我们已经看到了labs()函数可以用来添加标题和轴标题,本节中我们将自定义轴标签,自定义轴标签的函数如下:

函数 选项
scale_x_continous()、scale_y_continous breaks=指定刻度标记,labels=指定刻度标记标签,limits=控制要展示的值得范围
scale_x_discret()和scale_y_discret() breaks=对因子的水平进行放置和排序,labels=指定这些水平的标签,limits=表示那些水平应该展示
coord_flip() 颠倒X轴和Y轴

可以看到,ggplot2的函数区分x轴和y轴,以及轴线是否代表一个连续或离散变量(因子)。
下面我们将这些函数应用到一个分组箱线图中,其中包含按学术等级和性别分组的薪资水平,代码:

> ggplot(data=Salaries, aes(x=rank, y=salary, fill=sex)) +
+        geom_boxplot() +
+         scale_x_discrete(breaks=c('AsstProf', 'AssocProf', 'Prof'), 
                           labels=c('Assistant\nProfessor', 'Associate\nProfessor', 'Full\nProfessor')) + 
+         scale_y_continuous(breaks=c(50000, 100000, 150000, 200000), 
                             labels=c('$50K','$100K','$150K','$200K')) +
+         labs(title='Faculty Salary by Rank and Sex', x='rank',y='salary')

可以很明显的看到,平均收入随着学术排名的上升而上升,在每个学术等级中男性的薪资水平高于女性。

6.2 图例

图例是指如何用颜色、形状尺寸等视觉特性表示数据特征的指南。ggplot2包能自动生成图例,而且很多时候能满足我们的需求;但在某些时候,我们可能需要对其进行自定义。标题和位置是最常用的定制特征。
当更改图例的标题时,必须考虑图例是否基于颜色、尺寸、形状或他们的组合。在6.1节中,图例代表fill,因此我们可以通过将fill='mytitle'加到labs()函数中来改变标题。标题的位置由theme()函数中的legend.position选项控制。可能的值包括'left'、'right'(默认)、'top'和'bottom'。我们也可以在图中给定位置指定一个二元向量。下面我们对6.1节中的图进行更改,增加一个分类因子color=discipline,并改变图例的标题和位置。

> ggplot(data=Salaries, aes(x=rank, y=salary, fill=sex, color=discipline)) +
+        geom_boxplot() +
+         scale_x_discrete(breaks=c('AsstProf', 'AssocProf', 'Prof'), 
                           labels=c('Assistant\nProfessor', 'Associate\nProfessor', 'Full\nProfessor')) + 
+         scale_y_continuous(breaks=c(50000, 100000, 150000, 200000),   
                             labels=c('$50K','$100K','$150K','$200K')) +
+         labs(title='Faculty Salary by Rank and Sex', x='rank',y='salary',color='Discip', fill='Gender') +
          ### labs()中的color='Discip'和fill='Gender'分别表示color和fill两个分类的图例的名称
+         theme(legend.position=c(0.1,0.8))    # 设置图例位置

本例中c(0.1,0.8)分别表示距离左侧边缘10%和底部边缘80%的部分。如果想删除图例则可以使用legend.position='none'

6.3 标尺

ggplot2包使用标尺把数据映射到可视化的空间中。标尺既可以应用到连续的变量,也可以应用到离散的变量。下面我们使用mtcars数据集,将disp变量映射到图形中点的大小,代码:

> ggplot(mtcars, aes(x=wt, y=mpg, size=gear)) +   # gear映射到散点的大小
+          geom_point(shape=21, color='black', fill='cornsilk') +
+          labs(x='Weight', y='Miles Per Gallon', title='Bubble Chart', size='Gear')

下面代码我们继续使用mtcars数据集,将cyl映射到图形颜色上(将颜色作为不同等级的标尺),并通过scale_color_manual()函数来手动设定三个cyl的点的颜色。

> mtcars$cyl <- factor(mtcars$cyl, levels=c(4,6,8), labels=c('cyl-4', 'cyl-6','cyl-8'))
> ggplot(mtcars, aes(x=wt, y=mpg, color=cyl)) +
+          geom_point(shape=21) +
+          labs(x='Weight', y='Miles Per Gallon') +
+         scale_color_manual(values=c('red','blue','green'))

此外,如果不想自己设置每一种颜色,可通过scale_color_brewer()scale_fill_brewer()函数来指定颜色集。

6.4 主题

theme()函数中的选项可以让我们调整字体、北京、颜色和网格线等。主题可以使用一次,也可以保存起来应用到多个图中。

library(ggplot2)
data(Salaries, package='car')

mytheme<-theme(plot.title=element_text(face='bold.italic',size=14,color='brown',hjust=0.5),  # 指定图标题的字体以及位置
          axis.title=element_text(face='bold.italic',size=10,color='brown'),  # 设置轴标题的字体大小等
          axis.text=element_text(face='bold.italic',size=9,color='darkblue'),  # 轴标签字体设置
          panel.background=element_rect(fill='white',color='darkblue'),  # 背景设置
          panel.grid.major.y=elemnt_line(color='grey',linetype=1),  # 设置网格线
          panel.grid.minor.y=element_line(color='grey', linetype=2), # 设置网格线
          panel.grid.minor.x=element_blank())  # 垂直网格不输出

ggplot(Salaries, aes(x=rank,y=salary,fill=sex)) + 
  geom_boxplot() +
  labs(title='Salary by Rank and Sex',x='RANK',y='Salary') +
  mytheme

7、组合图

在基础绘图中,我们使用par()函数的mfrow和mfcol参数和layout()来将多个图形组合起来。将多个ggplot图形组在一起最简单的方式是使用gridExtra包中的grid.arrange()函数,使用前需先安装这个包。
下面我们将三个ggplot图形合并到一个图中,代码:

> p1<-ggplot(data=Salaries, aes=(x=rank)) + geom_bar()
> p1<-ggplot(data=Salaries, aes(x=rank)) + geom_bar()
> p2<-ggplot(data=Salaries, aes(x=sex)) + geom_bar()
> p3<-ggplot(data=Salaries, aes(x=yrs.since.phd, y=salary)) + geom_point()
> library(gridExtra)
> grid.arrange(p1, p2, p3)
> grid.arrange(p1, p2, p3, ncol=3)

8、保存图形

保存图形能使用基础绘图的方法,但ggsave()函数能更为方便地保存它。它的选项包括保存那幅图形,保存位置以及保存形式。

myplot<-ggplot(data=mtcars, aes(x=mpg)) + geom_histogram()
ggsave(file='filename.tiff', plot=myplot, width=5, height=4)

我们可以通过将文件名的扩展格式设置为不同的格式来以不同的方式保存。如果忽略plot=选项,最近创建的图形会被保存。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容