R入门25招

第一招:下载和安装R

问题:

要把R安装到自己的电脑上。

解决方案:

Windows和OS X用户可以从CRAN(Comprehensive R Archive Network)上下载R。Linux和Unix用户则可以用各自的包管理工具安装R软件包。

Windows

  1. 在浏览器中打开http://www.r-project.org/
  2. 点击“CRAN”。然后会看到按国家排序的镜像站点列表。
  3. 就近选择一个镜像站点。
  4. 点击“Download and Install R”下的“Windows”。
  5. 点击“base”。
  6. 点击下载最新版R(一个.exe文件)的链接。
  7. 下载完成后,双击.exe文件,按照提示安装。

OS X

  1. 在浏览器中打开http://www.r-project.org/
  2. 点击“CRAN”。然后会看到按国家排序的镜像站点列表。
  3. 就近选择一个镜像站点。
  4. 点击“MacOS X”。
  5. 在“Files”中点击最新版R的.pkg文件,下载。
  6. 下载完成后,双击.okg文件,按照提示安装。

Linux或Unix
主要的Linux发行版都有安装R的包。下表中是是一些例子:

发行版

包名称

Ubuntu或Debian

r-base

Red Hat或Fedora

R.i386

Suse

R-base

用系统的包管理器下载并安装R的软件包。这通常都需要root密码或者是sudo权限,或者也可以要系统管理员来安装。

讨论

在Windows和OS X上安装R都是很简单的,因为相应的安装包都是预编译好的。只需要根据安装程序的提示一步步地安装即可。CRAN的网页上也提供了跟安装有关的资源,例如常见问题(FAQ)和针对某些特殊情况的提示(“如何在Windows Vista上安装R?”(“How toinstall R when using Windows Vista?”)),这些都挺有用的。

理论上来说,在Linux或Unix上有两种安装R的方法:安装某个发行版的软件包,或是自己编译源代码。在实际使用中,安装软件包是更好的选择。各个发行版所提供的软件包在安装和随后的升级等方面都更加方便。

在Ubuntu和Debian中,是用apt-get下载和安装R。需要用sudo命令获得必要的权限:

代码

1

$ sudo apt-get install r-base

在Red Hat和Fedora则是用yum:

代码

1

$ sudo yum install R.i386

大部分的系统还提供了更加方便的图形化包管理器。

除了基础包,笔者还建议读者把文档包也装上。例如,在我的Ubuntu机器上,我安装了r-base-html(因为我喜欢阅读带超链接的文档)和r-doc-html两个包,这样就将R的用户手册安装到了本地:

代码

1

$ sudo apt-get install r-base-html r-doc-html

有些Linux的软件库中还有一些CRAN上的R软件包。我没用这些软件包,因为我更喜欢直接从CRAN上下载这些软件包的最新版。

在个别情况下,读者有可能需要自己从源代码编译R:有可能是在使用不被支持的Unix版本,或是在性能和配置方面有特殊的考量。Linux和Unix上的编译过程是非常标准化的。从CRAN镜像站点的首页上下载压缩包,压缩包的名字一般是R-2.12.1.tar.gz,只是其中的2.12.1会被最新的版本号代替。解压这个压缩包,然后根据其中的INSTALL文件的提示进行编译。

扩展阅读

R in a Nutshell一书中对R的下载和安装有更加详细的介绍,包括如何在Windows和OS X上编译R。但有关R的安装的最强秘笈还是CRAN上的R Installation andAdministration,其中介绍了如何在各种各样的操作系统上编译和安装R。

这一招是关于如何安装基础包的。安装CRAN上的其他包则是用install.packages函数。

第2招:获取函数的帮助

问题

要获得安装在自己电脑上的某个函数的帮助信息。

解决方案

用help显示函数的文档:

代码

1

> help(functionname)

用args了解该函数的参数:

代码

1

> args(functionname)

用example察看函数的使用例子:

代码

1

> example(functionname)

讨论

本书中出现了各种各样的函数。在有限的篇幅里不可能面面俱到地介绍各个R函数。笔者强烈建议读者自己阅读感兴趣的函数的帮助文件。帮助文件中所提供的信息是非常有用的。

如果想要了解mean函数,可以这样使用help函数:

代码

1

> help(mean)

R会新开一个窗口显示帮助文档,或者是在控制台中显示帮助文档,具体的显示方式取决于读者所使用的操作系统。help命令的一个快捷方式是?号(问号),然后在后面加上函数的名称:

代码

1

> ?mean

如果只是想知道函数有哪些参数,顺序是怎样安排的,就可以用args函数:

代码

1

2

3

4

5

6

> args(mean)

function (x, ...)

NULL

> args(sd)

function (x, na.rm = FALSE)

NULL

args函数的第一行输出是函数调用的概要。对于mean函数,概要中显示的是其唯一的参数,x,这是一个数值向量。对于sd函数,概要中首先也是一个同样的向量,x,还有另一个名为na.rm的可选参数。(读者可以忽略第二行输出,通常都是NULL。)

大部分函数的文档的末尾都会有例子。在R里面可以直接运行这个例子,演示函数的功能。例如,mean函数的文档中就有几个例子。读者不必自己输入这些例子。用example函数就能看到这些例子的运行结果:

代码

1

2

3

4

5

6

7

8

9

> example(mean)

 

mean> x <- c(0:10, 50)

mean> xm <- mean(x)

mean> c(xm, mean(x, trim = 0.1))

[1] 8.75 5.50

mean > mean(USArrests, trim=0.2)

  Murder  Assault  UrbanPop   Rape

    7.42    167.60     66.20    20.16

用户只需要输入example(mean),R会负责运行文档中的例子,并显示结果。

第3招:查看R的文档

问题

要阅读R附带的文档。

解决方案

执行help.start函数可以看到文档的目录:

代码

> help.start()

这里有链接指向所有已安装的文档。

讨论

R本身都附带了上千页内容丰富的文档。在安装其它的包时,相应的文档也会安装到电脑上。

用help.start函数可以很方便的浏览这些文档,该函数会打开一个浏览器窗口,显示出最顶层的目录,见图1-1。


图1-1 R文档目录

在References部分中有两个链接是特别有用的:

Packages

点击这个链接能看到所有已经安装的包,包括基础包和额外安装的包。点击各个包的名称,就能看到其中的函数和数据集。

Search Engine \& Keyword

这是一个简单的搜索引擎,可以用关键字搜索文档。这里还有一些按主题组织的常见关键字,点击这些关键字能看到与其相关的页面。

扩展阅读

本地计算机上的文档其实跟R项目网站上的文档是一样的,网站上的文档经常会更新。

 第4招:在网上搜索帮助信息

问题

在网络上搜索跟R有关的信息和问题解答。

解决方案

在R中,可以用RSiteSearch函数搜索关键字或短语:

代码>RsiteSearch("key phrase")

如果用浏览器的话,可以用这些网站搜索R相关的信息:

http://rseek.org

这是一个针对跟R有关的网站的Google自定义搜索。

http://stackoverflow.com

Stack Overflow是一个具有搜索功能的Q&A网站,主要是面向一些编程方面的问题,例如数据结构、编码和图形等。

http://stats.stackexchange.com

Stack Exchange上的统计分析(Statistical Analysis)区也是一个有搜索功能的Q&A网站,但这个网站更加偏向于统计,而不是编程。

讨论

RSiteSearch函数会打开一个浏览器窗口,并打开R项目网站的搜索引擎。在这里能看到一个可以继续优化的初始搜索结果。例如,下面这个命令会搜索“canonical correlation”:

代码

> RsiteSearch("canonicalcorrelation")

这非常不方便,不用离开R,就可以完成一些快速的搜索。但是,这个搜索只局限于R的文档和邮件列表的存档。

RSeek.org提供了范围更广的搜索。RSeek利用了Google搜索引擎的强大功能,但专注于跟R相关的网站。所以,用RSeek不会像在一般的Google中那样搜出一大堆无关的的搜索结果。RSeek.org最给力的地方是其搜索结果的组织非常直观方便。


图1-2 RSeek.org的搜索结果

如1-2展示了在RSeek.org中搜索“canonical correlation”的搜索结果。网页的左侧是从各个R网站中搜索出来的内容。网页的右侧的标签就是将这些搜索结果组织成如下几个类:

Introduction,简介

Task Views,任务视图

Support Lists,支持列表

Functions,函数

Books,书

Blogs,博客

Related Tools,相关的工具

例如,点击Introductions标签,就能看到各种教程。Task Views标签中则是跟搜索词有关的Task View。类似的,Functions标签中的链接则是指向相关的R函数。这是一种组织搜索结果的好办法。

Stack Overflow也被称为Q&A网站,意思就是任何人可以在网站上提问,有经验的用户则会回答问题——一个问题通常会有多个答案。读者给答案投票,最好的答案就会逐步地升到顶部。这样就构成了一个能搜索的庞大的Q&A对话数据库。Stack Overflow显然是面向问题的,而且上面的话题通常更侧重于R的程序设计。

Stack Exchange(不是Overflow)有一个专门的Statistical Analysis(统计分析)分区。这里更关注的是统计,而不是编程。所以,如果读者的问题更侧重于一般的统计学而不是R本身的话,可以到这里来提问。

扩展阅读

如果找到了有用的软件包,用install.packages函数就能将软件包安装到电脑上。

第5招:读取表格数据文件

问题

读取含有数据表格的文本文件。

解决方案

用read.table函数,该函数会返回一个数据框(data frame):

代码

> dfrm <-read.table("filename.txt")

讨论

表格数据文件是很常见的。这是一种格式简单的文本文件:

每一行是一条记录

在每条记录中,各个字段之间都用一个字符分给,例如空格、制表符、逗号、分号等

每条记录的字段的数量是一样的

这种格式比固定宽度的格式更加灵活,每个字段的位置不再需要对齐。下面就是一个名为statisticians.txt的表格文件,是用空格符分割字段的:

代码

Fisher R.A. 1890 1962

Pearson Karl 1857 1936

Cox Gertrude 1900 1978

read.table函数就是用来读取这种文件的。缺省情况下,该函数认为数据字段是由空白(空格或制表符)分割的:

代码

> dfrm <-read.table("statisticians.txt")

> print(dfrm)

   V1 V2 V3 V4

1 Fisher R.A. 1890 1962

2 Pearson Karl 1857 1936

如果不是用空白分割符,可以用用sep参数指定分隔符。例如,如果字段分隔符是冒号(:),就可以这样读取:

代码

> dfrm <-read.table("statisticians.txt", sep=":")

从输出中看不出什么问题,但实际上read.table会把姓和名视作factor,而不是字符串。我们可以查看一下这两列的class:

代码

> class(dfrm$V1)

[1] "factor"

为了不让read.table将字符串视为factor,把stringAsFactor参数设为FALSE:

代码

> dfrm <-read.table("statisticians.txt", stringsAsFactor=FALSE)

> class(dfrm$V1)

[1] "character"

现在第一列的类型就是字符了,而不是factor。

如果文件中包含“NA”,read.table函数会认为这个值是缺失的,并将其转化成NA。你自己的数据文件也可以用不同的字符串来表示缺失值,用na.strings参数来指定。例如在SAS传统中,缺失值就是用.表示的。我们可以这样来读取这样的数据:

代码

> dfrm <-read.table("filename.txt", na.strings=".")

我非常喜欢自描述的数据,就是说光从数据文件本身就就能看出它所包含的是什么内容。(用计算机学家的话来说就是,文件包含了它自己的元数据。)read.table有两个特性是支持这个功能的。第一个,数据文件的第一行可以是整个数据的头部,给每一列数据指定一个名字。这一行指定了每一列数据的名称,所使用的分隔符跟数据是一样的。下面是就是带头部信息的数据文件:

代码

lastname firstname born died

Fisher R.A. 1890 1962

Pearson Karl 1857 1936

现在在读取该文件时就要告诉read.table函数这个文件是包含头部的,该函数在构建数据框时就会把这一行数据当作每一列的名称:

代码

> dfrm <-read.table("statisticians.txt", header=TRUE, stringsAsFactor=FALSE)

> print(dfrm)

lastname firstname born died

1 Fisher R.A. 1890 1962

2 Pearson Karl 1857 1936

 

read.table的第二个功能是注释行。所有以井号(#)开头的行都会被忽略,可以把注释放在#符号后:

代码

# 本文件中是一些著名统计学家的信息

# Last edited on 1994-06-18

lastname firstname born died

Fisher R.A. 1890 1962

Pearson Karl 1857 1936

Cox Gertrude 1900 1978

Yates Frank 1902 1994

Smith Kirstine 1878 1939

read.table函数有很多参数来控制它解读输入文件的方法。详见该函数的帮助页面。

扩展阅读

如果数据是逗号分割的,请参阅第6招:读取CSV文件。

 第6招:读取CSV文件

问题

从逗号分割(CSV)的文件中读取数据。

解决方案

用read.csv读取CSV文件。如果CSV文件有头部,就是这样用:

代码

1> tbl <- read.csv("filename")

若CSV文件没有头部,就把header选项设为FALSE:

代码

1> tbl <-read.csv("filename", header=FALSE)

讨论

像R、Excel等各种电子表格程序、很多数据库管理器、大部分的统计软件包都可以导入和导出CSV格式的文件,所以这种格式比较流行。CSV是一种表格数据的文本文件,每一行是一条数据,一条数据中的各个项目用逗号分隔。下面是一个简单的两行三列的CSV文件(第一行是头部,其中包含了各列的名称,也是用逗号分割的):

代码

label,lbound,ubound

low,0,0.674

mid,0.674,1.64

high,1.64,2.33

read.csv函数会读取文件中的数据,并创建一个用于存储数据的数据框(data frame)。在R中,常用数据框来表示表格数据。在默认情况下,该函数认为数据文件的第一行是表头:

代码

> tbl <-read.csv("table-data.csv")

> tbl

label lbound ubound

1 low 0.000 0.674

2 mid 0.674 1.640

3 high 1.640 2.330

read.csv函数会用表头中的内容给数据框中的各列命名。如果文件中没有表头,可以指定header=FALSE,这样R就会按序给各列命名(在这里就是V1、V2和V3):

代码

> tbl <-read.csv("table-data-with-no-header.csv", header=FALSE)

> tbl

V1 V2 V3

1 low 0.000 0.674

2 mid 0.674 1.640

3 high 1.640 2.330

read.csv的一个特性是会自动地将非数值类型的数据视为因子(factor),即分类变量。这通常能满足大部分情况下的需求,毕竟R是一个统计软件,而不是Perl。tbl数据框中的label变量确实是因子,而不是字符。让我们来看看tbl的结构:

代码

> str(tbl)

'data.frame':

3 obs. of 3 variables:

$ label : Factor w/ 3 levels"high","low","mid": 2 3 1

$ lbound: num 0 0.674 1.64

$ ubound: num 0.674 1.64 2.33

但在某些情况下,也有可能确实需要字符串,而不是因子。把as.is参数设为TRUE即可,此时R就不会将分数值数据视为因子:

代码

> tbl <-read.csv("table-data.csv", as.is=TRUE)

> str(tbl)

'data.frame':

3 obs. of 3 variables:

$ label : chr "low""mid" "high"

$ lbound: num 0 0.674 1.64

$ ubound: num 0.674 1.64 2.33

要注意,现在label标量是字符串,而不再是factor。另一个有用的特性是以井号(#)开始的行,即数据文件中的注释,都会被忽略。设置comment.char=”"可以禁用该功能。

read.csv函数还有很多有用的小技巧。例如,跳过输入文件的前若干行、控制每一列数据的转换、过滤较短的行、限制行数以及控制字符串的形式等等。详见R帮助页面。

扩展阅读

请读者参阅read.table的R帮主页面,这是read.csv的基础。写CSV文件可以用write.csv函数。

第7招:创建向量

问题

要创建一个向量(vector)。

解决方案

用c(…)操作符根据给定的值创建向量。

讨论

向量不仅仅是一种数据结构,它还是R的核心部件。向量中可以包含数字、字符串或逻辑值,但不能把这些东西同时放在一个向量中。可以用c(…)操作符将简单的元素构建成向量:

代码

> c(1,1,2,3,5,8,13,21)

[1] 1 1 2 3 5 8 13 21

> c(1*pi, 2*pi, 3*pi, 4*pi)

[1] 3.141593 6.283185 9.424778 12.566371

> c("Everyone","loves", "stats.")

[1] "Everyone" "loves""stats."

> c(TRUE,TRUE,FALSE,TRUE)

[1] TRUE TRUE FALSE TRUE

如果c(…)操作符的参数本身就是向量,那么c(…)就会将这些向量拼接成单独一个向量:

代码

> v1 <- c(1,2,3)

> v2 <- c(4,5,6)

> c(v1,v2)

[1] 1 2 3 4 5 6

向量中不能同时包含多种数据类型,例如不能把数字和字符串放在同一个向量中。如果用多种元素构建向量,R会尝试将其转换成同一种数据类型:

代码

> v1 <- c(1,2,3)

> v3 <-c("A","B","C")

> c(v1,v3)

[1] "1" "2""3" "A" "B" "C"

上面的代码视图用数字和字符串创建向量。在创建向量前,R将所有的数字都转换成了字符串,使得向量中的元素都是同一数据类型。从技术上说,只有两个模式(mode)相同的数据元素才能共存于一个向量中。3.1415的模式是数字,而“foo”的模式则是字符:

代码

> mode(3.1415)

[1] "numeric"

> mode("foo")

[1] "character"

这两种模式是不兼容得。为了将它们放在一个向量中,R将3.1415转换成字符模式,使其跟“foo”兼容:

代码

> c(3.1415, "foo")

[1] "3.1415" "foo"

> mode(c(3.1415, "foo"))

[1] "character"

注意:c是一个泛型操作符,就是说它可以用于各种数据类型,而不仅仅是向量。但它对各种数据类型的处理结果可能会跟读者的想象有差别,所以在用c处理其他数据类型和对象时要注意检查操作的结果。

第8招:计算基本统计量

问题

要计算基本的统计量:均值、中值、标准差、方差、相关系数、协方差

解决方案

使用下面的函数,假设x和y是向量:

代码

mean(x)

median(x)

sd(x)

var(x)

cor(x, y)

cov(x, y)

讨论

我第一次打开R的文档时,就想在其中找到诸如“计算标准差的操作步骤”这样的章节。但我发现,要解决这个重要的问题需要读上一整章的内容。

其实这并没有那么复杂。

用很简单的函数就能计算出标准差等基本统计量。通常,函数的参数就是一个向量,然后函数就会返回计算出来的统计量:

代码

> x <- c(0,1,1,2,3,5,8,13,21,34)

> mean(x)

[1] 8.8

> median(x)

[1] 4

> sd(x)

[1] 11.03328

> var(x)

[1] 121.7333

sd函数计算样本标准差,var计算的是样本方差。

cor和cov函数分别计算的是两个向量间的相关系数和协方差:

代码

> x <- c(0,1,1,2,3,5,8,13,21,34)

> y <- log(x+1)

> cor(x,y)

[1] 0.9068053

> cov(x,y)

[1] 11.49988

这些函数都对NA敏感。只要向量中有NA值,函数就会返回NA,甚至会报错:

代码

> x <- c(0,1,1,2,3,NA)

> mean(x)

[1] NA

> sd(x)

[1] NA

R的这种小心翼翼让人心烦,但确实应该这么做。遇到这种情况一定要小心。数据中的NA是不是会使得统计量没有意义?如果是的,那么R就作对了。如果不是,可以将na.rm参数设为TRUE,R就会忽略NA值:

代码

> x <- c(0,1,1,2,3,NA)

> mean(x, na.rm=TRUE)

[1] 1.4

> sd(x, na.rm=TRUE)

[1] 1.140175

mean和sd函数都能很智能的处理数据框。他们知道数据框中的每一列都是一个不同的变量,因此它们会逐列地计算统计量。下面这个例子计算了一个有三列数据的数据框的基本统计量:

代码

> print(dframe)

small medium big

1 0.6739635 10.526448 99.83624

2 1.5524619 9.205156 100.70852

3 0.3250562 11.427756 99.73202

4 1.2143595 8.533180 98.53608

5 1.3107692 9.763317 100.74444

6 2.1739663 9.806662 98.58961

7 1.6187899 9.150245 100.46707

8 0.8872657 10.058465 99.88068

9 1.9170283 9.182330 100.46724

10 0.7767406 7.949692 100.49814

> mean(dframe)

small

medium

big

1.245040 9.560325 99.946003

> sd(dframe)

small medium big

0.5844025 0.9920281 0.8135498

可以看到,mean和sd都返回了三个值,分别对应数据框中的三列。(技术上来说,是返回了一个有三个元素的向量,名称来自于数据框中每一列的名称。)

var函数也能理解数据框,但是它的处理方法跟mean和sd有些不同。他们计算数据框中每两列间的方差,返回一个方差矩阵:

代码

> var(dframe)

small medium big

small 0.34152627 -0.21516416 -0.04005275

medium -0.21516416 0.98411974 -0.09253855

big -0.04005275 -0.09253855 0.66186326

类似的,如果x是数据框或矩阵,cor(x)会返回相关矩阵,cov会返回协方差矩阵:

代码

> cor(dframe)

small

small 1.00000000

medium -0.37113670

big -0.08424345

> cov(dframe)

small

small 0.34152627

medium -0.21516416

big -0.04005275

medium big

-0.3711367 -0.08424345

1.0000000 -0.11466070

-0.1146607 1.00000000

medium big

-0.21516416 -0.04005275

0.98411974 -0.09253855

-0.09253855 0.66186326

遗憾的是,median函数是无法理解数据框的。要计算数据框中各列的中位值,需要用lapply函数将median函数应用数据框的每一列上。

扩展阅读

第12招中介绍了如何计算均值的可信区间。第15招中介绍了检测相关性的显著性。

 

第9招:用列数据初始化数据框

问题

数据是按列组织的,需要将其放入一个数据框中。

解决方案

如果数据是由若干个向量或因子组成的,可以用data.frame函数将其组装成一个数据框:

代码

> dfrm <- data.frame(v1, v2, v3, f1,f2)

如果数据是包含在列表中的向量或因子,可以用as.data.frame函数:

代码

> dfrm <-as.data.frame(list.of.vectors)

讨论

数据框是由若干个列组成的,每一列对应一个观察的变量(统计意义上的变量,不是编程里面说的变量)。如果数据已经组织成了列的形式,将其组织成数据框就是很方便的事情了。

data.frame函数可以从向量构建出数据框,每个向量就是一个观察变量。假如有两个数值预测变量,一个分类预测变量和一个响应变量。data.frame函数把这些向量组织成数据框:

代码

> dfrm <- data.frame(pred1, pred2,pred3, resp)

> dfrm

pred1 pred2 pred3 resp

1 -2.7528917 -1.40784130 AM 12.57715

2 -0.3626909 0.31286963 AM 21.02418

3 -1.0416039 -0.69685664 PM 18.94694

4 1.2666820 -1.27511434 PM 18.98153

5 0.7806372 -0.27292745 AM 19.59455

6 -1.0832624 0.73383339 AM 20.71605

7 -2.0883305 0.96816822 PM 22.70062

8 -0.7063653 -0.84476203 PM 18.40691

9 -0.8394022 0.31530793 PM 21.00930

10 -0.4966884 -0.08030948 AM 19.31253

注意data.frame函数用变量的名称命名每一列数据。你也可以自己设定每一列的名称:

代码

> dfrm <- data.frame(p1=pred1,p2=pred2, p3=pred3, r=resp)

> dfrm

p1 p2 p3 r

1 -2.7528917 -1.40784130 AM 12.57715

2 -0.3626909 0.31286963 AM 21.02418

3 -1.0416039 -0.69685664 PM 18.94694

.

. (etc.)

.

另外,数据也有可能是以列表中的向量的形式,而不是独立的向量保存的,像这样:

代码

> lst <- list(p1=pred1, p2=pred2,p3=pred3, r=resp)

没问题,可以用as.data.frame函数从列表中创建数据框:

代码

> as.data.frame(lst)

p1 p2 p3 r

1 -2.7528917 -1.40784130 AM 12.57715

2 -0.3626909 0.31286963 AM 21.02418

3 -1.0416039 -0.69685664 PM 18.94694

.

. (etc.)

.

第10招:根据位置从数据框中选择列

问题

根据数据的位置从数据框中选取出若干列。

解决方案

选择一列数据的操作如下:

代码

dfrm[[n]]

这会返回一列数据,即dfrm中的第n列。

要从数据框中选择多列数据并将其包装成新的数据框,用下面的的表达式:

代码

dfrm[n]

这会返回一个仅仅由dfrm的第n列数据构成的数据框。

代码

dfrm[ c(n1, n2,……,nk) ]

这会返回由dfrm的n1、n2、……、nk列构成的数据框。还可以用矩阵下标风格的方式选择一列或多列数据:

代码

dfrm[, n]

返回的是第n列(假设n只含有一个数值)。

代码

dfrm[, c(n1, n2, ..., nk)]

返回的是由n1, n2,…, nk列构成的数据框。

注意,根据你选择的是一列还是多列,矩阵下标风格可以返回两种数据类型(单列或数据框)。

讨论

有很多从数据框中选择列的方法。在你弄明白这其中的逻辑关系之前,你肯定是一头雾水。在你阅读下面的解释时,要注意语法上的细微变化,例如一个逗号,一个双括号之类的小玩意对表达式含义的影响。

然我们来摆弄一下芝加哥地区16个大城市的人口数据:

代码

> suburbs

city county state pop

1 Chicago Cook IL 2853114

2 Kenosha Kenosha WI 90352

3 Aurora Kane IL 171782

4 Elgin Kane IL 94487

5 Gary Lake(IN) IN 102746

6 Joliet Kendall IL 106221

7 Naperville DuPage IL 147779

8 Arlington Heights Cook IL 76031

9 Bolingbrook Will IL 70834

10 Cicero Cook IL 72616

11 Evanston Cook IL 74239

12 Hammond Lake(IN) IN 83048

13 Palatine Cook IL 67232

14 Schaumburg Cook IL 75386

15 Skokie Cook IL 63348

16 Waukegan Lake(IL) IL 91452

用列表的概念可以精确的选中某一列,比如选择第一列:

代码

> suburbs[[1]]

[1] "Chicago" "Kenosha""Aurora" "Elgin"

[5] "Gary" "Joliet""Naperville" "Arlington Heights"

[9] "Bolingbrook""Cicero" "Evanston" "Hammond"

[13] "Palatine""Schaumburg" "Skokie" "Waukegan"

suburbs的第一列是一个向量,所以suburbs[[1]]返回的就是一个向量。如果第一列是一个因子,那我们得到的就是因子。

如果使用单方括号的话,结果就会有些变化了,例如suburbs[1]或suburbs[ c(1,3) ]。得到的依然是所选择的列,但R会将其中的数据包装秤一个数据框。下面的例子返回的第一列就包装在数据框中:

代码

> suburbs[1]

city

1 Chicago

2 Kenosha

3 Aurora

4 Elgin

5 Gary

6 Joliet

7 Naperville

8 Arlington Heights

9 Bolingbrook

10 Cicero

11 Evanston

12 Hammond

13 Palatine

14 Schaumburg

15 Skokie

16 Waukegan

接下来的例子会以数据框的形式返回第一和第三列:

代码

> suburbs[ c(1,4) ]

city pop

1 Chicago 2853114

2 Kenosha 90352

3 Aurora 171782

4 Elgin 94487

5 Gary 102746

6 Joliet 106221

7 Naperville 147779

8 Arlington Heights 76031

9 Bolingbrook 70834

10 Cicero 72616

11 Evanston 74239

12 Hammond 83048

13 Palatine 67232

14 Schaumburg 75386

15 Skokie 63348

16 Waukegan 91452

容易让人困惑的一个重要原因就是suburbs[[1]]和suburbs[1]的差别很小,但两者的结果却大相径庭:

代码

1

suburbs[[1]]

返回的是一列数据。

代码

suburbs[1]

这返回的是一个数据框,其中只有一列数据。这其实是dfrm[ c(n1,n2,...,nk)]的一个特例。因为这里只有一个数字,所以不需要用到c(…)。

 

这里的关键是“一列数据”跟“包含一列数据的一个数据框”是不一样的。第一个表达式返回的是一列数据,是一个向量或因子;第二个表达式返回的是一个数据框,这是不一样的。

在R中还可以用矩阵的概念选择列,我们在解决方案中已经演示过。这里要强调一个比较奇怪的特性:你可能得到的是一列数据,也有可能得到一个数据框,这取决于你用了多少下标。最简单的情况是只有一个下标,那么得到就是一列数据,比如:

代码

> suburbs[,1]

[1] "Chicago" "Kenosha""Aurora" "Elgin"

[5] "Gary" "Joliet""Naperville" "Arlington Heights"

[9] "Bolingbrook""Cicero" "Evanston" "Hammond"

[13] "Palatine""Schaumburg" "Skokie" "Waukegan"

如果是按照矩阵下标的风格使用多个多下标,返回的就是一个数据框:

代码

> suburbs[,c(1,4)]

city pop

1 Chicago 2853114

2 Kenosha 90352

3 Aurora 171782

4 Elgin 94487

5 Gary 102746

6 Joliet 106221

7 Naperville 147779

8 Arlington Heights 76031

9 Bolingbrook 70834

10 Cicero 72616

11 Evanston 74239

12 Hammond 83048

13 Palatine 67232

14 Schaumburg 75386

15 Skokie 63348

16 Waukegan 91452

这导致了一个问题。加入你在老式的R脚本中看到下面这样的表达式:

代码

dfrm[,vec]

这返回的是一列数据还是一个数据框?好吧,这取决于具体情况。如果vec中只含有一个值,得到的就是一列数据;否则,就会得到一个数据框。仅仅从这个语法形式是无法判断最终结果的。

为了避免这个问题,可以在下标中加入drop=FALSE;这强制R将返回的结果转换成数据框:

代码

dfrm[,vec,drop=FALSE]

现在返回的数据结构就不会再模棱两可了,一定会是一个数据框。

总而言之,用矩阵下标从数据框中选择列并不是一个好办法。我建议用前面说到的列表操作符代替这种方式。功能完全一样,但要更加简洁明了。

我以前一直以为这些操作返回的数据类型是一样的,经常为此而在矩阵、向量、数据框之间不断地做强制转换,这下才恍然大悟了。R里面的诡异规则还不少啊。

 

第11招 根据名称选择数据框中的列

问题

根据列的名称从数据框中选择列。

解决方案

下面这个表达式选择单列数据:

代码

dfrm[[ "name" ]]

这会得到一列名为name的数据。

代码

dfrm$name

这句命令的效果是一样的,只是语法形式不同。选择一列或多列数据将其打包成一个数据框,可以用列表表达式:

代码

dfrm[ "name" ]

这条命令选中了一列数据,并将其当如一个数据框对象中。

代码

dfrm[ c("name1","name2", "name3") ]

选择多列数据,并将它们放入一个数据框中。

还可以使用矩阵风格的下标来选择一列或多列数据:

代码

dfrm[, "name"]

可以选择一列数据。

代码

dfrm[, c("name1","name2", ..., "namek")]

可以选择多列数据,并将其包装成数据框。

务必再提醒一下,根据选择的是一列数据还是多列数据,矩阵风格的下标能返回两种不同的数据类型(向量或数据框)。

讨论

数据框中的每一列都必须有名称。如果知道所需的列的名称,那么通过名称选取数据通常都会比根据列的位置选取数据要直观方便。这里介绍的方法跟第十招中介绍的按位置选择数据很类似。区别就是用列的名称替换了列的位置。所有在第十招中的结论也都适用于这里:

dfrm[[ "name" ]] 返回一列数据,而不是数据框。

dfrm[ c("name1","name2", "name3") ] 返回的是数据框,而不是列。

dfrm["name"] 是前一个表达式的特例,返回的是数据框,而不是列。

矩阵风格的下标既可以返回一列数据,也可以返回一个数据框,使用的时候要小心。见第十招中的相关讨论,和drop=FALSE的使用。

这里还有一条新的:

代码

dfrm$name

这跟dfrm[["name" ]]的效果是一模一样的,但输入更方便,也更易读。

扩展阅读

关于列的选择,请参阅第十招中的更多信息。

 


  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值