(数据科学学习手札37)ggplot2基本绘图语法介绍
一、简介
ggplot2是R语言中四大著名绘图框架之一,且因为其极高的参数设置自由度和图像的美学感,即使其绘图速度不是很快,但丝毫不影响其成为R中最受欢迎的绘图框架;ggplot2的作者是现任Rstudio首席科学家的Hadley Wickham,ggplot2基于Leland Wilkinson在Grammar of Graphics(图形的语法)中提出的理论,取首字母缩写再加上plot,于是得名ggplot,末尾的2是因为Hadley写包的一个习惯——对先前的版本不满意便写一个新版本的名称不变仅在末尾加上2,如reshape2等;
按照《图形的语法》一书中的观点,一张统计图形就是从数据到点、线或方块等几何对象的颜色、形状或大小等图形属性的一个映射,其中还可能包含对数据进行统计变换(如求均值或方差),最后将这个映射绘制在一定的坐标系中就得到了我们需要的图形。图中可能还有分组,就是生成关于数据的不同子集的图形。使用ggplot2绘图的过程就是选择合适的几何对象、图形属性和统计变换来充分暴露数据中所含有的信息的过程;
因为ggplot2绘图语法风格的迥然不同,使得其学习成本比其他绘图包(包括基础绘图框架)要高不少,在刚开始上手的时候可能稍有难度(而且官网的帮助内容比较不友好),而本文也是我在日常使用和与别人交流中摸索和总结出来的,将对ggplot2的绘图语法和绘图部件进行介绍,并附以常用的一些图形示例;
下面我们就来探索ggplot2的神奇之处~
二、从qplot开始
2.1 基础图形
用惯了基础的绘图函数之后,突然转到ggplot2的绘图风格,或多或少会有些摸不着头脑,因此我们先从ggplot2中的qplot方法开始,这是一种语法规则和参数设置介于常规plot与ggplot2之间的一种绘图函数;
与plot相似,qplot()的基本参数是x、y,分别代表所要绘制图像的x轴与y轴,并且为了和数据框高度契合(我也十分鼓励将变量都放进数据框中规整起来),qplot还提供了参数data,控制传入的数据框名称,这样在qplot()中涉及数据框中变量的参数就可以直呼其名而不用加$;
在介绍qplot能够绘制的其他几何图像之前,我们先来理解一下其默认的模式——绘制散点图,以ggplot2中自带数据集diamonds作为示例,这是一个关于50000多颗圆切钻石各个指标的数据集,变量说明如下:
变量名 | 变量说明 |
price | 钻石价格 |
carat | 钻石重量 |
cut | 钻石切削水平 |
color | 钻石颜色 |
clarity | 钻石的透明度 |
x | 钻石长度 |
y | 钻石宽度 |
z | 钻石高度 |
depth | 深度百分比 |
table | 钻石正上顶点距离最宽顶点距离 |
我们以探究钻石重量carat与其对应价格price间关系为目的作图:
library(ggplot2) data <- diamonds qplot(carat, price, data=data)
可以看出,qplot的默认图像类型是散点图,我们还可以对qplot中的数据参数传入一些函数或计算式的:
qplot(log(carat), log(price), data=data)
qplot(carat, x*y*z, data=data)
同样的,我们也可以对图中的散点设置颜色、大小、形状等参数,与plot不同的是,qplot中可以使用更加丰富的内容和更自由的赋参方法,我们可以传入类别型数据,qplot会自动将其识别并分配对应到不同的颜色和不同的尺寸:
qplot(carat, price, data=data, colour=color)
qplot(carat, price, data=data, shape=cut)
而对于diamonds这种数量稍显庞大的数据集,很多点在画板上被重叠到一起,因此并不能正确的体现数据的情况,好在qplot中提供了控制散点透明度的参数alpha,通常会传入I(分数)形式的参数代表基础图形的透明度,在散点图形重叠的地方会进行透明度的无损累加使得其颜色变得很深:
qplot(carat ,price, data=data, alpha=I(1/20))
2.2 更多几何图像
上述的散点图只是qplot中的参数geom的默认参数point(当x与y都有传入值时的默认值,只有x传入时是hist图),这个参数用来控制图形类型,值得一提的是,他几乎涵盖了所有的图像类型,例如:
2.2.1 拟合曲线
当geom='smooth'时,将会拟合出一条平滑的曲线以及它的置信区间范围:
qplot(carat, price,data=data,geom='smooth')
如果你希望散点和拟合图共存时,可在geom中传入向量形式来组合各个图层,这也是ggplot2的绘图思想的一个体现,以叠加绘图元素的形式绘制一幅图像:
qplot(carat, price, data=data, geom=c('point','smooth'))
若不想绘制置信区间,则可以设置se=FALSE:
而关于拟合曲线的形式,可以通过method参数自行确定,比如我们希望拟合出一条线性曲线,则可以传入method='lm':
qplot(carat, price, data=data, geom=c('point','smooth'), method='lm')
也可以与其他包联动起来,如在加载MASS包的情况下,传入method='rlm',便可以以一种对异常值不敏感的拟合方式绘制拟合直线:
library(MASS) qplot(carat, price, data=data, geom=c('point','smooth'), method='rlm')
2.2.2 箱线图
箱线图作为一种经典的统计图像,它以数据的五数概括作为特征对数据进行可视化,在qplot中,当传入x为类别型变量,y为数值型变量时,通过传入geom='boxplot',可以绘制出分组箱线图,例如下面绘制钻石颜色color与每颗钻石每克拉价格price/carat的分组箱线图:
qplot(color, price/carat, data=data, geom='boxplot')
我们还可以通过传入参数colour来控制点与箱线边框的颜色、通过传入参数fill来控制箱线图填充的颜色:
qplot(color, price/carat, data=data, geom='boxplot', alpha=I(1/5), colour=color, fill=color)
2.2.3 扰动点图
仅通过箱线图可能只能了解到五数概括的情况,而想要在类似的图像结构中看出所有点的分布情况,可以选择扰动点图;
我们传入geom='jitter'来绘制扰动点图,这里我们增加透明度参数以更加真实地看出样本点在各分组内的分布情况:
qplot(color, price/carat, data=data, geom='jitter', alpha=I(1/5))
我们还可以通过传入colour参数来控制点的颜色:
qplot(color, price/carat, data=data, geom='jitter', alpha=I(1/5), color=color)
2.2.4 直方图
通过传入geom='histogram'可以来绘制直方图,并利用参数binwidth来控制组距:
qplot(carat, data=data, geom='histogram', binwidth=1)
qplot(carat, data=data, geom='histogram', binwidth=0.01)
通过传入参数fill来在一幅图上绘制分组后的重叠的直方图:
qplot(carat, data=data, geom='histogram', fill=color)
2.2.5 密度直线图
通过传入geom='density'来绘制密度直线图:
qplot(carat, data=data, geom='density')
传入colour来绘制分组的多条密度直线图:
qplot(carat, data=data, geom='density', colour=color)
传入fill来控制每个曲线下的填充颜色:
2.2.6 条形图
设置geom='bar'可以绘制条形图,当传入单个离散类别型数据时,可以自动绘制每个类别的频数统计条形图:
qplot(color, data=data, geom='bar', fill=color)
2.2.7 时间序列图
通过设置geom='line'可绘制线型图,当传入x为时间型数据时,即绘制出时间序列图:
data("economics") data <- economics qplot(date, unemploy/pop, data=data, geom='line')
2.2.8 路径图
有时候我们关注的是某些变量之间的对应变化情况,这种时候路径图就可以实现,通过设置geom='path':
qplot(unemploy/pop, uempmed,data=data, geom=c('point','path'))
2.2.9 分面
有时候,我们希望绘制的不是同样样式的不同分组图像在一幅图上反复绘制的情况,,而是希望根据分组产生一页多图的形式,通过设置参数facets=sep_var~.可以实现,其中sep_var为分组依据的变量,例如下面我们以钻石颜色为分组依据:
qplot(price,data=data, geom='density', facets = color~., colour=color)
2.2.10 其他参数
除了上面特殊的一些图像外,qplot中还有很多基本的参数,如:
xlim,ylim:设置x轴与y轴的显示区间
log:传入字符型,用于控制将哪个轴转成对数轴,'x'和'y'分别代表x轴与y轴,'xy'代表两个轴都进行变化
main:设置图形的主标题
xlab,ylab:设置x轴与y轴的名称
三、ggplot2的图形图层语法
图形图层语法是ggplot2的语法基础,它使得图形的重复更新变得更简单灵活,在遇到新问题时也许只需要照搬之前堆砌成的一个优美图形全部代码再稍加修改即可直接使用,下面我们就对ggplot2的语法规则进行探索:
3.1 ggplot2的绘图过程
我们先来看一下ggplot2的绘图过程:
仅根据上面的图,你心中一定很是疑惑,没关系,请你先短暂浏览上面这个过程,下面我们对这个过程进行一系列拆分,再回过头来理解这个过程(接下来会连续使用到mpg数据集);
3.1.1 图层
图层,就是生成在基础图床上的一种图形,它表现了信息的一种特点,例如:
library(ggplot2) data(mpg) data <- mpg qplot(displ, hwy, data=data)
这里,我们使用的图层是散点层,也就是图中的散点,目前为止它是我们这幅图的第一层图层,接下来,我们再添加上一层图层:
qplot(displ, hwy, data=data)+
geom_smooth()
心细的你一定发现了,我们这里不同于前面传入geom=c()的形式定义多个图层,而是在先前函数的基础上,+geom_smooth(),实现了图层的叠加,类似的,我们还可以叠加更多图层,虽然这看起来毫无意义。。。但请记住这种用法,这是叠加图层的基础;
qplot(displ, hwy, data=data)+ geom_smooth()+ geom_line()
3.1.2 标度
标度控制数据到图形属性的映射,每一个属性都需要由标度x,y来驱动,才能实现从指定数据——指定图层的映射,对应的,colour,shape等参数,也是由标度进行控制,再映射到对应图层上的对应样式颜色的变换,而ggplot2有一个特性,当传入的属性值非正常输入时,譬如colour中输入的是data中某列类别型变量时,整个绘图过程不会有异常,因为ggplot2内部非常“宽容”地对类别型变量进行了标度转换,如下例:
qplot(displ, hwy, data=data, colour=drv)+ geom_smooth()+ geom_line()
drv是一列字符型的数据,有f、r、4三种类型,坦白的说,若不是在这里进行绘图,你很难将他们与颜色联系在一起,但是这里他们的的确确被转换为常规的颜色,换成shape也是一样:
qplot(displ, hwy, data=data, shape=drv)
3.1.3 坐标系
坐标系即coord,可将对象的位置映射到图形平面上,ggplot2中绘制的通常为2D图像,即图像的位置信息由(x,y)决定,且通常为笛卡尔坐标系,用得较少的是极坐标系和各种地图坐标系;
坐标系最大的特点是,它可以同时影响所有的位置变量,譬如说,条形图在笛卡尔坐标系中是规规矩矩的条形,但在极坐标系中,条形就变成了一个个扇形,据此可以构造南丁格尔玫瑰图,如下例:
这是笛卡尔坐标系下的柱形图:
qplot(cyl,data=data, geom='bar', fill=factor(cyl))
我们将其坐标轴更新为极坐标系,语法规则也比较明了,就是coord_polor():
qplot(cyl,data=data, geom='bar', fill=factor(cyl))+ coord_polar()
3.1.4 分面
分面前面也说过,是通过数据框中的某个特征为依据,构建不同的分块图形来展示数据的不同子集:
qplot(displ, hwy, data=data, facets = .~cyl, colour=factor(cyl))
3.1.5 数据结构
ggplot2通过其特殊的图形语法,将整个图形相关元素编码到R的列表数据结构中,而一个完整的图形对象就是一个由数据、映射、图层、标度、坐标和分面组成的列表:
> p <- qplot(displ, hwy, data=data, + facets = .~cyl, + colour=factor(cyl)) > > summary(p) data: manufacturer, model, displ, year, cyl, trans, drv, cty, hwy, fl, class [234x11] mapping: colour = factor(cyl), x = displ, y = hwy faceting: <ggproto object: Class FacetGrid, Facet> compute_layout: function draw_back: function draw_front: function draw_labels: function draw_panels: function finish_data: function init_scales: function map: function map_data: function params: list render_back: function render_front: function render_panels: function setup_data: function setup_params: function shrink: TRUE train: function train_positions: function train_scales: function super: <ggproto object: Class FacetGrid, Facet> ----------------------------------- geom_point: na.rm = FALSE stat_identity: na.rm = FALSE position_identity
而对于ggplot2中的绘图,有两种方式:一是在qplot中一步到位配置好所有的参数以产出所需的图像;另一种是利用ggplot逐层定义绘图部件,并用加号连接,保存到一个对象里,再使用print这个对象的方法将其呈现在屏幕上,或是用ggsave函数将图像文件按照设置的尺寸保存在外存里,用summary查看其数据结构,
3.2 通过ggplot()用图层来构建图像
前面我们依次介绍了ggplot2图层语法中的各种主要结构,但仅使用了qplot()进行绘图,其局限性是只能使用在qplot()中定义的一个数据集和对应的一组图形属性映射,若希望将不同的数据通过不同的图层构建方式来展现在一张图上,就需要使用ggplot()函数,该函数有两个主要的参数,对应了数据和图形属性映射,这两个参数将作为接下来绘图的默认参数,直到在新加的图层中设定了新的参数,默认值才会被修改‘;其中,数据指定绘图所使用的默认数据框且必须是数据框;映射的设定则与qplot非常相似,只需要将图形属性和变量名放到函数aes()内即可,但要注意,这里不像qplot默认的图层为散点图,使用ggplot时如果不+geom_部分,则没有图层会被创建,例如:
library(ggplot2) data(mpg) data <- mpg #未加上图层部件 p <- ggplot(data=data, aes(displ, hwy)) print(p)
#加上图层部件 p <- ggplot(data=data, aes(displ, hwy))+ geom_point() print(p)
3.2.1 图层
在定义了基础的ggplot()后,我们通过+图层函数的方式添加图层,这里只介绍快捷函数的方式,其主要形式为geom_XXX(mapping,...,stat,position),其中mapping是可选的图形属性映射,若想要在图层中展现新的数据和参数,则可以在geom_XXX()中指明aes()形式的mapping即可:
p <- ggplot(data=data, aes(displ, hwy))+ geom_point(mapping = aes(shape=factor(cyl)))+ geom_smooth(mapping = aes(displ,hwy,colour=factor(cyl))) print(p)
上面的示例中,我们在ggplot中创建了基础的数据映射之后,又接连添加了两个图层,第一个图层绘制出以因子转化后的cyl为shape的散点图,第二个图层绘制出以因子转化后的cyl为colour的光滑拟合曲线,这时summary我们的p也可以观察到分图层的各图层信息:
我们还可以使用更多的扩展包来丰富ggplot2图层内的参数:
library(scales) p <- ggplot(data,aes(displ,hwy))+ geom_smooth(method='lm', se=F, colour=alpha('steelblue',0.5), size=2)+ geom_point() print(p)
3.2.2 数据
ggplot2只接受数据框输入,而且,对于一个已经创建好的基于数据框1的绘图对象p,可以用p %+% 数据框2 的形式直接替代原来的数据集:
library(ggplot2) data <- mtcars p <- ggplot(data = data,aes(mpg,wt, colour=cyl))+ geom_point()+ labs(title='变换前')+ theme(plot.title = element_text(hjust = 0.5)) p #对原数据集数据内容进行改造 mtcars <- transform(mtcars, mpg = mpg^2) p %+% mtcars + labs(title='变换后')+ theme(plot.title = element_text(hjust = 0.5))
*这里labs用于修改标题,theme(plot.title=element_text(hjust=0.5))用于决定标题居中
要注意的是,转换后的数据集中若涉及连续型与离散型间的转换,就需要注意一下,因为有些图在这种情况会因为不兼容的原因而绘图失败。
3.2.3 关于aes映射需要注意的一些情况
1、aes中设置colour与aes外设置colour不同
在aes外面设置colour时,是正常的参数,没有强制标度转换的过程,如下:
p <- ggplot(data=data,aes(mpg,wt))+ geom_point(color='darkblue') p
而在aes内部设置colour时,它会将传入的代表颜色的字符型'darkblue'当成一个变量来看待,由于其为单个字符串,于是便被映射为色轮上的起点也即是红色:
p <- ggplot(data=data,aes(mpg,wt))+ geom_point(aes(colour='darkblue')) p
由此你可以看出,aes中的参数都是会依据变量类型进行标度转换的;
2、每次新图层中的数据都是在ggplot()中默认值的修改
在ggplot()中已经设置过aes(x,y)之后,后续图层则仅需要根据实际需求修改部分,比如我在新的图层中仅需要改变y,则只需要在该图层语句中aes(y=new_y)即可;
3.2.4 几何对象
所谓几何对象,简称geom,我们在前面也提到过,并多次使用过,它控制生成的图像类型;
3.2.5 位置调整
位置调整指的是对该层中的元素位置进行微调,ggplot2中所有可用的位置调整参数如下:
名称 | 描述 |
dodge | 禁止重叠,并排放置 |
fill | 堆叠元素并将高度放缩为1 |
identity | 不做任何调整(就像神经网络里的identity激活函数一样) |
jitter | 给点添加扰动避免重合 |
stack | 将图形元素堆叠起来 |
而上述这些位置参数通常是应用在条形图中,下面一一进行展示:
p <- ggplot(data,aes(am,fill=factor(cyl)))+ geom_bar(position = 'dodge')+ labs(title='并排放置时')+ theme(plot.title = element_text(hjust=0.5)) p
p <- ggplot(data,aes(am,fill=factor(cyl)))+ geom_bar(position = 'fill')+ labs(title='高度放缩为1时')+ theme(plot.title = element_text(hjust=0.5)) p
p <- ggplot(data,aes(factor(am),fill=factor(cyl)))+ geom_bar(position = 'identity')+ labs(title='不做任何处理时')+ theme(plot.title = element_text(hjust=0.5)) p
p <- ggplot(data,aes(factor(am),fill=factor(cyl)))+ geom_bar(position = 'jitter')+ labs(title='添加随机扰动时')+ theme(plot.title = element_text(hjust=0.5)) p
p <- ggplot(data,aes(factor(am),fill=factor(cyl)))+ geom_bar(position = 'stack')+ labs(title='堆叠时')+ theme(plot.title = element_text(hjust=0.5)) p
以上就是关于ggplot2的基本内容,如有笔误,望指出。