R语言初学者指南-第二章

写在前面

嗯~怎么说呢,之前的那本书,我又鸽了,原因有这几个:

  1. 书太厚了
  2. 讲的东西太全面,而且感觉实用性不大,我在实际使用中很多问题这本书解决不了
  3. 说句不该说的,那本书有很大部分与《R语言实战》重合

所以,我又重开了一本书,这本书解决了我最近的一个大问题。这本书我想以记笔记的形式记录下来,以配套的课后习题为主。

废话少说,进入正题

第二章:R中数据输入

2.1 R中的第一步

2.1.1 少量的数据录入

这一部分主要针对在R中输入少量数据的问题,数据见下表,有四个形态参数,每个形态参数有8个观测值:

翼弦 踝骨 体重
59 22.3 31.2 9.5
55 19.7 30.4 13.8
53.5 20.8 30.6 14.8
55 20.3 30.3 15.2
52.5 20.8 30.3 15.5
57.5 21.58 30.8 15.6
53 20.6 32.5 15.6
55 21.5 NA 15.7

具体变量的解释就不多说了,知道是变量就可以了。 对于这种数据量较少的表格,可以一个个输入,不过这种方法很累。比如录入翼弦的前5个数据:

a <- 59
b <- 55
c <- 53.5
d <- 55
e <- 52.5

此时要想看R中的结果,可以输入“a”,之后敲回车(话说这本书写的是真细)。这里就不演示了,之后对于简单的命令,笔记中都不会进行演示。

这种方法不仅累,而且以字母最为变量名容易乱,可以改名,但是没什么卵用,比如这样:

Wing1 <- 59
Wing2 <- 55
Wing3 <- 53.5
Wing4 <- 55
Wing5 <- 52.5 

这本书接下来先以这些变量讲解了一下运算,比如:

sqrt(Wing1)
## [1] 7.681146
2*Wing1
## [1] 118
Wing1+Wing2
## [1] 114

此时,R只是运算了一下,并没将这些结果储存,最好以新变量来储存这些结果:

SQ.wing1 <- sqrt(Wing1)
#加了个圆括号就可以直接出结果
(SQ.wing1 <- sqrt(Wing1))
## [1] 7.681146

2.1.2 用c函数连接数据

对于这个表格,4个参数8个变量,共需要32个变量名,而这是不实际的。可以用c()函数简化一下:

#用c()函数生成了一个长度是8的向量
Wingcrd <- c(59, 55, 53.5, 55, 52.5, 57.5, 53, 55)

这个时候只要输入Wingcrd就可以查看结果:

Wingcrd
## [1] 59.0 55.0 53.5 55.0 52.5 57.5 53.0 55.0

要是想看这个向量的第一个值,可以这样,注意是方括号:

Wingcrd[1]
## [1] 59

如果想看前5个值

Wingcrd[1:5]
## [1] 59.0 55.0 53.5 55.0 52.5

如果想看除了第二个值之外的

Wingcrd[-2]
## [1] 59.0 53.5 55.0 52.5 57.5 53.0 55.0

负号这个用法以后会经常用到。 至于R中内置的函数,比如sum等,可以这么用,

S.win <- sum(Wingcrd)
S.win
## [1] 440.5

接下来,把剩余三个参数也输入进R中,一般来说,R中的变量名最好以大写字母开头,而且要方便记忆。

Tarsus <- c(22.3, 19.7, 20.8, 20.3, 20.8, 21.5, 20.6, 21.5)
Head <- c(31.2, 30.4, 30.6, 30.3, 30.3, 30.8, 32.5, NA)
Wt <- c(9.5, 13.8, 14.8, 15.2, 15.5, 15.6, 15.6, 15.7)

这个时候发现Head向量中有一个缺失值NA(这不废话吗,上面表格中就看见了),此时算Head的和就会出现问题:

sum(Head)
## [1] NA

调用其他函数比如mean什么的也会这样,这个时候可以用“?sum”命令来看看怎么剔除这个缺失值,发现可以这样做:

sum(Head, na.rm = TRUE)
## [1] 216.1

加个na.rm = TRUE就可以了。当然也可以用na.rm = T代替,不过有时候会出错,所以最好还是用“TRUE”。

2.1.3 使用c,cbind和rbind函数结合变量

首先用c()函数连接Wingcrd,Tarsus,Head和Wt变量。

BirdData <- c(Wingcrd, Tarsus, Head, Wt)

这个时候输入这个新变量名可以看到这是一个长度为32的向量:

BirdData
##  [1] 59.0 55.0 53.5 55.0 52.5 57.5 53.0 55.0 22.3 19.7 20.8 20.3 20.8 21.5
## [15] 20.6 21.5 31.2 30.4 30.6 30.3 30.3 30.8 32.5   NA  9.5 13.8 14.8 15.2
## [29] 15.5 15.6 15.6 15.7

但是这个变量并没有区分哪些值输入哪一个变量,为实现这一目的,可以生成另一个长度为32的向量,命名为ID:

Id <- c(1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4)
Id
##  [1] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4

这样也很费时间,可以这样:

Id <- rep(c(1, 2, 3, 4), each = 8)
Id
##  [1] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4

还可以这样:

Id <- rep(1:4, each = 8)
Id
##  [1] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4

还可以这样:

a <- seq(from = 1, to = 4, by = 1)
a
## [1] 1 2 3 4
rep(a, each = 8)
##  [1] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4

至此,这里只是实现了数字的连接,假如想生成一个长度为32的向量“Id“,这个向量包含单词”Wingcrd“8次,”Tarsus“8次等,可以先生成一个名为VarNames的新变量:

VarNames <- c("Wingcrd", "Tarsus", "Head", "Wt")
VarNames
## [1] "Wingcrd" "Tarsus"  "Head"    "Wt"

但是这只是名称,而不是含有数值的变量。接下来再用rep函数来生成需要的向量:

Id2 <- rep(VarNames, each = 8)
Id2
##  [1] "Wingcrd" "Wingcrd" "Wingcrd" "Wingcrd" "Wingcrd" "Wingcrd" "Wingcrd"
##  [8] "Wingcrd" "Tarsus"  "Tarsus"  "Tarsus"  "Tarsus"  "Tarsus"  "Tarsus" 
## [15] "Tarsus"  "Tarsus"  "Head"    "Head"    "Head"    "Head"    "Head"   
## [22] "Head"    "Head"    "Head"    "Wt"      "Wt"      "Wt"      "Wt"     
## [29] "Wt"      "Wt"      "Wt"      "Wt"

注意不能丢掉each,否则会得到:

Id3 <- rep(VarNames,8)
Id3
##  [1] "Wingcrd" "Tarsus"  "Head"    "Wt"      "Wingcrd" "Tarsus"  "Head"   
##  [8] "Wt"      "Wingcrd" "Tarsus"  "Head"    "Wt"      "Wingcrd" "Tarsus" 
## [15] "Head"    "Wt"      "Wingcrd" "Tarsus"  "Head"    "Wt"      "Wingcrd"
## [22] "Tarsus"  "Head"    "Wt"      "Wingcrd" "Tarsus"  "Head"    "Wt"     
## [29] "Wingcrd" "Tarsus"  "Head"    "Wt"

结果差异很明显了。 前面用c()来结合数据,实际上没个乱用,我也不知道这本书为啥会写它,实际应用中主要用cbind函数来进行数据连接。

Z <- cbind(Wingcrd, Tarsus, Head, Wt)
Z
##      Wingcrd Tarsus Head   Wt
## [1,]    59.0   22.3 31.2  9.5
## [2,]    55.0   19.7 30.4 13.8
## [3,]    53.5   20.8 30.6 14.8
## [4,]    55.0   20.3 30.3 15.2
## [5,]    52.5   20.8 30.3 15.5
## [6,]    57.5   21.5 30.8 15.6
## [7,]    53.0   20.6 32.5 15.6
## [8,]    55.0   21.5   NA 15.7

假设需要访问Z的第一列,

Z[,1]
## [1] 59.0 55.0 53.5 55.0 52.5 57.5 53.0 55.0

要访问第二行,

Z[2,]
## Wingcrd  Tarsus    Head      Wt 
##    55.0    19.7    30.4    13.8

方括号中,逗号前面代表行,后面代表列。 当然也可以将这些查询结果储存为新变量,

X <- Z[2,]
Y <- Z[,-3]
W <- Z[,c(-1,-3)]

如果想知道Z的维度,

dim(Z)
## [1] 8 4

也可以将这个结果存储起来,

n <- dim(Z)
n
## [1] 8 4

或者,仅需要储存Z的行数,

nrow <- dim(Z)[1]
nrow
## [1] 8

当然,cbind也可以用rbind来代替,不过就是行表示形态变量而列表示鸟个体,

Z2 <- rbind(Wingcrd, Tarsus, Head, Wt)
Z2
##         [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## Wingcrd 59.0 55.0 53.5 55.0 52.5 57.5 53.0 55.0
## Tarsus  22.3 19.7 20.8 20.3 20.8 21.5 20.6 21.5
## Head    31.2 30.4 30.6 30.3 30.3 30.8 32.5   NA
## Wt       9.5 13.8 14.8 15.2 15.5 15.6 15.6 15.7

2.1.4 使用vector函数结合数据

vector()函数与c()函数类似,可以用来代替c函数。 假如想生成一个长度为8,包含了所有8只鸟的Wingcrd数据的一个向量,可以这么做:

W <- vector(length = 8)
W[1] <- 59
W[2] <- 55
W[3] <- 53.5
W[4] <- 55
W[5] <- 52.5
W[6] <- 57.5
W[7] <- 53
W[8] <- 55
W
## [1] 59.0 55.0 53.5 55.0 52.5 57.5 53.0 55.0

可以看到这是先生成一个长度为8的向量,然后再向其中每个元素赋值的方法。所以在输入第一条命令后直接键入W,会得到一个FALSE的向量,因为R并不知道这个W是啥。

先定义向量长度,这就是vector函数的优点,比如在做循环运算的时候,当然,平常还是用c()比较方便。

和c()函数一样,可以用W[i],W[i:j],W[-i],W[i,j,k]等访问其中元素,i,j,k是具体的数字哈。

2.1.5 使用矩阵结合数据

书上说初学者可以跳过这一节,我是吗?

可能吧~

不过在具体数据分析时,经常会用到矩阵或者数据框的形式,所以,我才不是初学者呢。 回归正题,为了不用向量来显示4个变量Wingcrd,Tarsus,Head和Wt(每个向量长度为8,这在创建矩阵时非常重要,每个向量长度要一致),可以生成一个8×4的矩阵:

Dmat <- matrix(nrow = 8, ncol = 4)
Dmat
##      [,1] [,2] [,3] [,4]
## [1,]   NA   NA   NA   NA
## [2,]   NA   NA   NA   NA
## [3,]   NA   NA   NA   NA
## [4,]   NA   NA   NA   NA
## [5,]   NA   NA   NA   NA
## [6,]   NA   NA   NA   NA
## [7,]   NA   NA   NA   NA
## [8,]   NA   NA   NA   NA

当然此时并没有给矩阵Dmat中的元素赋值,所以元素都是Na。

为了给每个元素赋值,可以这么做:

Dmat[, 1] <- c(59, 55, 53.5, 55, 52.5, 57.5, 53, 55)
Dmat[, 2] <- c(22.3, 19.7, 20.8, 20.3, 20.8, 21.5,
                 20.6, 21.5)
Dmat[, 3] <- c(31.2, 30.4, 30.6, 30.3, 30.3, 30.8,
                 32.5, NA)
Dmat[, 4] <- c(9.5, 13.8, 14.8, 15.2, 15.5, 15.6,
                 15.6, 15.7)
Dmat
##      [,1] [,2] [,3] [,4]
## [1,] 59.0 22.3 31.2  9.5
## [2,] 55.0 19.7 30.4 13.8
## [3,] 53.5 20.8 30.6 14.8
## [4,] 55.0 20.3 30.3 15.2
## [5,] 52.5 20.8 30.3 15.5
## [6,] 57.5 21.5 30.8 15.6
## [7,] 53.0 20.6 32.5 15.6
## [8,] 55.0 21.5   NA 15.7

这是在按Dmat矩阵的每列分别赋值。当然也可以按行来赋值(方括号中的行列[行,列])。

此时可以发现,这个矩阵没有列标签,默认的是[,1],[,2],[,3],[,4]。可以用colnames()函数来给这个矩阵加上列标签,也就是列名:

colnames(Dmat) <- c("Wingcrd", "Tarsus", "Head", "Wt")
Dmat
##      Wingcrd Tarsus Head   Wt
## [1,]    59.0   22.3 31.2  9.5
## [2,]    55.0   19.7 30.4 13.8
## [3,]    53.5   20.8 30.6 14.8
## [4,]    55.0   20.3 30.3 15.2
## [5,]    52.5   20.8 30.3 15.5
## [6,]    57.5   21.5 30.8 15.6
## [7,]    53.0   20.6 32.5 15.6
## [8,]    55.0   21.5   NA 15.7

以此类推,给每个行命名也是可以的,用rownames()函数就可以了。

但是,可以发现,这么给矩阵元素赋值还是比较麻烦,毕竟我们已经有了具体的Wingcrd,Tarsus,Head和Wt这四个向量,那么,可以这么做:

Dmat2 <- as.matrix(cbind(Wingcrd,Tarsus,Head, Wt))
Dmat2
##      Wingcrd Tarsus Head   Wt
## [1,]    59.0   22.3 31.2  9.5
## [2,]    55.0   19.7 30.4 13.8
## [3,]    53.5   20.8 30.6 14.8
## [4,]    55.0   20.3 30.3 15.2
## [5,]    52.5   20.8 30.3 15.5
## [6,]    57.5   21.5 30.8 15.6
## [7,]    53.0   20.6 32.5 15.6
## [8,]    55.0   21.5   NA 15.7

这里就用到了as.matrix()和cbind()函数。

2.1.6 使用data.frame函数结合数据

前面提到了数据框,这一小节就是利用数据框来结合数据。同样,要求也是向量长度必须相同。

Dfrm <- data.frame(WC = Wingcrd, TS = Tarsus,
                HD = Head, W = Wt)
Dfrm
##     WC   TS   HD    W
## 1 59.0 22.3 31.2  9.5
## 2 55.0 19.7 30.4 13.8
## 3 53.5 20.8 30.6 14.8
## 4 55.0 20.3 30.3 15.2
## 5 52.5 20.8 30.3 15.5
## 6 57.5 21.5 30.8 15.6
## 7 53.0 20.6 32.5 15.6
## 8 55.0 21.5   NA 15.7

data.frame()这个函数创建了一个名为Dfrm的数据框。数据框的优点在于可以在不影响原始数据的基础上改变数据,比如可以在这个数据框中结合体重值和体重值的平方根:

Dfrm1 <- data.frame(WC = Wingcrd, TS = Tarsus,
                HD = Head, W = Wt, Wsq=sqrt(Wt))
Dfrm1
##     WC   TS   HD    W      Wsq
## 1 59.0 22.3 31.2  9.5 3.082207
## 2 55.0 19.7 30.4 13.8 3.714835
## 3 53.5 20.8 30.6 14.8 3.847077
## 4 55.0 20.3 30.3 15.2 3.898718
## 5 52.5 20.8 30.3 15.5 3.937004
## 6 57.5 21.5 30.8 15.6 3.949684
## 7 53.0 20.6 32.5 15.6 3.949684
## 8 55.0 21.5   NA 15.7 3.962323

这就相当于增加了一列。

2.1.7 使用list函数结合数据

嗯,书上又说了初学者可以跳过这一节。

就不跳。

书上是这么说list的。假设现在需要一个黑盒子,里面可以放入尽可能多各种各样的变量,一些可能是相关的,一些可能具有相似的维数,一些可能是向量,一些可能是矩阵,一些可能是字符串,这就是list能完成的事。

其实,就一句话,list里面包含所有的数据类型。下面举个例子:

x1 <- c(1,2,3)#数值向量
x2 <- c("a","b","c","d")#字符向量
x3 <- 3#数值
x4 <- matrix(nrow = 2,ncol = 2)#矩阵
x4[,1] <- c(1,2)
x4[,2] <- c(3,4)
Y <- list(x1=x1,x2=x2,x3=x3,x4=x4)

此时键入Y,可以得到:

Y
## $x1
## [1] 1 2 3
## 
## $x2
## [1] "a" "b" "c" "d"
## 
## $x3
## [1] 3
## 
## $x4
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4

也可以通过Y$x1什么的单独访问某个信息。

为什么要介绍list呢,因为在之后的学习中,有一些函数的结果,比如线性回归什么的,这些结果都是存储在list中的。比如应用线性回归模型实现将翅膀长度表示为体重的函数:

M <- lm(WC ~ Wt, data = Dfrm)

如果键入:

names(M)
##  [1] "coefficients"  "residuals"     "effects"       "rank"         
##  [5] "fitted.values" "assign"        "qr"            "df.residual"  
##  [9] "xlevels"       "call"          "terms"         "model"

会得到一堆奇特的输出结果。这时就可以用M$cofficients什么的分别访问。这个M就是个列表。

回到原来的例子。对于前面表2.1中鸟的形态参数,由于其中每一行都代表同一只鸟的数据,所以将其存入一个列表中没啥用。然而,当任务就是生成一个列表,这个列表需要将所有数据放在一个长向量中,还需要另一个向量来识别这些变量(例如ID的作用),同时需要一个8×4的矩阵来表示这些数据,并且还需要一个包含了4中形态参数名称的向量时,可以这么处理:

AllData <- list(BirdData = BirdData, Id = Id, Z = Z,
                VarNames = VarNames)

可以看下结果:

AllData
## $BirdData
##  [1] 59.0 55.0 53.5 55.0 52.5 57.5 53.0 55.0 22.3 19.7 20.8 20.3 20.8 21.5
## [15] 20.6 21.5 31.2 30.4 30.6 30.3 30.3 30.8 32.5   NA  9.5 13.8 14.8 15.2
## [29] 15.5 15.6 15.6 15.7
## 
## $Id
##  [1] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4
## 
## $Z
##      Wingcrd Tarsus Head   Wt
## [1,]    59.0   22.3 31.2  9.5
## [2,]    55.0   19.7 30.4 13.8
## [3,]    53.5   20.8 30.6 14.8
## [4,]    55.0   20.3 30.3 15.2
## [5,]    52.5   20.8 30.3 15.5
## [6,]    57.5   21.5 30.8 15.6
## [7,]    53.0   20.6 32.5 15.6
## [8,]    55.0   21.5   NA 15.7
## 
## $VarNames
## [1] "Wingcrd" "Tarsus"  "Head"    "Wt"

这种数据的存放方法在处理一些数据,比如用某些R包处理代谢组学数据时,极为有用。

2.2数据的载入

前面都是说的数据的输入,少量数据还好,但是大量的数据,这个就不合适了,最好是直接将excel文件什么的直接导入R中。

2.2.1 Excel中的数据载入

2.2.1.1 Excel中的数据准备

这节很重要,因为原始文件中数据的排列方式不仅对向R导入,还对之后的分析至关重要。

为方便,一般将数据排列成样本-变量的形式,也就是说,行代表各种变量列代表各种样本,当然,反过来也行。一般最好以Excel中的第一列来识别样本,第一行作为变量名。举个例子,下图是一组乌贼的性腺指数(GSI)的数据库。 MdTwlD.jpg

2.2.1.2 数据提取到制表符分割的ascii文件

将这个Excel文件另存为文本文件(制表符分割)即txt文件,注意,另存为那里还有别的txt文件格式,别选错了。

2.2.1.3 read.table函数

当制表符分割的ascii文件没有空内容或者包含空格的名称时(想想看,为什么不能有包含空格的名称),就可以将数据载入R中。

#Squid <- read.table(file = "C:/RBook/squidGSI.txt",header = TRUE)

为了不报错,我将这个命令注释掉了。因为我在C盘下,没有这个文件夹和文件。header = TRUE这参数表示第一行包含了标签。

2.2.2 从其他统计程序包中访问数据

其实R不仅能从文本文件也就是txt文件中读出数据,.xls等Excel文件也可以。后面再讲。 同时,也可以从Minitab,SAS,SPSS什么的导入数据。可以键入:

#library(foreign)

然后再查找这个包的帮助文档来解决导入其他数据文件。

2.2.3 访问数据库

嗯~~这部分就不讲了,目前在学数据库,没搞明白呢。因为这个从R中访问数据库,需要一些驱动程序,有些麻烦。

2.3 本章涉及的R函数

自己去整合吧。

习题

  1. 有人通过观察生长在西班牙一些地方的野猪和马鹿得到这个数据,包含了两种生物的肺结核(tuberculosis,Tb)信息,寄生虫Elaphostrongylus cervi的信息,这种寄生虫只会感染马鹿。 在另一些人的研究中,Tb被当做是一个连续变量的函数,动物的长度由lengthCT(CT是cabeza-tronco的缩写,表示头体)表示Tb和Ecervi是由0和1组成的向量,分别代表未发现或发现了Tb和Ecervi的幼虫。下表的前7行给出了马鹿的数据。
农场 月份 年份 性别 LengthClass LengthCT Ecervi Tb
MO 11 00 1 1 75 0 0
MO 07 00 2 1 85 0 0
MO 07 01 2 1 91.6 0 1
MO NA NA 2 1 95 NA NA
LN 09 03 1 1 NA 0 0
SE 09 03 2 1 105.5 0 0
QM 11 02 2 1 106 0 0

使用c()函数创建一个包含了7只动物长度值的变量,再生成一个包含了Tb的变量,包含NA,并求7只动物的平均长度。

  1. 继续习题1中的数据。首先生成一个包含了农场和月份的变量,注意农场是字符串。然后用cbind结合月份、长度和Tb的数据,并将结果储存在变量Boar中,同时确保可以提取Boar的行、列及每个元素。并用dim,nrow和ncol函数确定Boar中动物的数量和变量的数量。

  2. 继续习题1的数据。类似于习题2,使用vector函数集合Tb数据,使用不一样的变量名,如TB2。

  3. 在R中生成下面的矩阵,并确定它的转置矩阵,逆矩阵,同时计算D和它的逆矩阵之乘积(结果将是单位矩阵)。 \[ D=\begin{bmatrix} 1&2&3\\ 4&2&1\\ 2&3&0\\ \end{bmatrix} \]
  4. 继续习题1至3的问题。生成一个包含习题1表中所有数据的数据框,并将长度数据值的平方根加到这个数据框中,再用list函数完成同样的操作,比较它们之间的不同点。

  5. 文件ISIT.xls包含了深海生物发光的数据,准备一个电子数据表,并且把数据提取到ascii文件中,依次使用read.table和scan函数将这些数据载入到R中,使用两个不同的变量来储存数据,比较它们的不同点,使用is.matrix和is.data.frame函数回答这个问题。

  6. 文件Deer.xls包含了习题1讨论的马鹿数据,但也包含其他动物的数据,把需要的数据从Excel提取到Ascii文件中,并将它载入R。

Avatar
Dr.二哈
在读苦逼科研狗

研究方向:脂质营养,业余时间自学R。

相关