前言

ggplot2是R语言最流行的第三方扩展包,是RStudio首席科学家Hadley Wickham读博期间的作品,是R相比其他语言一个独领风骚的特点。包名中“gg”是grammar of graphics的简称,是一套优雅的绘图语法。Wickham Hadley将这套语法诠释如下:

一张统计图形就是从数据几何对象(geometric object,缩写geom)的图形属性(aesthetic attribute,缩写aes)的一个映射。此外,图形中还可能包含数据的统计变换(statistical transformation,缩写stats),最后绘制在某个特定的坐标系(coordinate system,缩写coord)中,而分面(facet)则可以用来生成数据不同子集的图形。

这个解释读起来还是有点抽象。我们举个具体的例子来解读这个概念。假设现在我们要对一批连续取值的数据绘制直方图。首先,要定义清楚需要几个分组或者每个分组的区间,根据分组定义统计落在这个分组里的个数,这个步骤就是把data变为stats。然后,需要选定表达数据的几何对象,这个例子选用的是条块bar,这个步骤就是选geomgeom有一堆属性需要设定,比如x、y、颜色等,称为aes,哪个aes由哪个stats指定,需要指定一个映射关系mapping,即指定谁对谁。知道谁对谁后,还需要知道怎么个对法,需要由scale决定,比如stats的color字段取值为1应该对到什么颜色上,取值为2应该对到什么颜色上。这些完成了以后,统计图形的主体部分就成形了,但是假如我们希望在直方图上,再画一个概率密度曲线图,怎么办?ggplot2的思想非常精妙,把上面的主体部分称为一个图层layer,一个统计图形可以拥有多个图层,每个图层叠加起来形成我们要的效果。接下来,再选定一个坐标系统coord,一张统计图形plot就做好了。假如我们有多组数据,每组数据都要按照相同的方法画一张图,每张图重复敲代码很繁琐,就可以使用分面facet快速绘制多张统计图形。这个过程用图形总结如下:

我们可以看到ggplot2相比其他绘图系统的几个特性:

  • 标准化:任何一个统计图形遵循相同的绘图流程,所以语法高度统一;

  • 面向数据:上面的绘图流程只与数据有关,与数据无关的绘图细节封装在单独的theme()方法里,数据相关绘图与数据无关绘图分离;

这两大特性解放了数据分析师的思维,做到绘图时所思即所见,非常优雅高效。下面我们来逐步剖析每个元素的内容。

数据

ggplot2接受的输入数据一般是data.frame,这是一个表格型结构,每一行是一个观测(observation),每一列是一个变量(variable)。R语言内置了许多著名的数据集,本文选取其中的iris进行讲解。iris中文名是鸢尾花,有四个属性,分别是Sepal.Length(花萼长度),Sepal.Width(花萼宽度),Petal.Length(花瓣长度),Petal.Width(花瓣宽度),以及一个类别标签Species。我在网上找了一个图片,做个标注,方便朋友理解。

我们可以使用str()查看数据集的结构,用summary()对每一个变量进行统计。

str(iris)

summary(iris)

Hadley对data.frame提出了一个是否tidy的概念,抽象来讲就是一个变量必须有自己独立的一列,一个观测必须有自己独立的一行,每个取值必须有自己独立的一个单元格。为了便于理解,我们从R for Data Science这本书截取出这个图进行解释:

左边的数据是tidy的,右边的数据是不tidy的,通过另一个包tidyr可以轻松完成二者的转换。ggplot2的数据要求是tidy的。

统计图形基本要素

几何对象geom

几何对象,说的直观一些,就是你选择什么几何图形来表示这组数据。ggplot2提供了众多几何对象geom_xyz()供大家选择。举两个常见的例子,geom_point()用于表示两个连续变量之间的关系,几何形状是点;geom_bar()用于表示x轴为离散变量,y轴为连续连续变量之间的关系,几何形状是条块。完整的几何对象请下载RStudio公司总结的ggplot2 cheetsheet

几何对象需要解决一个问题,即相同数据的几何对象位置相同,是放在一个位置相互覆盖还是用别的排列方式。ggplot2的几何对象有一个position选项,用于指定如何在空间内布置相同取值的集合对象。dodge为并排模式;fill为堆叠模式,并归一化为相同的高度;stack为纯粹的堆叠模式;jitter会在X和Y两个方向增加随机的扰动来防止对象之间的覆盖。

统计变换stats

在ggplot2里,几何对象与统计变换往往是一一对应的。每个统计变换需要通过一个几何对象来展现;每个几何对象的展现依赖统计变换的结果。举个简单例子,以下两行代码的效果是一样的:

ggplot(iris) + geom_bar(aes(x=Sepal.Length), stat="bin", binwidth = 3)
ggplot(iris) + stat_bin(aes(x=Sepal.Length), geom="bar", binwidth = 3)

图形属性aes

每个几何对象都有自己的属性,这些属性的取值需要通过数据提供。数据与图形属性之间的映射关系称为mapping,在ggplot2中用aes()进行定义。常见的图形属性有:xysizecolorgroup。图形属性的任意一项都可以用数据的某一个变量来表示。

标尺scale

前面提到aes()设定了数据与图形属性的映射关系,但是数据怎么映射为属性,这就是标尺(Scales)的功能。对于任何一个图形属性,如xyalphacolorfilllinetypeshapesize,ggplot2都提供以下四种标尺:

  • scale_*_continuous():将数据的连续取值映射为图形属性的取值

  • scale_*_discrete():将数据的离散取值映射为图形属性的取值

  • scale_*_identity():使用数据的值作为图形属性的取值

  • scale_*_mannual():将数据的离散取值作为手工指定的图形属性的取值

举个例子

group_iris <- iris %>% group_by(Species) %>% dplyr::summarise(avg_sepal_length=mean(Sepal.Length))
str(group_iris)
p <- ggplot(group_iris) + geom_bar(aes(x=Species, weight=avg_sepal_length, fill=Species))
p

p + scale_fill_manual(
values = c("skyblue", "royalblue", "navy"), # mannual类scale特有的选项,指定图形属性的取值范围
limits = c("setosa", "versicolor", "virginica"), # 数据的取值范围
breaks = c("setosa", "versicolor", "virginica"), # 图例和轴要显示的分段点
name = "Species", # 图例和轴使用的名称
labels = c("set", "ver", "vir") # 图例使用的标签
)

除了上述四大类通用的标尺,特定的图形属性还有一些专门的标尺类型。对于xy类图形属性,有如下几种特殊的标尺:

  • scale_x_date(labels=date_format("%m/%d"), breaks=date_breaks("2 weeks"))

  • scale_x_datetime()

  • scale_x_log10()

  • scale_x_reverse()

  • scale_x_sqrt()

对于colorfill类的图形属性,有如下几类特殊标尺:

  • scale_fill_brewer(palette="Blues"):根据调色盘生成颜色标尺,可用的调色盘可以通过RColorBrewer::display.brewer.all()命令查看;对于具体的一个调色盘,可以通过RColorBrewer::brewer.pal(n=4, name="Blues")查看具体某个名字调色盘的n个配色值。

  • `scale_fill_grey(start=0.2, end=0.8, na.value="red"):灰度标尺

  • scale_fill_gradient(low="red", high="yellow"):双色渐变标尺

  • scale_fill_gradient2(low="red", high="blue", mid="white", midpoint=25):三色渐变标尺

  • scale_fill_gradientn(colours=terrain.colors(6)):n色渐标尺,其他的调色盘有rainbow()heat.colors()topo.colors()cm.colors()以及RColorBrewer包的调色盘。

对于shape类的图形属性,我们可以手工指定形状:scale_shape_manual(values=c(3:7)。每个形状用数字表示,根据下图可以选择自己需要的形状。

图层layer

ggplot2的绘图过程有点像Photoshop,有一个图层的理念,每个图层可以有自己的图形对象和图形属性,通过+将不同图层叠加起来生成最后的统计图形。如果将数据定义在ggplot()中,那么所有图层都可以共用这个数据;如果将数据定义在geom_xyz()中,那么这个数据就只供这个几何对象使用。

坐标系coord

ggplot2默认的坐标系是笛卡尔坐标系,可以用如下方法指定取值范围:coord_cartesian(xlim=c(0,5), ylim=c(0,3))。如果想要让x轴和y轴换位置,比如将柱形图换成条形图,可以使用coord_flip()函数。coord_polar(theta="x", direction=1)是角度坐标系,theta指定角度对应的变量,start指定起点离12点钟方向的偏离值,direction若为1表示顺时针方向,若为-1表示逆时针方向。

掌握了数据、几何对象、图形属性、图层和坐标系的概念后,我们就可以开始绘制常见的统计图形了。

练习

Kaggle数据挖掘竞赛里有一个经典的探索性分析例子,对iris数据集进行了各种形式的可视化,帮助人通过直观的图形更深地理解特征与label的关系。Kaggle官网给出了Python版本的实现。本节用R对该notebook的代码进行重现。

library(ggplot2)

# Make scatter plot of Sepal.Length and Sepal.Width
p.scatter <- ggplot(iris) + geom_point(aes(x=Sepal.Length, y=Sepal.Width))
p.scatter

# One piece of information missing in the plots above is what species each plant is
p.scatter <- ggplot(iris) + geom_point(aes(x=Sepal.Length, y=Sepal.Width, color=Species))
p.scatter

# Boxplot to explore numeric variable
p.box <- ggplot(iris) + geom_boxplot(aes(x=Species, y=Petal.Length))
p.box

# One way we can extend this plot is adding a layer of individual points on top of it
p.box.jitter <- p.box + geom_jitter(aes(x=Species, y=Petal.Length))
p.box.jitter

# A violin plot combines the benefits of the previous two plots and simplifies them
# Denser regions of the data are fatter, and sparser thiner in a violin plot
p.violin <- ggplot(iris) + geom_violin(aes(x=Species, y=Petal.Length))
p.violin

# A final plot useful for looking at univariate relations is the kdeplot,
p.density <- ggplot(iris) + geom_density(aes(x=Petal.Length, colour=Species))
p.density

分面

分面,就是分组绘图,根据定义的规则,将数据分为多个子集,每个子集按照统一的规则单独制图,排布在一个页面上。ggplot2提供两种分面模式:facet_grid()facet_wrap()

我们先来看一下facet_grid()的效果。

library(tidyr)
library(dplyr)
# 将数据变为tidy的
tidy_iris <- iris %>%
gather(feature_name, feature_value, one_of(c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"))) p.box.facet <- ggplot(tidy_iris) + geom_boxplot(aes(x=Species, y=feature_value)) + facet_grid(feature_name~Species)
p.box.facet

可以看到facet_grid()是一个二维的矩形布局,每个子集的位置由行位置变量~列位置变量的决定,在上面的例子中就是每一个Species的取值作为一行,每一个feature_name的取值作为一列。

再来看一下facet_wrap()的效果。

p.box.facet <- ggplot(tidy_iris) + geom_boxplot(aes(x=Species, y=feature_value)) + facet_wrap(~feature_name+Species, scales="free")
p.box.facet

facet_wrap()生成一个动态调整的一维布局,根据~位置变量1+位置变量2+...来确定每个子集的位置,先逐行排列,放不下了移动到下一行。scales="free"让每个子图的坐标系适合自己的数据,便于在有限的空间里充分展示子图的细节,但也失去了不同子图之间比较的作用,需要谨慎使用。

题外话:复杂布局

分面的特点是可以快速生成多个子图,每个子图的生成方式是一样的,因此只需要指定分组的规则即可。但是有时候我们希望绘制多个子图,每个子图的生成方法却不一样,这个时候分面就不起作用了,需要使用grid包提供的布局功能。下面我们用ggplot2和grid的布局实现一个较为复杂的统计图形效果:

library(grid)
# Show bivariate scatter plot and univariate histogram
p.hist.len <- ggplot(iris) + geom_histogram(aes(x=Sepal.Length))
p.hist.wid <- ggplot(iris) + geom_histogram(aes(x=Sepal.Width)) + coord_flip()
grid.newpage()
pushViewport(viewport(layout = grid.layout(3, 3)))
print(p.scatter, vp=viewport(layout.pos.row=2:3, layout.pos.col=1:2))
print(p.hist.len, vp=viewport(layout.pos.row=1, layout.pos.col=1:2))
print(p.hist.wid, vp=viewport(layout.pos.row=2:3, layout.pos.col=3))

在做数据分析时,我们经常需要观察变量自身与变量之间的两两关系。这个过程中需要绘制大量的图表,且每个业务的数据分析都需要这么做,因此算是一种重复性比较大的工作。我们可以使用GGally包来快速完成这个探索性分析的任务。

library(GGally)
# Another useful seaborn plot is the pairplot, which shows the bivariate relation
# between each pair of features
#
# From the pairplot, we'll see that the Iris-setosa species is separataed from the other
# two across all feature combinations ggpairs(iris, aes(colour=Species), alpha=0.4) # R could be better!!

题外话:其他练习

Kaggle数据挖掘竞赛剩下的例子是绘制Parallel coordinate graph、Andrews Curve、radviz,前两个的实现参考如下,最后一个暂时没找到对应的方法。

# Parallel coordinate graph & Andrews Curve
# 修改自:http://cos.name/2009/03/parallel-coordinates-and-andrews-curve/
# 轮廓图的思想非常简单、直观,它是在横坐标上取n个点,依次表示各个指标(即变量);横坐标上则对应各个指标的值(或者经过标准化变换后的值),然后将每一组数据对应的点依次连接即可
# 调和曲线图的思想和傅立叶变换十分相似:
# 根据三角变换方法将 n 维空间的点映射到二维平面上的曲线上,其中x取值范围为[-pi,pi]。 # Another multivariate visualization technique pandas has is parallel_coordinates
# Parallel coordinates plots each feature on a separate column & then draws lines
# connecting the features for each data sample p.paral <- ggplot(cbind(iris %>% gather(feature_name, feature_value, one_of(c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"))), id=1:nrow(iris))) + geom_line(aes(x=feature_name, y=feature_value, group=id, colour=Species))
p.paral

# One cool more sophisticated technique pandas has available is called Andrews Curves
# Andrews Curves involve using attributes of samples as coefficients for Fourier series
# and then plotting these
andrews_curve <- function(data, x_col, y_col, step=pi/30){
x = as.matrix(data[, x_col])
t = seq(-pi, pi, pi/30)
m = nrow(x)
n = ncol(x)
f = matrix(0, m, length(t))
for(i in 1:m) {
f[i,] = x[i,1]/sqrt(2)
for(j in 2:n) {
if (j%%2 == 0)
f[i, ] = f[i, ] + x[i, j] * sin(j/2 * t)
else f[i, ] = f[i, ] + x[i, j] * cos(j%/%2 * t)
}
}
colnames(f) <- t
label <- data[, y_col]
id <- c(1:nrow(f))
res <- cbind(as.data.frame(f), label, id) %>%
gather(x, y, -label, -id, convert = TRUE)
}

# A final multivariate visualization technique pandas has is radviz
# Which puts each feature as a point on a 2D plane, and then simulates
# having each sample attached to those points through a spring weighted
# by the relative value for that feature # 暂时没能力实现

其他元素

主题

所有与数据不相关的图形控制细节都放在theme()这个函数里。ggplot2内置了一些常见的主题:theme_bw()theme_classic()theme_grey()theme_minimal()。如果需要更多的主题可以安装ggthemes包,也可以自定义主题。

图例

ggplot2可以设定图例的位置:theme(legend.position="bottom"),其他选项有top、left和right。

每个图形属性都会有一个图例,图例的类型共有三种:colorbar为颜色条,适合连续变量;legend为键值对,适合有限取值的变量;none,将一个图形属性的图例设置为none,则不显示这个图形属性的图例。

标签

常用的绘图标签有:

  • ggtitle("New Plot Title"):指定图形名称

  • xlab("New X label"):指定x轴标签

  • ylab("New Y label"):指定y轴标签

  • 图例标签需要使用scale_*()namelabels选项进行指定

文章转自:丹追兵,https://segmentfault.com/a/1190000006120665

ggplot2基础学习的更多相关文章

  1. salesforce 零基础学习(五十二)Trigger使用篇(二)

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

  2. 如何从零基础学习VR

    转载请声明转载地址:http://www.cnblogs.com/Rodolfo/,违者必究. 近期很多搞技术的朋友问我,如何步入VR的圈子?如何从零基础系统性的学习VR技术? 本人将于2017年1月 ...

  3. IOS基础学习-2: UIButton

    IOS基础学习-2: UIButton   UIButton是一个标准的UIControl控件,UIKit提供了一组控件:UISwitch开关.UIButton按钮.UISegmentedContro ...

  4. HTML5零基础学习Web前端需要知道哪些?

    HTML零基础学习Web前端网页制作,首先是要掌握一些常用标签的使用和他们的各个属性,常用的标签我总结了一下有以下这些: html:页面的根元素. head:页面的头部标签,是所有头部元素的容器. b ...

  5. python入门到精通[三]:基础学习(2)

    摘要:Python基础学习:列表.元组.字典.函数.序列化.正则.模块. 上一节学习了字符串.流程控制.文件及目录操作,这节介绍下列表.元组.字典.函数.序列化.正则.模块. 1.列表 python中 ...

  6. python入门到精通[二]:基础学习(1)

    摘要:Python基础学习: 注释.字符串操作.用户交互.流程控制.导入模块.文件操作.目录操作. 上一节讲了分别在windows下和linux下的环境配置,这节以linux为例学习基本语法.代码部分 ...

  7. CSS零基础学习笔记.

    酸菜记 之 CSS的零基础. 这篇是我自己从零基础学习CSS的笔记加理解总结归纳的,如有不对的地方,请留言指教, 学前了解: CSS中字母是不分大小写的; CSS文件可以使用在各种程序文件中(如:PH ...

  8. Yaf零基础学习总结5-Yaf类的自动加载

    Yaf零基础学习总结5-Yaf类的自动加载 框架的一个重要功能就是类的自动加载了,在第一个demo的时候我们就约定自己的项目的目录结构,框架就基于这个目录结构来自动加载需要的类文件. Yaf在自启动的 ...

  9. Yaf零基础学习总结4-Yaf的配置文件

    在上一节的hello yaf当中我们已经接触过了yaf的配置文件了, Yaf和用户共用一个配置空间, 也就是在Yaf_Application初始化时刻给出的配置文件中的配置. 作为区别, Yaf的配置 ...

随机推荐

  1. (最小生成树 次小生成树)The Unique MST -- POJ -- 1679

    链接: http://poj.org/problem?id=1679 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82831#probl ...

  2. java基础-day1

    第01天 java基础知识 今日内容介绍 u Java概述.helloworld案例 u 工具安装 .配置环境变量.注释.关键字 u 常量.变量.数据类型.标识符 第1章   Java概述 1.1  ...

  3. [ajax] quick double or multiple click ajax submit cause chrome explorer's error snatshot

    快速点击ajax提交,引发的错误截图1: snapshot -2:

  4. MySQL Route负载均衡与读写分离Docker环境使用

    Docker环境描述 主机名 部署服务 备注 MySQL Route MySQL Route 部署在宿主机上的MySQL Route服务 MySQL Master1 MySQL 5.7.16 Dock ...

  5. 解决由AJAX请求时forms认证实效的重新认证问题

    前言: 当用AJAX请求一个资源时,服务器检查到认证过期,会重新返回302,通过HTTP抓包,是看到请求了登录页面的,但是JS是不会进行跳转到登录页面. 使用环境: ASP.NET MVC 4 JQU ...

  6. SQLite 编译错误 - 试图加载格式不正确的程序

    刚开始用SQLite,刚开始写了一个小程序,连接数据库的时候出现了问题,提示试图加载格式不正确的程序, 原因是我当前工程的目标平台是X86,改成Any CPU之后问题就解决了.如下图:

  7. 删除 iptables nat 规则

    原文:https://www.cnblogs.com/hixiaowei/p/8954161.html 删除FORWARD 规则: iptables -nL FORWARD --line-number ...

  8. 对表单控制是否提交 需要在方法名前面加上 return

  9. 《Python绝技:运用Python成为顶级黑客》 用Python刺探网络

    1.使用Mechanize库上网: Mechanize库的Browser类允许我们对浏览器中的任何内容进行操作. #!/usr/bin/python #coding=utf-8 import mech ...

  10. Django + DRF + Elasticsearch 实现搜索功能

    django使用haystack来调用Elasticsearch搜索引擎  如何使用django来调用Elasticsearch实现全文的搜索 Haystack为Django提供了模块化的搜索.它的特 ...