在介绍了缺失值处理的方法之后,我们可以得到完整的数据集,但在进行数据分析之前,还需要对数据进行整理,下面我们将介绍数据整理的相关知识。
1.数据合并
我们在R语言基础语法教程中介绍了数据合并的一般方法,即利用函数cbind()和rbind()来进行合并,但这只是对数据进行简单的连接,且要求用于合并的数据集有相同的维数,否则R语言将会报错。在处理一些相对复杂的情况时,这两个函数显得不够实用,需要借助其他函数来实现,下面我们介绍更加“智能化”的函数merge(),该函数适用于合并含有共同的行或者列的两个数据集。其函数的基本书写格式为:
代码语言:javascript复制> merge(x,y, by = intersect (names(x), names(y)),by.x =by,
by.y= by, all = FALSE,all.x = all,sort=TRUE,
suffixes=c(".x" ,".y"), incomparables = NULL, ....)
参数介绍:
x, y:用子合并的两个数据框或其他数据对象;
by, by.x, by.y: 指定依据哪些行合并数据框,默认值为x、y中列名相同的列;
all, all.x, all.y:逻辑值,指定x和y的行是否全在输出文件中,默认值为FALSE;
Sort:逻辑值,指定参数by中的列是否需要排序,默认值为TRUE;
Sutlives:字符串向量,指定除参数by小中的列外相同列名的后缀;
Incomparables:指定参数by中哪些单元不进行合并,默认值为NULL。
需要注意的是,函数merge()只能对两个数据对象进行合并,而不能同时合并多个数据对象。下面我们通过实例演练进行详细介绍:
代码语言:javascript复制> a<-matrix(1:10,nrow=5,dimnames=list(c("A","B","c","D","E"),c("x1","x2")))
> b<-matrix(11:19,nrow=3,dimnames=list(c("A","B","E"),c("x1","x2","x3")))
> a
x1 x2
A 1 6
B 2 7
c 3 8
D 4 9
E 5 10
> b
x1 x2 x3
A 11 14 17
B 12 15 18
E 13 16 19
> merge(a,b,all=T)
x1 x2 x3
1 1 6 NA
2 2 7 NA
3 3 8 NA
4 4 9 NA
5 5 10 NA
6 11 14 17
7 12 15 18
8 13 16 19
由于矩阵a和b的行数和列数都不相同,且指定所有数据都要合并,R语言采用列数较多的矩阵b的列名然后再将矩阵合并,用"NA"填补空格位置。
代码语言:javascript复制> c<-matrix(1:8,nrow=4,dimnames=list(c("A","B","D","E"),c("x1","x3")))
> c
x1 x3
A 1 5
B 2 6
D 3 7
E 4 8
> merge(a,c,all=T)
x1 x2 x3
1 1 6 5
2 2 7 6
3 3 8 7
4 4 9 8
5 5 10 NA
> merge(a,c)
x1 x2 x3
1 1 6 5
2 2 7 6
3 3 8 7
4 4 9 8
上述代码中:我们创建一个新的矩阵c,将其与矩阵a合并,当指定所有数据合并时,输出一个5*3的矩阵,其中元素为矩阵a、c按列合并,空格位置用“NA”填补;如果不指定所有数据合并,则去掉含有缺失值的行后输出,
结果为4*3的矩阵。
如果两矩阵中出现相同的行元素,则函数merge还可以输出相同的行:
代码语言:javascript复制> d<-matrix(c(1,2,4,50,6,7,9,100),nrow=4,dimnames=list(c("A","B","D","E"),c("x1","x2")))
> d
x1 x2
A 1 6
B 2 7
D 4 9
E 50 100
> merge(a,d,by=colnames(a))
x1 x2
1 1 6
2 2 7
3 4 9
> merge(a,d,by=colnames(d),all=T)
x1 x2
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
6 50 100
上述代码表示:先创建一个矩阵d,然后合并矩阵a、d,由于两矩阵中有相同的行,如果不指定如果指定所有数据合并,则将相同的行合并输出,结果为3*2的矩阵;如果指定所有数据合并,则将两矩阵中所有行“粘”在一起,去掉相同行后输出,即两矩阵的行求并集后输出,结果为6*2的矩阵。
2.选取子集
有时候我们需要选取数据集中的一部分或者删除部分,这就需要选取子集函数subset()。我们以iris数据集来讲解。
下面是利用数据索引方式选取子集。
代码语言:javascript复制> data(iris)
> head(iris) # 查看iris数据集的前6行。
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
> head(iris[,c(2:4)])# 查看iris数据集的前6行的2~4列。
Sepal.Width Petal.Length Petal.Width
1 3.5 1.4 0.2
2 3.0 1.4 0.2
3 3.2 1.3 0.2
4 3.1 1.5 0.2
5 3.6 1.4 0.2
6 3.9 1.7 0.4
> head(iris[,-c(2:4)]) # 查看iris数据集的前6行,除了2~4列
Sepal.Length Species
1 5.1 setosa
2 4.9 setosa
3 4.7 setosa
4 4.6 setosa
5 5.0 setosa
6 5.4 setosa
> head(iris[2:20,])# 选取iris数据集的2~20行,查看其前6行
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
> head(iris[-c(2:20),]) # 除去iris数据集的2~20行,查看其前6行
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
21 5.4 3.4 1.7 0.2 setosa
22 5.1 3.7 1.5 0.4 setosa
23 4.6 3.6 1.0 0.2 setosa
24 5.1 3.3 1.7 0.5 setosa
25 4.8 3.4 1.9 0.2 setosa
> iris$Sepal.Length<-NULL # 将变量Sepal.Length剔除。
> head(iris)
Sepal.Width Petal.Length Petal.Width Species
1 3.5 1.4 0.2 setosa
2 3.0 1.4 0.2 setosa
3 3.2 1.3 0.2 setosa
4 3.1 1.5 0.2 setosa
5 3.6 1.4 0.2 setosa
6 3.9 1.7 0.4 setosa
上述方法很简单,但很有用,特别对于数据维度比较大的数据。下面我们介绍subset()函数选取子集。
subset(x, subset, select, drop = FALSE, ...)
x是操作的对象,subset是逻辑值,指定需要选取的行或元素,select指定需要选取的列。
代码语言:javascript复制> d1<-subset(iris,Sepal.Length=mean(iris$Sepal.Length),select=-Sepal.Width)
# Sepal.Length 的值必须大于Sepal.Length的均值,并去除Sepal.Width
> head(d1)
Petal.Length Petal.Width Species
1 1.4 0.2 setosa
2 1.4 0.2 setosa
3 1.3 0.2 setosa
4 1.5 0.2 setosa
5 1.4 0.2 setosa
6 1.7 0.4 setosa
> summary(d1)
Petal.Length Petal.Width Species
Min. :1.000 Min. :0.100 setosa :50
1st Qu.:1.600 1st Qu.:0.300 versicolor:50
Median :4.350 Median :1.300 virginica :50
Mean :3.758 Mean :1.199
3rd Qu.:5.100 3rd Qu.:1.800
Max. :6.900 Max. :2.500
还可以通过随机抽样的方法选取子集。在医学统计学或者流行病学里的现场调查、样本选择经常会提到一个词:随机抽样。随机抽样是为了保证各比较组之间均衡性的一个很重要的方法。抽样的函数sample()取到重要的作用。
sample(x, size, replace = FALSE, prob = NULL)
x是指要进行抽样的数据对象;size是一个非负整数,指定抽样的大小,replace是否重复抽样,默认值为FALSE,prob是一个向量,指定元素被抽取的概率权重,默认值为NULL,即概率抽取。
代码语言:javascript复制> sample(1:10) # 从1-10中无重复随机抽取10个数
[1] 7 4 10 6 9 8 3 2 1 5
> sample(1:10,size=12,replace=T)# 从1-10中可重复随机抽取12个数
[1] 5 2 1 5 7 3 7 1 8 8 1 2
> set.seed(1234) # 设置随机种子,个数为1234
> nrow(iris)
[1] 150
> d2<-iris[sample(1:150,size=50),] # iris数据集中的150行观测值中随机抽取50条。
> head(d2)
Sepal.Width Petal.Length Petal.Width Species
18 3.5 1.4 0.3 setosa
93 2.6 4.0 1.2 versicolor
91 2.6 4.4 1.2 versicolor
92 3.0 4.6 1.4 versicolor
126 3.2 6.0 1.8 virginica
149 3.4 5.4 2.3 virginica
3.数据转换
在基础语法教程中介绍了数据、数据和数据框的知识。在实际中有时候我们需要转换数据类型,比如将数据框转换成矩阵等。下面就介绍数据转换。
代码语言:javascript复制> (x<-matrix(1:10,nrow=5)) # 创建矩阵 x
[,1] [,2]
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
> as.vector(x) 将x转换为向量
[1] 1 2 3 4 5 6 7 8 9 10
> (y <- array(1:18, dim=c(3,3,2))) # 创建数组y
, , 1
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
, , 2
[,1] [,2] [,3]
[1,] 10 13 16
[2,] 11 14 17
[3,] 12 15 18
> as.vector(y) # 将数组转化为向量
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
> (z=data.frame(x=c(1:3),y=c(2:4))) # 创建一个数据框z
x y
1 1 2
2 2 3
3 3 4
> unlist(z) # 将z向量化。
x1 x2 x3 y1 y2 y3
1 2 3 2 3 4
上面这些方法与判断数据是什么类型的is.xx()序列函数一样,都是用as.x()序列的函数,其他的都是早葫芦画瓢。我们重点介绍transform(),within(),stack()和unstack()函数。
transform()可以为原数据增加新列变量、改变原列变量的值和删除列变量。函数的基本书写格式为:
代码语言:javascript复制transform(`_data`, ...)
_data表示需要进行操作的R语言数据对象,数据框等。
代码语言:javascript复制> data(iris)
> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
> iris2<-transform(iris,log.slength=log(Sepal.Length))
> # 增加一列log.slength,值为Sepal.Length的log值,不改变原数据
> head(iris2)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species log.slength
1 5.1 3.5 1.4 0.2 setosa 1.629241
2 4.9 3.0 1.4 0.2 setosa 1.589235
3 4.7 3.2 1.3 0.2 setosa 1.547563
4 4.6 3.1 1.5 0.2 setosa 1.526056
5 5.0 3.6 1.4 0.2 setosa 1.609438
6 5.4 3.9 1.7 0.4 setosa 1.686399
> iris3<-transform(iris2,Sepal.Width=NULL) # 删除Sepal.Width
> head(iris3)
Sepal.Length Petal.Length Petal.Width Species log.slength
1 5.1 1.4 0.2 setosa 1.629241
2 4.9 1.4 0.2 setosa 1.589235
3 4.7 1.3 0.2 setosa 1.547563
4 4.6 1.5 0.2 setosa 1.526056
5 5.0 1.4 0.2 setosa 1.609438
6 5.4 1.7 0.4 setosa 1.686399
下面是within的应用。需要注意的是:函数within()中需要将具体指令用花括号括起来,如果指令有多条,每一条之间使用分号隔开;删除数据集中的列变量需要用到函数rm()。此外,该函数还可以对其他类型的数据对象进行操作,这里就不一一介绍,可以自己去了解。
代码语言:javascript复制> iris4<-within(iris,{log.slength=log(Sepal.Length)})
> head(iris4)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species log.slength
1 5.1 3.5 1.4 0.2 setosa 1.629241
2 4.9 3.0 1.4 0.2 setosa 1.589235
3 4.7 3.2 1.3 0.2 setosa 1.547563
4 4.6 3.1 1.5 0.2 setosa 1.526056
5 5.0 3.6 1.4 0.2 setosa 1.609438
6 5.4 3.9 1.7 0.4 setosa 1.686399
> iris5<-within(iris4,{rm(Sepal.Width)})
> head(iris5)
Sepal.Length Petal.Length Petal.Width Species log.slength
1 5.1 1.4 0.2 setosa 1.629241
2 4.9 1.4 0.2 setosa 1.589235
3 4.7 1.3 0.2 setosa 1.547563
4 4.6 1.5 0.2 setosa 1.526056
5 5.0 1.4 0.2 setosa 1.609438
6 5.4 1.7 0.4 setosa 1.686399
使用函数stack()和 unstack()可以对数据框和列表的长、宽格式进行转换,函数stack()用于将数据框或列表转换成两列,分别是数据和对应的列名称;而函数unstack()的作用巧好相反。
代码语言:javascript复制> z
x y
1 1 2
2 2 3
3 3 4
> (zz=stack(z))
values ind
1 1 x
2 2 x
3 3 x
4 2 y
5 3 y
6 4 y
> unstack(zz)
x y
1 1 2
2 2 3
3 3 4