R数据可视化手册

978-7-115-34227-0
作者: 【美】Winston Chang
译者: 肖楠邓一硕魏太云
编辑: 杨海玲

图书目录:

详情

R具有强大的统计计算功能和便捷的数据可视化系统。本书重点讲解R的绘图系统,指导读者通过绘图系统实现数据可视化。书中提供了快速绘制高质量图形的150多种技巧,每个技巧用来解决一个特定的绘图需求。读者可以通过目录快速定位到自己遇到的问题,查阅相应的解决方案。同时,作者在大部分的秘方之后会进行一些讨论和延伸,介绍一些总结出的绘图技巧。

图书摘要

版权信息

书名:R数据可视化手册

ISBN:978-7-115-34227-0

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [美] Winston Chang

  译    肖 楠  邓一硕  魏太云

  审  校 邱怡轩

  责任编辑 杨海玲

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


作者介绍

Winston Chang是Rstudio的软件工程师,致力于R中的数据可视化和软件开发工具的研发。他创立的网站“Cookbook for R”提供了R中常见问题的解决技巧。

译者介绍

肖楠,中南大学数学与统计学院统计学系在读博士,统计之都论坛R语言版版主。合作翻译出版了《R语言实战》、《ggplot2:数据分析与图形艺术》等图书,编写了protr、Rcpi等R软件包。关注领域为统计机器学习、化学信息学与生物信息学、定量与系统药理学。

邓一硕,毕业于中央财经大学统计与数学学院,统计之都论坛金融投资分析版版主,现效力于首钢总公司计财部。擅长的领域为时间序列分析以及数据挖掘在金融投资分析中的应用。

魏太云,毕业于中国人民大学统计学院,统计之都理事会主席。合作翻译出版了《ggplot2:数据分析与图形艺术》等图书,参与编写了corrplot、recharts、knitr以及fun等R软件包。感兴趣的主题包括统计建模、机器学习和数据可视化。

审校者介绍

邱怡轩,普渡大学统计系在读博士,统计之都理事会成员。合作翻译出版了《ggplot2:数据分析与图形艺术》、《R语言编程艺术》等图书,参与编写了R2SWF、rARPACK、showtext、Layer、rationalfun、fun等R软件包。感兴趣的方向有函数型数据分析、统计计算和数据可视化等。

本书特色

1.作者基于自己的R Cookbook网站的内容写成本书,有很好的实用性。

2.书提供了快速绘制高质量图形的150多个精选的技巧,读者不需要了解R绘图系统的全部细节便可以掌握这些技巧。

3.书中的大多数方法使用的是以强大、灵活制图而著称的ggplot2包。


Copyright ©2013 by O’Reilly Media, Inc.

Simplified Chinese Edition, jointly published by O’Reilly Media, Inc. and Posts & Telecom Press, 2014. Authorized translation of the English edition, 2013 O’Reilly Media, Inc., the owner of all rights to publish and sell the same.

All rights reserved including the rights of reproduction in whole or in part in any form.

本书中文简体版由O’Reilly Media, Inc.授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式复制或抄袭。

版权所有,侵权必究。


O’Reilly Media通过图书、杂志、在线服务、调查研究和会议等方式传播创新知识。自1978年 开始,O’Reilly一直都是前沿发展的见证者和推动者。超级极客们正在开创着未来,而我们关 注真正重要的技术趋势——通过放大那些“细微的信号”来刺激社会对新科技的应用。作为 技术社区中活跃的参与者,O’Reilly的发展充满了对创新的倡导、创造和发扬光大。

O’Reilly为软件开发人员带来革命性的“动物书”;创建第一个商业网站(GNN);组织了 影响深远的开放源代码峰会,以至于开源软件运动以此命名;创立了Make杂志,从而成为 DIY革命的主要先锋;公司一如既往地通过多种形式缔结信息与人的纽带。O’Reilly的会议和 峰会集聚了众多超级极客和高瞻远瞩的商业领袖,共同描绘出开创新产业的革命性思想。作 为技术人士获取信息的选择,O’Reilly现在还将先锋专家的知识传递给普通的计算机用户。无 论是通过书籍出版,在线服务或者面授课程,每一项O’Reilly的产品都反映了公司不可动摇的 理念——信息是激发创新的力量。

业界评论

“O’Reilly Radar博客有口皆碑。”

——Wired

“O’Reilly凭借一系列(真希望当初我也想到了)非凡想法建立了数百万美元的业务。”

——Business 2.0

“O’Reilly Conference是聚集关键思想领袖的绝对典范。”

——CRN

“一本O’Reilly的书就代表一个有用、有前途、需要学习的主题。”

——Irish Times

“Tim是位特立独行的商人,他不光放眼于最长远、最广阔的视野并且切实地按照 Yogi Berra的建议去做了 : ‘如果你在路上遇到岔路口,走小路(岔路)。’回顾 过去Tim似乎每一次都选择了小路,而且有几次都是一闪即逝的机会,尽管大路也 不错。”

——Linux Journal


R具有强大的统计计算功能和便捷的数据可视化系统。本书重点讲解R的绘图系统,指导读者通过绘图系统实现数据可视化。

书中提供了快速绘制高质量图形的150多种技巧,每个技巧用来解决一个特定的绘图需求。读者可以通过目录快速定位到自己遇到的问题,查阅相应的解决方案。同时,作者在大部分的技巧之后会进行一些讨论和延伸,介绍一些总结出的绘图技巧。

本书侧重于解决具体问题,是R数据可视化的实战秘籍。本书中绝大多数的绘图案例都是以强大、灵活制图而著称的R包ggplot2实现的,充分展现了ggplot2生动、翔实的一面。从如何画点图、线图、柱状图,到如何添加注解、修改坐标轴和图例,再到分面的使用和颜色的选取等,本书都有清晰的讲解。虽然本书的大多数技巧使用的是ggplot2,但是并不仅仅局限于ggplot2的介绍。作者的理念是用合适的工具来完成合适的绘图任务,读者也可以学到许多其他有用的绘图函数和工具,来适应各种复杂的需求。

本书是学习R中丰富的数据可视化方法的权威手册,非常适合对R语言有基本的了解的读者阅读。


R官方网站的第一句话是这样介绍R语言的:“R是一个用于统计计算和绘图的自由软件环境。”这句话突出了R的两大特色:强大的统计计算功能和便捷的数据可视化系统。在很多情况下,当我们将R与其他同类的语言、软件进行比较时,通常都会强调其灵活的编程和计算能力,然而事实上,R的绘图系统也是其最大的优势之一。

经过长年的开发和完善,目前R主要支持了四套图形系统:基础图形(base)、网格图形(grid)、lattice图形和ggplot2。其中前三个都内置于R的发行包,其功能已经非常稳定,而最为年轻的ggplot2(最早开发于2005年)在历经若干次重大更新后,也逐渐成为了R中数据可视化的主流选择。由于ggplot2具有强大的语法特性和优雅的图形外观,它迅速吸引了众多的使用者和开发者,并已经开始被移植到其他语言中,如Python、Julia等。可以说,ggplot2与R中传统的图形系统形成了良好的互补:后者适合快速的数据探索性分析,而前者则可以便捷地生成复杂的、高质量的统计图形。

本书中绝大多数的绘图案例都是基于ggplot2实现的,在某种意义上,本书可以认为是一部优秀的ggplot2入门和进阶手册。正如之前所说,ggplot2之所以强大,是因为它不仅仅是一些函数的堆砌,而是有其内在的语法支持。因为这些特点,ggplot2看上去更像是一门新的“语言”,从而需要使用者有一个学习和熟练的过程。

我们知道,学习一门语言(中文、英语、C++、R等),大体都要经过一个从语法到词汇,再到句法应用的过程。ggplot2的作者Hadley Wickham曾写过一本介绍ggplot2核心思想的书籍《ggplot2: Elegant Graphics for Data Analysis》,其中介绍了ggplot2的基本概念和用法,相当于为读者搭起了ggplot2的“语法”框架,而本书则是一本丰富、有趣的“词汇和例句书”。换句话说,本书侧重于解决具体问题,是一本实战秘籍:通过它读者可以了解到ggplot2生动、翔实的一面。比如,从如何画点图、线图、柱状图,到如何添加注解、修改坐标轴和图例,再到分面的使用和颜色的选取等,本书都有清晰的讲解。

本书由150多个精选的“技巧”组成,每个“技巧”都用来解决一个特定的绘图需求。读者可以通过章节的目录快速定位到自己遇到的问题,然后查阅相应的解决方案。同时,作者在大部分的“技巧”之后会进行一些讨论和延伸,介绍一些总结出的绘图技巧。就我自己的经历而言,在阅读本书之前,有时用ggplot2作图会遇到一些非常细节的问题,例如,如何调整条形图的顺序,如何修改图例的外观,如何选择文字的大小和字体?这些问题都会让我花费很多时间来搜索解决方案。而在我把这本书通读一遍之后,很多问题立刻迎刃而解。我相信,本书对于ggplot2和数据可视化的学习都是大有裨益的。

当然,本书并不仅仅局限于ggplot2的介绍。作者的理念是用合适的工具来完成合适的绘图任务,所以在本书的第2章、第13章和第14章,读者也可以学到许多其他有用的绘图函数和工具,来适应各种复杂的需求。

本书的三位译者为本书中文版的面世付出了大量的时间和精力,其中肖楠翻译了第7~11章和附录,邓一硕翻译了第2~6章,魏太云翻译了前言、第1章和第12~15章。三位译者均是统计之都的成员,他们也是当前国内R语言社区的领军人物,曾翻译了许多R语言的经典教材和书籍,并通过组织中国R语言会议、开办COS数据分析沙龙、参与论坛问题讨论、撰写博客、整理文档、编写软件包等多种方式为R语言社区做出了诸多贡献。值得一提的是,本书13.1节中介绍的corrplot软件包,正是由译者之一的魏太云开发完成的。统计之都团队作为国内R语言最早的一批布道者之一,有幸见证了R语言在国内兴起的整个过程。从网站创始人谢益辉开始在个人博客上连载R语言的教程和心得,到2008年第一届中国R语言会议的召开,再到如今众多R语言中文版书籍的面世,我相信其中的每一步都是国内统计学、以及更广义的数据科学不断向前发展的印记。我们也希望更多的有志之士加入到统计之都的团队之中,为国内统计学和数据科学贡献自己的力量。

本书的译者力求使翻译准确、生动,但疏漏之处在所难免,欢迎读者予以指正。为了让读者方便地获取和提交勘误信息,我们在GitHub上建立了本书的翻译项目页面。读者也可以在统计之都的图书出版栏目留言或提问。

本书的出版离不开众多人士的大力帮助,我们要郑重感谢爱荷华州立大学的王芯同学、中国人民大学的陈森同学和浙江大学的张政同学,他们在本书的翻译过程中提出了很多中肯的意见。没有他们的帮助,本书很难完成。此外,我们还要感谢人民邮电出版的杨海玲女士和编辑们,他们的专业精神让我叹服。

邱怡轩

2013年12月于普渡大学


几年前读研时我开始用R,主要用来分析我在科研工作中收集到的数据。我使用R首先是想摆脱SPSS这样的统计软件的禁锢,即严格的环境和死板的分析。更何况,R是免费的,所以我用不着说服别人为我购买一套这样的软件——这对一个穷研究生来说是相当的重要!此后,随着我对R的了解不断深入,我才发现原来R还可以绘制出非常优秀、动人的数据图形。

本书的每个“技巧”中,都列出了一个问题和对应的解决方法。在大多数情况下,我提供的并不是R中唯一的实现方法,但却是我认为的最佳方案。R如此受欢迎的一个重要原因是它有很多附加的软件包,每一个软件包都为R提供了一些独特的功能。在R中也有很多可视化方面的软件包,但本书主要使用ggplot2(声明:我现在工作的一部分就是开发ggplot2;但是,在我还没意识到我可能会从事与ggplot2相关的工作之前,我已经完成了本书的大部分工作)。

本书并不想罗列五花八门的方法,成为R数据可视化的综合手册;但是我希望当你想绘制所需图形的时候,本书能够对你有所帮助。或者说,当你不知道怎么画的时候,翻一翻这本书或许就可以找到一些可行的方案。

本书面向的读者需要对R至少有一些基本的了解。书中的技巧会让你明白如何解决一些特定的问题。在使用例子的时候,我力图简单明了,这样你就会明白它们的工作机理,并可以方便地把解决方法应用到自己的问题上。

书中的大部分“技巧”都是用ggplot2完成的,有些“技巧”需要ggplot2的最新版本0.9.3,这样也就要求有一个版本相对较新的R——你可以在R的官方网站获取最新版本的R。

如果你对ggplot2不熟悉,请参阅附录A,那里对该包有一个简要的说明。

安装了R后,你可以再安装一些必要的包。除了ggplot2之外,你还可以选择安装gcookbook包(它包含了本书大多数例子的数据集)。要同时安装这两个包,只需运行命令:

install.packages("ggplot2")
install.packages("gcookbook")

你可能会被问到选择CRAN(Comprehensive R Archive Network)镜像的问题。一般而言,任何一个镜像都可以正常工作,不过最好选择一个离你更近的,因为这样速度会更快。安装完包后,每次需要使用ggplot2包时在R会话中运行:

library(ggplot2)

本书中的技巧总是假设你已经加载了ggplot2,所以不会显示这一行代码。

如果你看到这样的错误,意味着你忘记了加载ggplot2。

错误:找不到函数"ggplot"

英文版R的错误提示是:

Error: could not find function "ggplot"

R的主要运行平台是Mac OS X、Linux和Windows,本书中所有的“技巧”都可以在这些平台上运行。在保存位图输出的时候,会有平台的差异,详情参见第14章。

本书采用的体例如下:

这个图标表示一个提示、建议或者一般的注记。

本书的目标是帮助你完成工作。一般而言,你可以在自己的程序和文档中使用本书中的代码,如果你要复制的不是很大一部分代码,则无须取得我们的许可。例如,你可以在程序中使用本书中的多个代码块,无须获取我们许可。但是,要销售或分发来源于O’Reilly图书中的示例的光盘则必须得到许可。通过引用本书中的示例代码来回答问题时,不需要事先获得我们的许可。但是,如果你的产品文档中融合了本书中的大量示例代码,则需要取得我们的许可。

我们很欢迎引用时给出署名,但不做要求。署名一般包括书名、作者、出版社和ISBN。例如“R Graphics Cookbook by Winston Chang (O'Reilly). Copyright 2013 Winston Chang, 978-1-449-31695-2”。

如果你觉得你对例子代码的使用已经超出了合理程度或者上文所述的许可范围,请通过permissions@oreilly.com联系我们。

如果你想就本书发表评论或有任何疑问,敬请联系出版社。

美国:

  O’Reilly Media Inc.

  1005 Gravenstein Highway North

  Sebastopol, CA 95472

中国:

  北京市西城区西直门南大街2号成铭大厦C座807室(100035)

  奥莱利技术咨询(北京)有限公司

关于本书的技术性问题或建议,请发邮件到:

  bookquestions@oreilly.com

欢迎登录我们的网站(http://www.oreilly.com),查看更多我们的书籍、课程、会议和最新动态等信息。

我们的其他联系方式如下:

  Facebook: http://facebook.com/oreilly

  Twitter: http://twitter.com/oreillymedia

  YouTube: http://www.youtube.com/oreillymedia

没有一本书的诞生可完全归结为个人的成果。本书的完成得到了很多人直接或间接的帮助。我要感谢R社区创造并培育了一个积极活跃的生态系统。非常感谢Hadley Wickham的诸多帮助:他编写了本书所依赖的软件包ggplot2,并在O’Reilly出版社考虑出版R图形书籍的时候推荐了我,他还为我打开了一扇深入了解R的窗户。

感谢本书的技术审稿人Paul Teetor、Hadley Wickham、Dennis Murphy和Erik Iverson。他们渊博的知识和对细节的重视极大地提高了本书的质量。我还要感谢O’Reilly出版社积极推进此书的编辑们:Mike Loukides 在初始阶段给了我很多指导,Courtney Nash伴我走到了最后。此外,我还要郑重感谢O’Reilly的Holly Bauer及制作团队的其他成员,他们从头到尾耐心地做了很多细致的编辑工作,并为此书增添了不少特色。

最后,我还要感谢我的妻子Sylia,感谢她一贯的支持和理解——当然不只是在写本书的时候。


本章包括以下基础知识:安装包、使用包和加载数据。

如果你想快速上手,本书大多数技巧都需要安装ggplot2和gcookbook包。运行下面命令来安装:

install.packages(c("ggplot2", "gcookbook"))

然后,在每个R会话中,你需要在运行本书的例子之前先加载它们:

library(ggplot2)
library(gcookbook)

附录A提供了一个关于ggplot2绘图包的简介,主要是面向不熟悉ggplot2的读者。

R中的包是一些为了便于分发和传播而封装在一起的函数和(或)数据集(可以没有数据集)的集合。在你的电脑中安装软件包,便可以扩展R的功能。如果一个R用户编写了一个包并觉得这个包对其他R用户可能有用,那么,这位R用户就可以通过软件包仓库将该包发布。发布R软件包的最主要的软件包仓库是CRAN(Comprehensive R Archive Network),不过也有其他的仓库,如Bioconductor和Omegahat。

如何从CRAN安装R包?

使用install.packages()函数来安装包,括号中写上要安装的包名。以安装ggplot2包为例,运行:

install.packages("ggplot2")

此时系统可能提示你选择一个下载镜像,可以选择离你最近的一个;如果想要确保包的版本是最新的,那就选择Austria站点,因为这是CRAN的主服务器。

当R安装一个包的时候,该包依赖的所有包也都会被自动安装。

CRAN是R包的仓库,在全球范围内有很多镜像,它是R默认使用的库。此外,还有几个软件包仓库,如Bioconductor,它是与基因组数据分析相关的包的软件包仓库。

如何加载一个已经安装了的包?

使用library()函数,括号中写上要加载的包名。以加载ggplot2包为例,运行:

library(ggplot2)

当然,必须确保要加载的包已经被安装了。

本书的大多数技巧都需要在运行代码前加载包,无论是为了绘图(ggplot2包)还是为了加载例子中的数据集(MASS和gcookbook包)。

R的一个不寻常之处是软件包(package)和软件库(library)的术语区别。 尽管我们使用library()函数来加载包(package),但一个包并不是一个软件库;如果你不幸犯此错误,可能会激怒一些资深的R用户。

软件库指的是一个包含了若干软件包的目录。你既可以拥有一个系统级别的软件库,也可以针对每个用户单独设立一个软件库。

如何加载一个分隔符式的文本文件中的数据?

加载逗号分隔组(CSV)数据的最常用方法是:

data <-read.csv("datafile.csv")

由于数据文件有许多不同的格式,为了加载它们,提供了很多对应的选项。如果一个数据集首行没有列名:

data <-read.csv("datafile.csv", header=FALSE)

得到的数据框的列名将是V1V2等,你可能想要重命名列:

#手动为列名赋值
names(data) <-c("Column1","Column2","Column3")

还可以用sep参数来设置分隔符号。如果是空格分隔,使用sep=" ";如果是制表符分隔,使用\t

data <-read.csv("datafile.csv", sep="\t")

默认情况下,数据集中的字符串(string)会被视为因子(factor)处理。假设下面是你的数据文件,然后,你用read.csv()来读取:

"First","Last","Sex","Number"
"Currer","Bell","F",2
"Dr.","Seuss","M",49
"","Student",NA,21

得到的数据框将会把FirstLast等存储为因子,尽管此时将它们视为字符串(或使用R中的术语,字符:character)更为合理。为了区别这一点,可以设置stringsAsFactors=FALSE。如果有些列应该被处理为因子格式,你可以再逐个转换:

data <-read.csv("datafile.csv", stringsAsFactors=FALSE)

#转换为因子
data$Sex <-factor(data$Sex)

str(data)

'data.frame': 3 obs. of 4 variables: $ First : chr "Currer" "Dr." "" $ Last : chr "Bell" "Seuss" "Student" $ Sex : Factor w/ 2 levels "F","M": 1 2 NA $ Number: int 2 49 21

或者,你可以在加载的时候不做设置(字符串自动转换为因子),加载之后再对需要的列进行因子到字符的转换。

read.csv()是对read.table()一个便捷的封装函数。如果需要更多的输入控制,参见?read.table

如何从Excel文件中加载数据?

xlsx包中的函数read.xlsx()可以读取Excel文件,下面的代码将会读取Excel中的第一个工作表:

#只需要安装一次
install.packages("xlsx")

library(xslx)
data <-read.xlsx("datafile.xlsx", 1)

如果需要阅读老版本的Excel文件(.xls格式),gdata包提供了函数read.xls():

# 只需要安装一次
install.packages("gdata")

library(gdata)
# 读取第一张工作表
data <-read.xls("datafile.xls")

使用read.xlsx()加载工作表时,既可以用序数参数sheetIndex来指定,也可以用工作表名参数sheetName来指定:

data <-read.xlsx("datafile.xls", sheetIndex=2)

data <-read.xlsx("datafile.xls", sheetName="Revenues")

使用read.xls()加载工作表时,可以用序数参数sheet来指定:

data <-read.xls("datafile.xls", sheet=2)

安装xlsx和gdata包时需要在电脑上安装其他软件。对于xlsx包,需要安装Java;对于gdata包,需要安装Perl。Perl在Linux和Mac OS X上是系统自带的,但在Windows上没有。如果是在Windows上,你需要安装ActiveState Perl,其社区版本可以免费获得。

如果你不想这样折腾,更简单的替代方案是打开Excel文件后另存为标准的文本格式,比如CSV。

输入?read.xls?read.xlsx来查看更多关于读取文件的选项。

如何从SPSS文件加载数据?

foreign包中的函数read.spss()可以读取SPSS文件。若要读取SPSS文件中的第一张表:

#只需首次使用时安装
install.packages("foreign")

library(foreign)
data <-read.spss("datafile.sav")

foreign包中还有很多读取其他格式文件的函数,包括以下几种。

输入ls("package:foreign")可以查看该包中的所有函数的列表。


虽然本书中大部分图形都是通过ggplot2包绘制的,但这并不是R绘制图形的唯一方法。要快速探索数据,有时使用R基础包中的绘图函数会很有用。这些函数随R软件默认安装,无需另行安装附加包。它们简短易输入,处理简单问题时使用方便,且运行速度极快。

如果你想绘制较为复杂的图形,那么,转用ggplot2包通常是更好的选择。部分原因在于ggplot2提供了一个统一的接口和若干选项来替代基础绘图系统中对图形的修修补补和各种特例。一旦掌握了ggplot2的工作机制,你就可以应用这些知识来绘制从散点图、直方图到小提琴图和地图等各种统计图形了。

本章介绍的技巧演示了用基础绘图系统绘制统计图形的方法,也对如何用ggplot2中的qplot()函数绘制同样的图形做出了说明。qplot()函数的语法与基础绘图系统类似,对于每一个由qplot()函数绘制的图形,技巧中也提供了用更强大的ggplot()函数来绘图的等价解决方案。

如果你已经知道如何使用基础图形系统,那么当你想绘制更复杂的图形时,可以将这些例子放在一起进行对比以帮助你过渡到ggplot2系统。

如何绘制散点图?

使用plot()函数可绘制散点图(见图2-1),运行命令时依次传递给plot()函数一个向量x和一个向量y

plot(mtcars$wt,mtcars$mpg)

图2-1 基础绘图系统绘制的散点图

对于ggplot2系统,可用qplot()函数得到相同的绘图结果(见图2-2):

library(ggplot2)
qplot(mtcars$wt, mtcars$mpg)

图2-2 使用ggplot2包中qplot()函数绘制的散点图

如果绘图所用的两个参数向量包含在同一个数据框内,则可以运行下面的命令:

qplot(wt,mpg,data=mtcars)
# 这与下面等价
ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point()

更多关于绘制散点图的详细内容可参见本书第5章。

如何绘制折线图?

使用plot()函数绘制折线图(见图2-3左图)时需向其传递一个包含x值的向量和一个包含y值的向量,并使用参数type="l"

plot(pressure$temperature, pressure$pressure, type="l")

图2-3 左图:基础绘图系统绘制的折线图 右图:向图形中添加数据点和另一条折线

如果要向图形中添加数据点或者多条折线(见图2-3右图),则需先用plot()函数绘制第一条折线,再通过points()函数和lines()函数分别添加数据点和更多折线:

plot(pressure$temperature, pressure$pressure, type="l")
points(pressure$temperature,pressure$pressure)
lines(pressure$temperature, pressure$pressure/2, col="red")
points(pressure$temperature, pressure$pressure/2, col="red")

在ggplot2中,可以使用qplot()函数并将参数设定为geom="line"得到类似的绘图结果(见图2-4):

library(ggplot2)
qplot(pressure$temperature, pressure$pressure, geom="line")

图2-4 左图:ggplot2中的qplot()函数绘制的折线图 右图:添加数据点的折线图

如果函数的两个参数向量已包含在同一个数据框中,则可以运行下面的语句:

qplot(temperature,pressure, data=pressure, geom="line")
# 这等价于下面的命令
ggplot(pressure, aes(x=temperature, y=pressure)) + geom_line()

# 添加数据点
qplot(temperature, pressure, data=pressure, geom=c("line", "point"))
# 这等价于下面的命令
ggplot(pressure, aes(x=temperature, y=pressure)) + geom_line() + geom_point()

更多关于绘制折线图的详细内容可参见本书第4章。

如何绘制条形图?

对变量的值绘制条形图(见图2-5左图),可以使用barplot()函数,并向其传递两个向量作为参数,第一个向量用来设定条形的高度,第二个向量用来设定每个条形对应的标签(可选)。

如果向量中的元素已被命名,则系统会自动使用元素的名字作为条形标签:

barplot(BOD$demand, names.arg=BOD$Time)

有时候,“条形图”表示的是分组数据中各个元素的频数(见图2-5右图)。这种条形图跟直方图有些类似,不过,其用离散取值的x轴替代了直方图中连续取值的x轴。要计算向量中各个类别的频数,可以使用table()函数。

table(mtcars$cyl)

 4 6 8 11 7 14 
# 值为4的频数为11,6的为7,8的为14

图2-5 左图:基础绘图系统绘制的条形图 右图:向量元素的频数条形图

只需将上面的表格结果传递给barplot()函数即可绘制频数条形图:

# 生成频数表
barplot(table(mtcars$cyl))

对于ggplot2系统,可以使用qplot()函数得到类似的绘图结果(见图2-6)。绘制变量值的条形图时需将参数设定为geom="bar"stat="identity"。注意变量x分别为连续取值和离散取值时输出结果的差异。

library(ggplot2)
qplot(BOD$Time, BOD$demand, geom="bar", stat="identity")
# 将x转化为因子型变量,令系统将其视作离散值
qplot(factor(BOD$Time), BOD$demand, geom="bar", stat="identity")

图2-6 左图:qplot()函数绘制的连续变量x的变量值条形图 右图:将变量x转化为因子型变量(注意,横坐标上没有6这个类别)

qplot()函数也可以用来绘制分组变量的频数条形图(见图2-7),事实上,这是ggplot2绘制条形图的默认方式,它比绘制变量值条形图的命令更简短。再提醒一次,注意连续x轴和离散x轴的差异。

# cyl是连续变量
qplot(mtcars$cyl)
# 将cyl转化为因子型变量
qplot(factor(mtcars$cyl))

图2-7 左图:qplot()函数绘制的连续变量x的频数条形图 右图:将cyl转化为因子型变量

如果参数向量包含在同一个数据框内,则可以运行下面的语句:

# 变量值条形图,这里用BOD数据框中的Time列
# 和demand列分别作为x和y参数
qplot(Time, demand, data=BOD, geom="bar", stat="identity")
# 这与下面的语句等价
ggplot(BOD, aes(x=Time, y=demand)) + geom_bar(stat="identity")

# 频数条形图
qplot(factor(cyl), data=mtcars)
# 这与下面的语句等价
ggplot(mtcars, aes(x=factor(cyl))) + geom_bar()

更多关于绘制条形图的详细内容可参见本书第3章。

如何绘制直方图来查看一维数据的分布特征?

可以使用hist()函数绘制直方图(见图2-8),使用时需向其传递一个向量:

hist(mtcars$mpg)

# 通过breaks参数指定大致组距
hist(mtcars$mpg,breaks=10)

图2-8 左图:基础绘图系统绘制的直方图 右图:使用更多分组。注意:由于组距变小,每组对应的样本数有所减少

对于ggplot2包,可以使用qplot()函数得到同样的绘图结果(见图2-9):

qplot(mtcars$mpg)

图2-9 左图:ggplot2中qplot()函数绘制的直方图,组距为默认值 右图:组距更大的直方图

如果参数向量在同一个数据框内,则可以使用下面的语句:

library(ggplot2)
qplot(mpg, data=mtcars, binwidth=4)
# 这等价于
ggplot(mtcars, aes(x=mpg)) + geom_histogram(binwidth=4)

更多关于绘制直方图的内容参见6.1节和6.2节。

如何绘制箱线图以对不同分布进行比较?

使用plot()函数绘制箱线图(见图2-10)时向其传递两个向量:xy。当x为因子型变量(与数值型变量对应)时,它会默认绘制箱线图:

plot(ToothGrowth$supp, ToothGrowth$len)

当两个参数向量包含在同一个数据框中时,也可以使用公式语法。公式语法允许我们在x轴上使用变量组合,如图 2-10 所示。

# 公式语法
boxplot(len ~ supo, data = ToothGrowth)
# 在x轴上引入两变量的交互
boxplot(len ~ supp + dose, data = ToothGrowth)

图2-10 左图:基础绘图系统绘制的箱线图 右图:基于多分组变量的箱线图

对于ggplot2包,你可以使用qplot()函数绘制同样的图形(见图2-11),使用时将参数设定为geom="boxplot"

library(ggplot2)
qplot(ToothGrowth$supp, ToothGrowth$len, geom="boxplot")

图2-11 左图:qplot()函数绘制的箱线图 右图:基于多分组变量的箱线图

当两个参数向量在同一个数据框内时,则可以使用下面的语句:

qplot(supp, len, data=ToothGrowth, geom="boxplot")
# 这等价于
ggplot(ToothGrowth, aes(x=supp,y=len)) + geom_boxplot()

使用interaction()函数将分组变量组合在一起也可以绘制基于多分组变量的箱线图,如图2-11右图所示。本例中,dose变量是数值型,因此,我们必须先将其转化为因子型变量,再将其作为分组变量:

# 使用三个独立的向量参数
qplot(interaction(ToothGrowth$supp, ToothGrowth$dose), ToothGrowth$len,geom="boxplot")
# 也可以以数据框中的列作为参数
qplot(interaction(supp, dose), len, data=ToothGrowth, geom="boxplot")
# 这等价于
ggplot(ToothGrowth, aes(x=interaction(supp, dose), y=len)) + geom_boxplot()

你可能会注意到基础绘图系统绘制的箱线图与ggplot2略有不同。这是因为两者在绘图过程中调用的计算分位数的方法略有差异。运行?geom_boxplot?boxplot.base命令可以得到更多关于两者差异的细节信息。

更多关于绘制箱线图的内容参见6.6节。

如何绘制函数图像?

可以使用curve()函数绘制函数图像,如图2-12左图所示。使用时需向其传递一个关于变量x的表达式:

curve(x^3 - 5*x, from=-4, to=4)

你可以绘制任何一个以数值型向量作为输入且以数值型向量作为输出的函数图像,包括你自己定义的函数,如图2-12右图所示。

图2-12 左图:基础绘图系统绘制的函数图像 右图:绘制用户自定义的函数

将参数设置为add=TRUE可以向已有图形添加函数图像:

# 绘制用户自定义的函数图像
myfun <- function(xvar) {
   1/(1 + exp(-xvar + 10))    
}
curve(myfun(x), from=0, to=20)
# 添加直线
curve(1-myfun(x), add = TRUE, col ="red")

对于ggplot2,可以使用qplot()函数绘制得到同样的结果(见图2-13)。使用时需设定stat="function"geom="line",并向其传递一个输入和输出皆为数值型向量的函数:

library(ggplot2)
# 将x轴的取值范围设定为0到20
qplot(c(0, 20), fun=myfun, stat="function", geom="line")
# 这等价于
ggplot(data.frame(x=c(0, 20)), aes(x=x)) + stat_function(fun=myfun, geom="line")

图2-13 qplot()函数绘制的函数图像

更多关于绘制函数图像的内容参见13.2节。


条形图也许是最常用的数据可视化方法,通常用来展示不同的分类下(在x轴上)某个数值型变量的取值(在y轴上)。例如,条形图可以用来形象地展示四种不同商品的价格情况,但不适宜用来展示商品价格随时间的变动趋势,因为这里时间是一个连续变量——尽管我们也可以这么做,后面会看到这种情形。

绘制条形图时需特别注意一个重要的细节:有时条形图的条形高度表示的是数据集中变量的频数,有时则表示变量取值本身。牢记这个区别——这里极易混淆,因为两者与数据集的对应关系不同,但又对应同样的术语。本章将对此进行深入讨论,并分别介绍这两类条形图的绘图技巧。

你有一个包含了两列数据的数据框,其中一列数据表示条形在x轴上的位置,另一列表示每个条形在y轴上对应的高度,基于此,如何绘制条形图?

使用ggplot()函数和geom_bar(stat="identity")绘制上述条形图,并分别指定与x轴和y轴对应的变量(见图3-1)。

library(gcookbook) #为了使用数据
ggplot(pg_mean, aes(x=group, y=weight)) + geom_bar(stat="identity")

图3-1 x轴为离散时,针对变量值绘制的条形图(参数stat="identity"

x是连续型(数值型)变量时,条形图的结果与上图会略有不同。此时,ggplot不是只在实际取值处绘制条形,而将在x轴上介于最大值和最小值之间所有可能的取值处绘制条形,如图3-2所示。我们可以使用factor()函数将连续型变量转化为离散型变量。

# 没有Time == 6的输入
BOD

 Time demand    1  8.3   2  10.3   3  19.0   4  16.0   5  15.6   7  19.8

# Time是数值型(连续型)变量
str(BOD)

'data.frame': 6 obs. of 2 variables: $ Time : num 1 2 3 4 5 7 $ demand: num 8.3 10.3 19 16 15.6 19.8 - attr(*, "reference")= chr "A1.4, p. 270"

ggplot(BOD, aes(x=Time, y=demand)) + geom_bar(stat="identity")

# 使用factor()函数将Time转化为离散型(分类)变量
ggplot(BOD, aes(x=factor(Time), y=demand)) + geom_bar(stat="identity")

图3-2 左图:针对变量值绘制的条形图(参数stat="identity"),x轴对应的是连续型变量  右图:将x转化为因子型变量之后绘制的条形图(注意此处缺失了取值为6的条形)

本例中,数据集中包含两列分别对应于xy变量。如果你想让条形图的高度与每组变量的频数相对应,可参见3.3节的内容。

默认设置下,条形图的填充色为黑灰色且条形图没有边框线,我们可通过调整fill参数的值来改变条形图的填充色;可通过colour参数为条形图添加边框线。在图3-3中,我们将填充色和边框线分别指定为浅蓝色和黑色。

图3-3 所有条形的填充色和边框线颜色均为单色

ggplot(pg_mean, aes(x=group, y=weight)) +
  geom_bar(stat="identity", fill="lightblue", colour="black")

在ggplot2中,颜色参数默认使用的是英式拼写colour,而非美式拼写color。然而,ggplot2会在底层将美式拼写重映射为英式拼写,因此输入美式拼写的参数并不影响函数的运行。

如果你想让条形图的高度对应于每组变量的频数,可参见3.3节的内容。

根据另一个变量值重排因子水平顺序的内容可参见15.9节。手动更改因子水平顺序的内容,可参见15.8节。

更多关于图形着色的内容,可参见本书第12章。

如何绘制基于某个分类变量的簇状条形图?

将分类变量映射到fill参数,并运行命令geom_bar(position="dodge")

下面以cabbage_exp数据集为例演示一下绘图过程,cabbage_exp数据集包含两个分类变量CultivarDate及一个连续型变量Weight

library(gcookbook) #为了使用数据
cabbage_exp

 Cultivar Date Weight     c39 d16 3.18     c39 d20 2.80     c39 d21 2.74     c52 d16 2.26     c52 d20 3.11     c52 d21 1.47

我们分别将DateCultivar映射给xfill(见图3-4)。

图3-4 簇状条形图

ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(position="dodge",stat="identity")

最简单的条形图通常只对应一个绘制在x轴上的分类变量和一个绘制在y轴上的连续型变量。有时候,我们想额外添加一个分类变量跟x轴上的分类变量一起对数据进行分组。此时,可通过将该分类变量映射给fill参数来绘制簇状条形图,这里的fill参数用来指定条形的填充色。在这一过程中必须令参数position="dodge"以使得两组条形在水平方向上错开排列,否则,系统会输出堆积条形图(参见3.7节)。

与映射给条形图x轴的变量类似,映射给条形填充色参数的变量应该是分类变量而不是连续型变量。

我们可以通过将geom_bar()中的参数指定为colour="black"为条形添加黑色边框线;可以通过scale_fill_brewer()或者scale_fill_manual()函数对图形颜色进行设置。在图3-5中,我们使用RColorBrewer包中的Pastel1调色盘对图形进行调色。

图3-5 添加了黑色边框线的簇状条形图,这里用了新的调色板

ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(position="dodge", stat="identity", colour="black") +
  scale_fill_brewer(palette="Pastel1")

其他图形属性诸如颜色colour(指定条形图的边框线颜色)和线型(linestyle)也能用来对变量进行分组,不过,填充色(fill)也许是最合人心意的图形属性。

注意,如果分类变量各水平的组合中有缺失项,那么,绘图结果中的条形则相应地略去不绘,同时,临近的条形将自动扩充到相应位置。删去上例数据中的最后一行后,可得到图3-6。

ce <- cabbage_exp[1:5,] #复制删除了最后一行的数据集
ce

Cultivar Date Weight    c39 d16 3.18    c39 d20 2.80    c39 d21 2.74    c52 d16 2.26     c52 d20 3.11
  
ggplot(ce, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(position="dodge",stat="identity", colour="black") +
  scale_fill_brewer(palette="Pastel1")

图3-6 缺失条形的簇状条形图——临近的条形自动扩充到相应位置

如果你的数据与上面类似,那么,你可以在分类变量组合缺失的那一项为变量y手动输入一个NA值。

更多关于条形图着色的内容,可参见3.4节。

根据另一个变量值重排因子水平顺序的内容可参见15.9节。

如果数据集中每行数据对应于一个样本,如何针对样本频数绘制条形图?

使用geom_bar()函数,同时不要映射任何变量到y参数(见图3-7)。

ggplot(diamonds, aes(x=cut)) + geom_bar()
# 等价于使用geom_bar(stat="bin")

图3-7 频数条形图

diamonds数据集共有53 940行数据,每行数据对应于一颗钻石的品质信息:

diamonds

   carat cut color clarity depth table price  x  y  z 1   0.23   Ideal  E  SI2   61.5   55  326  3.95 3.98 2.43 2  0.21 Premium  E  SI1   59.8  61  326  3.89 3.84 2.31 3  0.23   Good  E  VS1   56.9  65  327  4.05 4.07 2.31 ... 53539 0.86 Premium  H  SI2   61.0  58  2757  6.15 6.12 3.74 53540 0.75  Ideal  D  SI2   62.2  55  2757  5.83 5.87 3.64

geom_bar()函数在默认情况下将参数设定为stat="bin",该操作会自动计算每组(根据x轴上面的变量进行分组)变量对应的观测数。从图中可以看到,切工精美的钻石大概有23 000颗。

本例中,x轴对应的是离散型变量。当x轴对应于连续型变量时,我们会得到一张直方图,如图3-8所示。

ggplot(diamonds, aes(x=carat)) + geom_bar()

图3-8 x轴对应于连续型变量的条形图,也即常说的直方图

在这个例子中,使用geom_bar()geom_histogram()具有相同的效果。

如果不想让ggplot()函数自动计算每组数据的行数绘制频数条形图,而是想通过数据框中的某列来指定y参数的话,可以参见3.1节的内容。

当然,也可以通过先计算出每组数据的行数,再将计算结果传递给ggplot()函数来绘制上图。更多关于数据描述的内容,可参见15.17节。

更多关于直方图的内容,可参见6.1节。

如何将条形图中的条形设定为不同的颜色?

将合适的变量映射到填充色(fill)上即可。

这里以数据集uspopchange为例。该数据集描述了美国各州人口自2000年到2010年的变化情况。我们选取出人口增长最快的十个州进行绘图。图中会根据地区信息(东北部、南部、中北部、西部)对条形进行着色。

首先,选取出人口增长最快的十个州:

library(gcookbook) #为了使用数据
upc <- subset(uspopchange, rank(Change)>40)
upc
     State  Abb Region Change     Arizona  AZ  West  24.6     Colorado  CO  West  16.9     Florida  FL  South  17.6     Georgia  GA  South  18.3      Idaho  ID  West  21.1      Nevada  NV  West  35.1 North  Carolina  NC  South  18.5 South  Carolina  SC  South  15.3       Texas  TX  South  20.6       Utah  UT  West  23.8

接下来,将Region映射到fill并绘制条形图(见图3-9):

ggplot(upc, aes(x=Abb, y=Change, fill=Region)) + geom_bar(stat="identity")

图3-9 将分类变量映射给fill参数

条形图的默认颜色不太吸引眼球,因此,可能需要借助函数scale_fill_brewer()scale_fill_manual()重新设定图形颜色。这里我们调用后者。我们通过把参数指定为colour="black"将条形的边框线设定为黑色(见图3-10)。注意:颜色的映射设定是在aes()内部完成的,而颜色的重新设定是在aes()外部完成的:

ggplot(upc, aes(x=reorder(Abb, Change), y=Change, fill=Region)) +
   geom_bar(stat="identity", colour="black") +
   scale_fill_manual(values=c("#669933","#FFCC66")) +
   xlab("State")

图3-10 分类着色、具有黑色边框线的簇状条形图,条形根据人口变动百分比排序

本例用到了reorder()函数。在本例中,根据条形图的高度进行排序比按照字母顺序对分类变量排序更有意义。

更多关于使用reorder()函数将因子根据另一个变量重新水平排序的内容,可参见15.9节。

更多关于图形着色的内容,参见第12章。

如何根据条形对应的正负取值对其进行分别着色?

下面以climate数据的一个子集为例。首先,创建一个对取值正负情况进行标示的变量pos

library(gcookbook) #为了使用数据
csub <- subset(climate, Source=="Berkeley" & Year >= 1900)
csub$pos <- csub$Anomaly10y >=0

csub

  Source  Year Anomaly1y Anomaly5y Anomaly10y Unc10y  Berkeley  1900    NA    NA   -0.171 0.108 FALSE Berkeley  1901    NA    NA   -0.162 0.109 FALSE Berkeley  1902    NA    NA   -0.177 0.108 FALSE ... Berkeley  2002    NA    NA   0.856 0.028 TRUE Berkeley  2003    NA    NA   0.869 0.028 TRUE Berkeley  2004    NA    NA   0.884 0.029 TRUE

上述过程准备完毕后,将pos映射给填充色参数(fill)并绘制条形图(见图3-11)。注意:这里条形图的参数设定为position="identity",可以避免系统因对负值绘制堆积条形而发出的警告信息。

ggplot(csub, aes(x=Year, y=Anomaly10y, fill=pos))+
   geom_bar(stat="identity", position="identity")

图3-11 对正负取值的条形分别着色

上面的绘图过程存在一些问题。首先,图形着色效果可能跟我们想要的相反:蓝色是冷色,通常对应于负值;红色是暖色,通常对应于正值。其次,图例显得多余且扰乱视觉。

我们可以通过scale_fill_manual()参数对图形颜色进行调整,设定参数guide=FALSE可以删除图例,如图3-12所示。同时,我们通过设定边框颜色(colour)和边框线宽度(size)为图形填加一个细黑色边框。其中,边框线宽度(size)是用来控制边框线宽度的参数,单位是毫米:

ggplot(csub, aes(x=Year, y=Anomaly10y, fill=pos)) +
  geom_bar(stat="identity", position="identity", colour="black", size=0.25) +
  scale_fill_manual(values=c("#CCEEFF","#FFDDDD"), guide=FALSE)

图3-12 重新设定颜色并移除了图例的条形图

更多关于更改图形颜色的内容可参见12.3节和12.4节。

更多关于隐藏图例的内容可参见10.1节。

如何调整条形图的条形宽度和条形间距?

通过设定geom_bar()函数的参数width可以使条形变得更宽或者更窄。该参数的默认值为0.9;更大的值将使绘制的条形更宽,反之则是更窄(见图3-13)。

例如,标准宽度的条形图如下:

library(gcookbook) #为了使用数据

ggplot(pg_mean, aes(x=group, y=weight)) + geom_bar(stat="identity")

图3-13 对应于不同条形宽度的条形图

窄些的条形图:

ggplot(pg_mean, aes(x=group, y=weight)) + geom_bar(stat="identity", width=0.5)

宽些的条形图(条形图的最大宽度为1):

ggplot(pg_mean, aes(x=group, y=weight)) + geom_bar(stat="identity", width=1)

簇状条形图默认组内的条形间距为0。如果希望增加组内条形的间距,则可以通过将width设定得小一些,并令position_dodge的取值大于width(见图3-14)。

图3-14 左图:条形更窄的簇状条形图 右图:具有条形间距的簇状条形图

更窄的簇状条形图可运行:

ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(stat="identity", width=0.5, position="dodge")

添加条形组距可运行:

ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(stat="identity", width=0.5, position=position_dodge(0.7))

第一幅图的绘图命令中用到了参数position="dodge",第二幅图的绘图命令中用到的参数是position=position_dodge()。这是因为position="dodge"是参数默认为0.9的position_dodge()的简写。当我们需要单独指定该参数的时候,必须输入完整的命令。

width参数的默认值是0.9,position_dodge函数中width参数的默认值也是0.9。更确切地说,position_dodge函数和geom_bar()函数中的width参数的取值是一样的。

下面的四个命令是等价的:

geom_bar(position="dodge")
geom_bar(width=0.9, position=position_dodge())
geom_bar(position=position_dodge(0.9))
geom_bar(width=0.9, position=position_dodge(width=0.9))

条形图中,条形中心对应的x轴坐标分别是1、2、3等,但通常我们不会利用上这些数值。当用户运行命令geom_bar(width=0.9)时,每组条形将在x轴上占据0.9个单位宽度。运行命令position_dodge(width=0.9)时,ggplot2会自动调整条形位置,以使每个条形的中心恰好位于当每组条形宽度为0.9,且组内条形紧贴在一起时的位置,如图3-15所示。图中上下两部分都对应position_dodge(width=0.9),只是上图对应于0.9的条形宽度,下图对应于0.2的条形宽度。虽然上下两部分对应的条形宽度不同,但两图的条形中心是上下对齐的。

图3-15 条形间距相同但条形宽度不同的簇状条形图

如果你将整幅图形进行伸缩,条形图也会依照相应的比例进行伸缩。要了解图形是怎样变化的,只需改变图形所在窗口的大小,然后,观察图形的变化即可。更多关于在输出图形文件时控制图片大小的内容可参见第14章。

如何绘制堆积条形图?

使用geom_bar()函数,并映射一个变量给填充色参数(fill)即可。该命令会将Date对应到x轴上,并以Cultivar作为填充色,如图3-16所示。

library(gcookbook) # 为了使用数据
ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(stat="identity")

图3-16 堆积条形图

弄清楚图形对应的数据结构有助于理解图形的绘制过程。上例数据集中Date变量对应于三个水平、Cultivar变量对应于两个水平,两个变量不同水平的组合又分别与一个Weight变量相对应:

cabbage_exp

 Cultivar Date Weight    sd n     se   c39  d16  3.18 0.9566144 10 0.30250803   c39  d20  2.80 0.2788867 10 0.08819171   c39  d21  2.74 0.9834181 10 0.31098410   c52  d16  2.26 0.4452215 10 0.14079141

默认绘制的条形图有一个问题,即条形的堆积顺序与图例顺序是相反的。我们可以通过guides()函数对图例顺序进行调整,并指定图例所对应的需要调整的图形属性,本例中对应的是填充色(fill),如图3-17所示。

ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(stat="identity") +
  guides(fill=guide_legend(reverse=TRUE))

图3-17 调整图例顺序后的堆积条形图

如果你想调整条形的堆叠顺序,可以通过指定图形映射中的参数order=desc()来实现:

library(plyr) # 为了使用desc()函数
ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar, order=desc(Cultivar))) +
  geom_bar(stat="identity")

图3-18 翻转了堆叠顺序的堆积条形图

当然,也可以通过调整数据框中对应列的因子顺序来实现上述操作(参见15.8节),但需谨慎进行该操作,因为对数据进行修改可能导致其他分析结果也发生改变。

为了获得效果更好的条形图,我们保持逆序的图例顺序不变,同时,使用scale_fill_brewer()函数得到一个新的调色板,最后设定colour="black"为条形添加一个黑色边框线(如图3-19所示)。

ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(stat="identity", colour="black") +
   guides(fill=guide_legend(reverse=TRUE)) +
   scale_fill_brewer(palette="Pastel1")

图3-19 翻转图例顺序,使用新调色板和黑色边框线的堆积条形图

更多关于条形图着色的内容可参见3.4节。

将因子根据另一个变量重新排列水平顺序的内容可参见15.9节。手动更改因子水平顺序的内容,可参见15.8节。

如何绘制可展示百分比的堆积条形图(又叫百分比堆积条形图)?

首先,通过plyr包中的ddply()函数和transform()函数将每组条形对应的数据标准化为100%格式,之后,针对计算得到的结果绘制堆积条形图即可,如图3-20所示。

library(gcookbook) # 为了使用数据
library(plyr)
# 以Date为切割变量()对每组数据进行transform()
ce <- ddply(cabbage_exp, "Date", transform,
       percent_weight = Weight / sum(Weight) * 100)

ggplot(ce, aes(x=Date, y=percent_weight, fill=Cultivar)) +
  geom_bar(stat="identity")

图3-20 百分比堆积条形图

我们用ddply()函数计算每组Date变量对应的百分比。本例中,ddply()函数根据指定的变量Date对数据框cabbage_exp进行分组,并对各组数据执行transform()函数(ddply()函数中设定的其他参数也会传递给该函数)。

下面是cabbage_exp数据,从中可以看出ddply()命令对其进行操作的过程。

cabbage_exp

 Cultivar Date Weight    sd n     se    c39 d16  3.18 0.9566144 10 0.30250803    c39 d20  2.80 0.2788867 10 0.08819171    c39 d21  2.74 0.9834181 10 0.31098410    c52 d16  2.26 0.4452215 10 0.14079141    c52 d20  3.11 0.7908505 10 0.25008887    c52 d21  1.47 0.2110819 10 0.06674995

ce <- ddply(cabbage_exp, "Date", transform,
       percent_weight = Weight / sum(Weight) * 100)

Cultivar Date Weight    sd n     se percent_weight    c39 d16  3.18 0.9566144 10 0.30250803    58.45588    c52 d16  2.26 0.4452215 10 0.14079141    41.54412    c39 d20  2.80 0.2788867 10 0.08819171    47.37733    c52 d20  3.11 0.7908505 10 0.25008887    52.62267    c39 d21  2.74 0.9834181 10 0.31098410    65.08314    c52 d21  1.47 0.2110819 10 0.06674995    34.91686

计算出百分比之后,就可以按照绘制常规堆积条形图的方法来绘制百分比堆积条形图了。

跟常规堆积条形图一样,我们可以调整百分比堆积条形图的图例顺序、更换调色板及添加边框线,如图3-21所示。

ggplot(ce, aes(x=Date, y=percent_weight, fill=Cultivar)) +
  geom_bar(stat="identity", colour="black") +
  guides(fill=guide_legend(reverse=TRUE)) +
  scale_fill_brewer(palette="Pastel1")

图3-21 反转图例顺序,使用新调色板和黑色边线框的百分比堆积条形图

更多关于分组对数据进行变换的内容可参见15.16节。

如何给条形图添加数据标签?

在绘图命令中加上geom_text()即可为条形图添加数据标签。运行命令时,需要分别指定一个变量映射给xy和标签本身。通过设定vjust(竖直调整数据标签位置)可以将标签位置移动至条形图顶端的上方或者下方,如图3-22所示。

library(gcookbook) # 为了使用数据

# 在条形图顶端下方
ggplot(cabbage_exp, aes(x=interaction(Date, Cultivar), y=Weight)) +
   geom_bar(stat="identity") +
   geom_text(aes(label=Weight), vjust=1.5, colour="white")

# 在条形图顶端上方
ggplot(cabbage_exp, aes(x=interaction(Date, Cultivar), y=Weight)) +
  geom_bar(stat="identity") +
  geom_text(aes(label=Weight), vjust=-0.2)

图3-22 左图:置于条形图顶端下方的数据标签 右图:置于条形图顶端上方的数据标签

注意,当数据标签被置于条形图顶端时,它们可能会被遮挡。为了避免这个问题,可以参见8.2节的内容。

在图3-22中,数据标签的y轴坐标位于每个条形的顶端中心位置;通过设定竖直调整(vjust)可以将数据标签置于条形图顶端的上方或者下方。这种做法的不足之处在于当数据标签被置于条形图顶端上方时有可能使数据标签溢出绘图区域。为了修正这个问题,我们可以手动设定y轴的范围,也可以保持竖直调整不变,而令数据标签的y轴坐标高于条形图顶端。后一种办法的不足之处在于,当你想将数据标签完全置于条形图顶端上方或者下方的时候,竖直方向调整的幅度依赖于y轴的数据范围;而更改vjust时,数据标签离条形顶端的距离会根据条形图的高度自动进行调整。

# 将y轴上限变大
ggplot(cabbage_exp, aes(x=interaction(Date, Cultivar), y=Weight)) +
  geom_bar(stat="identity") +
  geom_text(aes(label="Weight"), vjust=-0.2) +
  ylim(0, max(cabbage_exp$Weight)*1.05)

# 设定标签的y轴位置使其略高于条形图顶端——y轴范围会自动调整
ggplot(cabbage_exp, aes(x=interaction(Date, Cultivar), y=Weight)) +
  geom_bar(stat="identity") +
  geom_text(aes(y=Weight+0.1, label=Weight))

对于簇状条形图,需要设定position=position_dodge()并给其一个参数来设定分类间距。分类间距的默认值是0.9,因为簇状条形图的条形更窄,所以,需要使用字号(size)来缩小数据标签的字体大小以匹配条形宽度。数据标签的默认字号是5,这里我们将字号设定为3使其看起来更小(见图3-23)。

ggplot(cabbage_exp, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(stat="identity", position="dodge") +
  geom_text(aes(label=Weight), vjust=1.5, colour="white",
        position=position_dodge(.9), size=3)

图3-23 簇状条形图的数据标签

向堆积条形图添加数据标签之前,要先对每组条形对应的数据进行累积求和。在进行本操作之前,须保证数据的合理排序,否则,可能计算出错误的累积和。我们可以用plyr包中的arrange()函数完成上述操作,plyr包是一个随ggplot2包加载的软件包。

library(plyr)
# 根据日期和性别对数据进行排序
ce <- arrange(cabbage_exp, Date, Cultivar)

确认数据合理排序之后,我们可以借助ddply()函数以Date为分组变量对数据进行分组,并分别计算每组数据对应的变量Weight的累积和。

# 计算累积和
ce <- ddply(ce, "Date", transform, label_y=cumsum(Weight))
ce

Cultivar Date Weight    sd n     se percent_weight label_y    c39 d16  3.18 0.9566144 10 0.30250803    58.45588  3.18    c52 d16  2.26 0.4452215 10 0.14079141    41.54412  5.44    c39 d20  2.80 0.2788867 10 0.08819171    47.37733  2.80    c52 d20  3.11 0.7908505 10 0.25008887    52.62267  5.91    c39 d21  2.74 0.9834181 10 0.31098410    65.08314  2.74    c52 d21  1.47 0.2110819 10 0.06674995    34.91686  4.21

ggplot(ce, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(stat="identity") +
  geom_text(aes(y=label_y, label=Weight), vjust=1.5, colour="white")

结果如图3-24所示。

图3-24 堆积条形图的数据标签

使用数据标签时,对堆叠顺序的调整最好是通过在计算累积和之前修改因子的水平顺序(参见15.8节)来完成。另一种修改堆叠顺序的方法是在标度中指定breaks参数,但在这里此方法并不合适,因为累计求和的顺序与堆叠的顺序并不一致。

如果想把数据标签置于条形中部(见图3-25),须对累计求和的结果加以调整,并同时略去geom_bar()函数中对y偏移量(offset)的设置:

ce <- arrange(cabbage_exp, Date, Cultivar)

#计算y轴的位置,将数据标签置于条形中部
ce <- ddply(ce, "Date", transform, label_y=cumsum(Weight)-0.5*Weight)

ggplot(ce, aes(x=Date, y=Weight, fill=Cultivar))+
  geom_bar(stat="identity")+
  geom_text(aes(y=label_y, label=Weight), colour="White")

图3-25 置于堆积条形图条形中部的数据标签

为了得到效果更好的条形图(见图3-26),我们修改一下图例顺序和颜色,将数据标签置于条形中间,同时通过字号参数(size)缩小标签字号,并调用paste函数在标签后面添加“kg”,为了使得标签保留两位小数我们还需调用format函数:

ggplot(ce, aes(x=Date, y=Weight, fill=Cultivar)) +
  geom_bar(stat="identity", colour="black") +
  geom_text(aes(y=label_y, label=paste(format(Weight, nsmall=2), "kg")),
        size=4)+
  guides(fill=guide_legend(reverse=TRUE)) +
  scale_fill_brewer(palette="Pastel1")

图3-26 自定义的带数据标签的堆积条形图

更多关于控制文本格式的内容可参见9.2节。

更多关于分组转换数据的内容可参见15.6节。

如何绘制Cleveland点图?

有时人们会用Cleverland点图来替代条形图以减少图形造成的视觉混乱并使图形更具可读性。

最简便的绘制Cleverland点图的方法是直接运行geom_point()命令(见图3-27)。

library(gcookbook) # 为了使用数据
tophit <- tophitters2001[1:25, ] # 取出tophitters数据集中的前25个数据

ggplot(tophit, aes(x=avg, y=name)) + geom_point()

图3-27 简单点图

tophitters2001数据集包含很多列,这里我们只看其中三列:

tophit[, c("name","lg","avg")]

      name lg  avg Larry Walker NL 0.3501 Ichiro Suzuki AL 0.3497 Jason Giambi AL 0.3423 ... Jeff Conine AL 0.3111 Derek Jeter AL 0.3111

图3-27中的名字是按字母先后顺序排列的,这种排列方式用处不大。通常,点图中会根据x轴对应的连续变量的大小取值对数据进行排序。

尽管tophit的行顺序恰好与avg的大小顺序一致,但这并不意味着在图中也是这样排序的。在点图的默认设置下,坐标轴上的变量通常会根据变量类型自动选取合适的排序方式。本例中变量name属于字符串类型,因此,点图根据字母先后顺序对其进行了排序。当变量是因子型变量时,点图会根据定义好的因子水平顺序对其进行排序。现在,我们想根据变量avg对变量name进行排序。

我们可以借助reorder(name,avg)函数实现这一过程。该命令会先将name转化为因  子,然后,根据avg对其进行排序。为使图形效果更好,我们借助图形主题系统(Theming System)删除垂直网格线,并将水平网格线的线型修改为虚线(见图3-28)。

图3-28 点图,根据平均击球距离对姓名进行了排序

ggplot(tophit, aes(x=avg, y=reorder(name,avg))) +
  geom_point(size=3) +   # 使用更大的点
  theme_bw() +
  theme(panel.grid.major.x = element_blank(),
     panel.grid.minor.x = element_blank(),
     panel.grid.major.y = element_line(colour="grey60", linetype="dashed"))

我们也可以将点图的x轴和y轴互换,互换后,x轴对应于姓名,y轴将对应于数值,如图3-29所示。我们也可以将数据标签旋转60°。

ggplot(tophit, aes(x=reorder(name, avg), y=avg)) +
  geom_point(size=3) + # 使用更大的点
  theme_bw() +
  theme(axis.text.x = element_text(angle=60,hjust=1),
     panel.grid.major.y = element_blank(),
     panel.grid.minor.y = element_blank(),
     panel.grid.major.x = element_line(colour="grey60", linetype="dashed"))

图3-29 x轴对应于名字,y轴对应于变量值的点图

有时候,根据其他变量对样本进行分组很有用。这里我们根据因子lg对样本进行分组,因子lg对应有NLAL两个水平,分别表示国家队(National League)和美国队(American league)。我们依次根据lgavg对变量进行排序。遗憾的是,reorder()函数只能根据一个变量对因子水平进行排序,所以我们只能手动实现上述过程。

# 提取出name变量,依次根据变量lg和avg对其进行排序
nameorder <- tophit$name[order(tophit$lg, tophit$avg)]

# 将name转化为因子,因子水平与nameorder一致
tophit$name <- factor(tophit$name, levels=nameorder)

绘制点图时(见图3-30),我们把lg变量映射到点的颜色属性上。借助geom_segment()函数用“以数据点为端点的线段”代替贯通全图的网格线。注意geom_segment()函数需要设定xyxendyend四个参数:

ggplot(tophit, aes(x=avg, y=name)) +
  geom_segment(aes(yend=name), xend=0, colour="grey50") +
  geom_point(size=3, aes(colour=lg)) +
  scale_colour_brewer(palette="Set1", limits=c("NL","AL")) +
  theme_bw() +
  theme(panel.grid.major.y = element_blank(),  # 删除水平网格线
     legend.position=c(1, 0.55),       # 将图例放置在绘图区域中
     legend.justification=c(1, 0.5))

图3-30 以队为分组变量的火柴杆图

另外一种分组展示数据的方式是分面,如图3-31所示。分面条形图中的条形的堆叠顺序与图3-30中的堆叠顺序有所不同;要修改分面显示的堆叠顺序只有通过调整lg变量的因子水平来实现。

ggplot(tophit, aes(x=avg, y=name)) +
  geom_segment(aes(yend=name), xend=0, colour="grey50") +
  geom_point(size=3, aes(colour=lg)) +
  scale_colour_brewer(palette="Set1", limits=c("NL","AL"), guide=FALSE) +
  theme_bw() +
  theme(panel.grid.major.y = element_blank()) +
  facet_grid(lg ~ ., scales="free_y", space="free_y")

图3-31 以队为分组变量进行分面绘图

更多关于调整因子水平顺序的内容可参见15.8节。关于基于其他变量调整因子水平顺序的细节内容可参见15.9节。

更多关于调整图例位置的内容可参见10.2节。更多关于隐藏网格线的内容,可参见9.6节。


相关图书

R语言编程:基于tidyverse
R语言编程:基于tidyverse
R语言医学多元统计分析
R语言医学多元统计分析
Python与R语言数据科学实践
Python与R语言数据科学实践
R数据挖掘实战
R数据挖掘实战
R语言机器学习实战
R语言机器学习实战
R语言高效能实战:更多数据和更快速度
R语言高效能实战:更多数据和更快速度

相关文章

相关课程