2.2 数据对象
在正式开始学习R之前,我们需要明白,R是一种基于对象的语言:对象是指可以赋值给变量的任何事物,包括常量、数据结构、函数甚至是图形。R中有多种用于存储数据的对象类型,包括向量、矩阵、数组、数据框和列表。
2.2.1 向量
向量是用于存储数值型、字符型或者逻辑型数据的一维数组,是所有R进行数据分析的基础数据结构之一。在R中,没有对标量类型的正式定义,标量只是长度为1的向量,所以向量是最基础也是最常用的数据类型。由于组成元素的不同,向量可以分为数值向量、逻辑向量和字复向量。
我们可以使用函数c()可以用来创建不同类型的向量:
<- c(1,2,3,4,5)
x <- c("a","b","c")
y <- c(TRUE,FALSE) z
这里,x是数值向量,y是字符型,z是逻辑型。
上面这段代码有两点需要注意的:
在同一个向量中,只能包含相同的数据类型(数值型、字符型、逻辑型)
在R中,尽量使用“<-”进行赋值,而不是“=”(有些函数会将=解释为判断)
通过“[]”,我们可以访问向量中指定的元素,x[]表示对x向量中的元素进行操作。接下来我们通过一些代码示例对前面生成的向量来进行演示:
3] x[
## [1] 3
1:3] x[
## [1] 1 2 3
c(1,3)] y[
## [1] "a" "c"
第二个语句中的冒号用于生成一个数值序列,例如,x<-c(1:5)等价于x<-c(1,2,3,4,5)。
2.2.2 矩阵
矩阵是一个二维数组,也可以说是一个二维向量,所以在矩阵中同样只能保存相同类型的数据。在R中一般使用matrix()函数来创建矩阵,创建矩阵的方式有两种:
<- matrix(1:16,nrow=4,ncol=4)
m1 m1
## [,1] [,2] [,3] [,4]
## [1,] 1 5 9 13
## [2,] 2 6 10 14
## [3,] 3 7 11 15
## [4,] 4 8 12 16
<-c(1:16)
x<-matrix(x,nrow = 4,byrow = TRUE)
m2 m2
## [,1] [,2] [,3] [,4]
## [1,] 1 2 3 4
## [2,] 5 6 7 8
## [3,] 9 10 11 12
## [4,] 13 14 15 16
我们使用两种方式创建了相同的两个4*4矩阵。在第二种方式中,byrow参数控制的是是否按列填充,当byrow=FALSE时,m2将成为m1的转置矩阵。有时,我们需要创建对角矩阵,R中提供diag()函数完成这个操作:
diag(1,nrow=4)
## [,1] [,2] [,3] [,4]
## [1,] 1 0 0 0
## [2,] 0 1 0 0
## [3,] 0 0 1 0
## [4,] 0 0 0 1
我们可以使用“[]”来对矩阵中的元素进行访问。x[i,j]指矩阵中的第i行,第j列;x[i,]会访问第i行的所有元素;x[,j]则会访问矩阵中第j列的所有元素:
<- matrix(1:16,nrow=4,ncol=4)
m1 2,2] m1[
## [1] 6
2,] m1[
## [1] 2 6 10 14
2] m1[,
## [1] 5 6 7 8
首先,我们创建了4*4的矩阵,默认按列填充。然后我们访问了矩阵中第2行、第2列的元素;接着,我们又分别访问了第2行和第二列中的所有元素。
在默认情况下,创建矩阵时不会自动分配行列名。但是在使用矩阵时,我们有时会希望对行和列进行命名,以赋予行列不同的含义。我们可以在创建矩阵时通过matrix()中的dimnames参数为行列进行命名:
matrix(1:16,nrow=4,ncol=4,
dimnames = list(c("r1","r2","r3","r4"),
c("n1","n2","n3","n4")))
## n1 n2 n3 n4
## r1 1 5 9 13
## r2 2 6 10 14
## r3 3 7 11 15
## r4 4 8 12 16
我们也可以在矩阵创建完成后,在对其行列进行命名:
<- matrix(1:16,nrow=4,ncol=4)
m1 rownames(m1)<-c("r1","r2","r3","r4")
colnames(m1)<-c("n1","n2","n3","n4")
m1
## n1 n2 n3 n4
## r1 1 5 9 13
## r2 2 6 10 14
## r3 3 7 11 15
## r4 4 8 12 16
矩阵运算符的使用
矩阵本质上来说还是向量,所以所有适用于向量的算术运算符也适用于矩阵。这些算术运算符在元素层面上进行运算。另外还有一些矩阵专用的运算符,例如矩阵乘法%*%。
+ m1 m1
## n1 n2 n3 n4
## r1 2 10 18 26
## r2 4 12 20 28
## r3 6 14 22 30
## r4 8 16 24 32
- 2*m1 m1
## n1 n2 n3 n4
## r1 -1 -5 -9 -13
## r2 -2 -6 -10 -14
## r3 -3 -7 -11 -15
## r4 -4 -8 -12 -16
* 2 m1
## n1 n2 n3 n4
## r1 2 10 18 26
## r2 4 12 20 28
## r3 6 14 22 30
## r4 8 16 24 32
/ m1 m1
## n1 n2 n3 n4
## r1 1 1 1 1
## r2 1 1 1 1
## r3 1 1 1 1
## r4 1 1 1 1
^ 2 m1
## n1 n2 n3 n4
## r1 1 25 81 169
## r2 4 36 100 196
## r3 9 49 121 225
## r4 16 64 144 256
%*% m1 m1
## n1 n2 n3 n4
## r1 90 202 314 426
## r2 100 228 356 484
## r3 110 254 398 542
## r4 120 280 440 600
在R语言中可以通过cbind()和rbind()两个函数来实现两个矩阵的列、行合并;但需要注意的是,在进行行、列的合并时,需要确保两个矩阵的行、列的数量是相同的。比如:
<- matrix(1:16,nrow=4,ncol=4)
m1 <- matrix(x,nrow = 4,byrow = TRUE)
m2 cbind(m1,m2)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 1 5 9 13 1 2 3 4
## [2,] 2 6 10 14 5 6 7 8
## [3,] 3 7 11 15 9 10 11 12
## [4,] 4 8 12 16 13 14 15 16
2.2.3 数组
数组是向量和矩阵的自然推广,是由三维或三维以上的数据构成的。本质上来说,数组仍然是一个向量,所以数组依然具有向量的性质,只能存储相同的数据类型。在R语言中,我们可以通过array()函数来创建数组:
<- array(vector, dim, dimnames) myarray
其中,vector包含了数组中的数据,dim是一个数值型向量,指定了数组的不同维度,dimnames是一个列表,可以指定不同维度的名称,例如:
<-array(1:12,dim=c(2,3,2),
arr1dimnames=list(c("a","b"),
c("d","e","f"),
c("g","h")))
arr1
## , , g
##
## d e f
## a 1 3 5
## b 2 4 6
##
## , , h
##
## d e f
## a 7 9 11
## b 8 10 12
对于已经存在的数组,可以使用dimnames()函数为数组的各个维度命名,例如:
dimnames(arr1)<-list(c("a1","a2"),
c("b1","b2","b3"),
c("c1","c2"))
同样的,我们也可以使用“[]”来对数组中的元素进行操作,例如arr1[1,2,2]为9。这里需要注意,在[1,2,2]这个坐标中:1为行坐标,第一个2为列坐标,第二个2则为第三维的坐标。
读到这里,细心的读者可以发现,向量、矩阵和数组的基本特征和基本操作几乎完全相同,而且它们都只能存储相同的数据类型,我们可以称它们为同质数据类。在R中,还存在异质数据类,即可以存储不同类型的数据。异质数据类存储更加灵活,但是在存储效率和运行效率上不如同质数据类。
2.2.4 数据框
在R语言中,数据框(dataframe)的数据结构与矩阵相似,但是其各列的数据类型可以不相同。一般情况,数据框的每列是一个变量,每行是一个观测样本。虽然,数据框内不同的列可以是不同的数据模式,但是数据框内每列的长度必须相同。
通常我们需要处理的数据集都会包含多种数据类型,如下表所示,此时此数据集无法写入矩阵或者数组,在这种情况下数据框是最佳选择。所以,数据框是R语言中最常处理的数据结构。
ID | Male | Name | Birthdate | Major |
---|---|---|---|---|
11 | 男 | 张三 | 12-29 | 工程管理 |
12 | 女 | 李四 | 5-6 | 工商管理 |
13 | 男 | 王五 | 8-8 | 国际贸易 |
在R语言中,我们可以调用data.frame()函数来创建数据框:
<- data.frame(col1,col2,col3) df
其中,列向量col可以为任何类型(字符型、数值型或者逻辑型),每一列的名称可由函数names()指定。或者,我们也可以在数据框创建完毕后使用colnames()和rownames()函数对其进行重命名:
<- data.frame(ID=c(404,405,406),
student Male=c("男","女","男"),
Name=c("张三","李四","王五"),
Birthdate=c("12-29","5-6","8-8"),
Major=c("工程管理","工商管理","国际贸易"))
student
## ID Male Name Birthdate Major
## 1 404 男 张三 12-29 工程管理
## 2 405 女 李四 5-6 工商管理
## 3 406 男 王五 8-8 国际贸易
rownames(student) <- letters[1:3]
数据框可以存储不同的数据类型,但是每一列的数据模式必须是唯一的。在进行数据分析时,我们可以以列为单位进行处理。最后一行代码的letters会自动生成abc的字母序列。
选取数据框中元素的方式有多种。我们任然可以使用”[]“对数据框中的数据进行选取,亦可以直接指定列名,还可以使用$符号,它的作用是选取给定数据框中的某个特定向量。
1:2] student[
## ID Male
## a 404 男
## b 405 女
## c 406 男
"Name"] student[
## Name
## a 张三
## b 李四
## c 王五
$ID student
## [1] 404 405 406
上述操作默认提取的是数据框的列变量,即student[1:2]会提取数据框的第1和第2列。如果我们想要进行行操作,可以仿照对矩阵的操作形式,使用student[1,],这样我们提取出来的是student数据框的第一行,且返回值是一个数据框。 但是我们使用这种方式提取列变量时,则需要设置drop参数,写成student[,1,drop=FALSE]这种形式,否则将会返回一个向量。
赋值
我们可以使用$或者“[]”结合赋值符号“<-”对列表中的成分进行重新赋值,例如:
$ID <- c("1","2","3")
student"Name"] <- c("a","b","c") student[
因子
数据框会默认的使用更有效率的利用内存的方式来存储数据,在存储的过程中,数据的类型会发生一些改变,我们可以使用str()函数进行查看:
str(student)
## 'data.frame': 3 obs. of 5 variables:
## $ ID : chr "1" "2" "3"
## $ Male : chr "男" "女" "男"
## $ Name : chr "a" "b" "c"
## $ Birthdate: chr "12-29" "5-6" "8-8"
## $ Major : chr "工程管理" "工商管理" "国际贸易"
如我们所看到的,字符向量会在数据框中转换为因子,相同的字符只存储一次,以节省内存。因子会带有水平(level)属性,水平指字符所有唯一取值的集合。例如,性别一列中,level为2,只有男和女两个水平。
这种存储方式提高了内存的利用效率,但是,也会产生一些问题:
1,"Name"] <- "d"
student[ student
## ID Male Name Birthdate Major
## a 1 男 d 12-29 工程管理
## b 2 女 b 5-6 工商管理
## c 3 男 c 8-8 国际贸易
这里会产生一个警告,这是因为在最初设定Name这一列时,相应的水平集合中没有“d”这个水平。这时我们就无法赋予它一个水平集合中不存在的名称。R是一种十分占用内存的语言,所以这种设定在R语言中有其合理之处,但是有时也会产生一些问题。我们可以通过设定data.frame()中的stringsAsFactors参数来禁止它转换成因子:
<- data.frame(ID=c(404,405,406),
student Male=c("男","女","男"),
Name=c("张三","李四","王五"),
Birthdate=c("1994-12-29","1993-5-6","1996-8-8"),
Major=c("工程管理","工商管理","国际贸易"),
stringsAsFactors = FALSE)
str(student)
## 'data.frame': 3 obs. of 5 variables:
## $ ID : num 404 405 406
## $ Male : chr "男" "女" "男"
## $ Name : chr "张三" "李四" "王五"
## $ Birthdate: chr "1994-12-29" "1993-5-6" "1996-8-8"
## $ Major : chr "工程管理" "工商管理" "国际贸易"
2.2.5 列表
列表(list)是R中最为复杂的一种数据结构。列表可以理解为广义的向量,是一些对象的有序集合,他可以包含各种类型的对象,甚至是其它列表。在R中,可以使用list()函数创建列表:
<- list(object1,object2,...) mylist
其中的对象可以是向量、矩阵、数组、数据框和列表。在创建列表的同时,你还可以为列表中的对象命名,当然你也可以在创建完之后使用names()函数对它进行重命名:
<- list(name1 = object1,name2 = object2,...) mylist
创建列表
<- "my list"
a <- c(1:5)
b <- matrix(1:10,nrow = 5)
c <- c("x1","x2","x3")
d <- list(a,b,c,d)
list1 names(list1) <- c("r1","r2","r3","r4")
list1
## $r1
## [1] "my list"
##
## $r2
## [1] 1 2 3 4 5
##
## $r3
## [,1] [,2]
## [1,] 1 6
## [2,] 2 7
## [3,] 3 8
## [4,] 4 9
## [5,] 5 10
##
## $r4
## [1] "x1" "x2" "x3"
提取列表中的元素有几种不同的方式,最常用的方法是使用美元符号$,通过成分(向量、矩阵等)的名称来提取列表中的成分。在提取出成分来之后,例如向量,还可以通过对向量的操作提取其中的元素:
$r1 list1
## [1] "my list"
$r2[1] list1
## [1] 1
还可以使用“[]”来访问列表中的元素。但是需要注意list1[2]返回的仍然是一个列表,list[[2]]返回的是列表中第2个元素的成分,及向量b。另外,在列表中还可以使用名称直接访问列表中的成分,list1[[2]]和list1[[“r2”]]是等价的。
2] list1[
## $r2
## [1] 1 2 3 4 5
2]] list1[[
## [1] 1 2 3 4 5
"r2"]] list1[[
## [1] 1 2 3 4 5
由于列表存储数据的灵活性,它成为了R语言中非常重要的数据结构。列表允许以一种简单的方式组织和重新调用不相干的信息;另外,在R语言中,许多函数的运行结果是以列表的形式返回的。列表中的数据可以灵活的访问和提取,所以,列表成为了R语言中非常重要的数据结构。