§2.3 与数据有关的对象
对象是R中的一个重要的概念. R指令不是直接访问计算机的存储器,而是构造出能够利用某些特定数据的框结构供R访问. 因此,在R中,这种框结构称为对象.
当R运行时,所有的变量、数据、函数及结果都是以对象的形式保存在计算机的活动内存中的,并且都具有相应的名称. 你可以使用一些运算符(如算术、逻辑、比较等)和函数(本身也是对象)来对这些对象进行操作.
这里介绍与数据输入、保存和运算有关的对象,如向量、矩阵、数组和数据框等.
§2.3.1 纯量
纯量是最简单的输入数据的方法,如:
n <- 30; a <- 59.9; b <- -3.8; c <- 3 + 4i name <- "XueYi"; L <- TRUE
这些数据中有整数、实数和复数,还有字条串和逻辑变量等.
对于实数(或复数)可做四则运算,运算规则是先乘除,后加减,幂运算优先,如:
> 1 + 2*3 + 4/5 + 6^2 [1] 43.8
和函数运算,如:
> sqrt(2) [1] 1.414214 > log(10) [1] 2.302585
也可以做逻辑运算.
> 3 == 5 [1] FALSE
变量之间的类型可以相互转换,例如,将数值型变量转换成字符型变量.
> x <- 3 > y <- as.character(x); y [1] "3"
也可以判别变量是否属于某种类型,如:
> is.numeric(y) [1] FALSE
表2.1给出了各种辨别与转换数据对象的函数,其使用方法与上面的例子相同.
表2.1 辨别与转换数据对象的函数
§2.3.2 向量
1. 向量的构成
如果一组数据,如10.4,5.6,3.1,6.4和21.7,可使用c()函数构造成向量.
> x <- c(10.4, 5.6, 3.1, 6.4, 21.7)
c()函数不但能对数量进行连接,也能对向量进行连接,如:
> y <- c(x, 0, x)
构成的向量y有11个分量,其中两边是向量x,中间是零.
也可以使用“:”构造向量,如:
> 1:10 [1] 1 2 3 4 5 6 7 8 9 10 > 10:1 [1] 10 9 8 7 6 5 4 3 2 1
从“:”的左端开始,至右端结束,如果左端<右端,则逐项加1,否则逐项减1. 对向量做逻辑运算,构造逻辑向量,如:
> y <- c(8, 3, 5, 7, 6, 2, 8, 9); y > 5 [1] TRUE FALSE FALSE TRUE TRUE FALSE TRUE TRUE
与逻辑向量有关的有3个函数all(),any()和which(),用例子说明这3个函数的用途.
> all(y > 5) [1] FALSE > any(y > 5) [1] TRUE > which(y > 5) [1] 1 4 5 7 8
第1个函数判断向量中的所有元素是否均大于5,第2个函数判断向量中是否存在大于5的元素,第3个函数判断向量中哪些元素大于5.
在R中,可以使用函数构造具有特定格式的向量,一个是seq()函数,另一个是rep()函数.
seq()函数是产生等距间隔数列的函数,其使用格式为:
seq(from = 1, to = 1, by = ((to - from)/(length.out - 1)), length.out = NULL, along.with = NULL, ...)
参数的名称、取值及意义如表2.2所示.
表2.2 seq()函数中参数的名称、取值及意义
注:by,length.out和along.with3个参数只能输入一项.
请看下面的例子.
> seq(0, 1, length.out = 11) [1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 > seq(1, 9, by = 2) [1] 1 3 5 7 9 > seq(1, 9, by = pi) [1] 1.000000 4.141593 7.283185 > seq(1, 6, by = 3) [1] 1 4 > seq(10) [1] 1 2 3 4 5 6 7 8 9 10 > seq(0, 1, along.with = rnorm(11)) [1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
命令中的rnorm(11)产生11个标准正态分布的随机数.
rep()是重复函数,它可以将某一变量或向量重复若干次,其使用格式为:
rep(x,...)
参数的名称、取值及意义如表2.3所示.
表2.3 rep()函数中附加参数的名称、取值及意义
请看下面的例子.
> rep(1:4, times = 2) [1] 1 2 3 4 1 2 3 4 > rep(1:4, length.out = 10) [1] 1 2 3 4 1 2 3 4 1 2 > rep(1:4, each = 2) [1] 1 1 2 2 3 3 4 4 > rep(1:4, c(1, 2, 2, 3)) [1] 1 2 2 3 3 4 4 4
times为默认参数,rep(1:4,times=2)与rep(1:4,2)的意义是相同的.
2. 向量的下标
如果x为向量,x[i]表示向量x的第i个分量,如:
> x <- c(1, 4, 7); x[2] [1] 4
如果x是长度为n的向量,v为取值在1至n之间的数(允许重复)的向量,则x[v]是向量x中由v所表示的分量构成的向量,如:
> x <- 10:20; x[c(1, 3, 5, 9)] [1] 10 12 14 18 > x[1:5] [1] 10 11 12 13 14 > x[c(1, 2, 3, 2, 1)] [1] 10 11 12 11 10 > c("a", "b", "c")[rep(c(2, 1, 3), times = 3)] [1] "b" "a" "c" "b" "a" "c" "b" "a" "c"
如果x是长度为n的向量,v为取值在−n至−1之间的向量,则x[v]是向量x中去掉v所表示的分量构成的向量,如:
> x <- 10:20; x[-(1:5)] [1] 15 16 17 18 19 20
如果x为一向量,v为与它等长的逻辑向量,则x[v]表示取出所有v为真值的元素,如:
&> x <- c(1, 4, 7); x[x < 5] [1] 1 4
在定义向量时,可以同时给元素加上名字,这个名字就称为字符下标,如:
> (ages <- c(Li = 33, Zhang = 29, Liu = 18)) Li Zhang Liu 33 29 18
在命令中,对赋值命令加括号是为了显示向量ages中的内容.
§2.3.3 因子
统计中的变量有几种重要类别:区间变量、名义变量和有序变量. 区间变量取连续的数值,可以进行求和、平均值等运算. 名义变量和有序变量取离散值,可以用数值代表,也可以是字符型值,其具体数值没有加减乘除的意义,不能用来计算,只能用来分类或计数. 名义变量,如性别、省份、职业;有序变量,如班级、名次.
在R中,使用因子来表示名义变量或有序变量,其中factor()函数是一种定义因子的方法. 它将一个向量转换成因子,其使用格式为:
factor(x = character(), levels, labels = levels, exclude = NA, ordered = is.ordered(x))
参数的名称、取值及意义如表2.4所示.
表2.4 factor()函数中参数的名称、取值及意义
请看下面的例子.
> data <- c(1, 2, 3, 3, 1, 2, 2, 3, 1, 3, 2, 1) > (fdata <- factor(data)) [1] 1 2 3 3 1 2 2 3 1 3 2 1 Levels: 1 2 3 > (rdata <- factor(data, labels=c("I", "II", "III"))) [1] III III III III II III IIII II I Levels: I II III
data为数据向量,factor()将数据转换成因子(fdata). 由于其他的可选参数均为默认值,因而相应的因子与原数据相同,从data中选出不同的值作为因子水平,共有3个水平. 第2个命令增加了可选项labels,这样就可以将默认因子转换成罗马数字.
另一种定义因子的函数是gl()函数,它定义有规律的因子向量,其使用格式为:
gl(n, k, length = n*k, labels = 1:n, ordered = FALSE)
参数的名称、取值及意义如表2.5所示.
表2.5 gl()函数中参数的名称、取值及意义
例如:
> gl(3, 5, labels = paste0("A", 1:3)) [1] A1 A1 A1 A1 A1 A2 A2 A2 A2 A2 A3 A3 A3 A3 A3 Levels: A1 A2 A3 > gl(5, 1, length = 15, labels = paste0("B", 1:5)) [1] B1 B2 B3 B4 B5 B1 B2 B3 B4 B5 B1 B2 B3 B4 B5 Levels: B1 B2 B3 B4 B5
§2.3.4 矩阵
除向量外,矩阵是数据输入和计算的最简单形式.
1. 矩阵的生成
生成矩阵最简单的方法是使用matrix()函数,其使用格式为:
matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)
参数的名称、取值及意义如表2.6所示.
表2.6 matrix()函数中参数的名称、取值及意义
例如:
> mdat <- matrix(c(1,2,3, 11,12,13), nrow = 2, ncol=3, byrow=TRUE, dimnames = list(c("row1", "row2"), c("C.1", "C.2", "C.3"))) > mdat C.1 C.2 C.3 row1 1 2 3 row2 11 12 13 > A <- matrix(1:15, nrow = 3, ncol = 5); A [,1] [,2] [,3] [,4] [,5] [1,] 1 4 7 10 13 [2,] 2 5 8 11 14 [3,] 3 6 9 12 15
注意,下面两种格式与前面的格式是等价的.
> A <- matrix(1:15, nrow = 3) > A <- matrix(1:15, ncol = 5)
还可以生成一个初始空矩阵,随后再赋值,如:
> B <- matrix(nr = 2, nc = 3) > B[1,1] <- 1; B[1,3] <- 0; B[2,2] <- 3; B [,1] [,2] [,3] [1,] 1 NA 0 [2,] NA 3 NA
这里nr是nrow的缩写,nc是ncol的缩写. 第一行的命令构成一个2×3的空矩阵,第二行对矩阵相应的位置赋值,如果没有第一行的命令,第二行的命令将视为错误.
可用dim()函数将向量转换成矩阵,其使用格式为:
dim(x) <- value
参数x为数据(向量),value为表示维数的向量,如:
> X <- 1:12; dim(X) <- c(3, 4); X [,1] [,2] [,3] [,4] [1,] 1 4 7 10 [2,] 2 5 8 11 [3,] 3 6 9 12
表示将向量1:12转换成3×4的矩阵,其中元素按列排列.
可以使用rbind()函数或cbind()函数将向量或矩阵合并成一个矩阵.rbind()函数是按行合并,每个子矩阵需要有相同的列数.cbind()函数是按列合并,每个子矩阵需要有相同的行数,如:
> X1 <- rbind(1:2, 101:102); X1 [,1] [,2] [1,] 1 2 [2,] 101 102 > X2 <- cbind(1:2, 101:102); X2 [,1] [,2] [1,] 1 101 [2,] 2 102 > cbind(X1, X2) [,1] [,2] [,3] [,4] [1,] 1 2 1 101 [2,] 101 102 2 102 > rbind(X1, X2) [,1] [,2] [1,] 1 2 [2,] 101 102 [3,] 1 101 [4,] 2 102
2. 与矩阵运算有关的函数
dim()函数的另一个功能是获取对象(如矩阵、数组等)的维数,如:
> dim(A) [1] 3 5
表示A为3×5阶的矩阵.
除dim()函数外,还有获取矩阵矩阵行数的函数——nrow()函数,以及获取矩阵矩阵列数的函数——ncol()函数,如:
> nrow(A) [1] 3 > ncol(A) [1] 5
上述两个函数对向量运算无效. 如果要对向量做运算,则只需将函数名称改成大写字母即可.
as.vector()函数可以将矩阵或数组强行转换成向量,形象地说,就是将矩阵按列拉直,如:
> as.vector(A) [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
3. 矩阵下标
要访问矩阵的某个元素或为该元素赋值,只要写出矩阵名和方括号中用逗号分开的两个下标即可,如:
> A[1, 2] [1] 4 > A[1, 2] <- 102
矩阵下标可以取正整数(不能超过矩阵的维数),其内容为矩阵下标对应的内容,如:
> A[c(1, 3), 2:4] [,1] [,2] [,3] [1,] 102 7 10 [2,] 6 9 12
也可以取负整数(整数不能超过矩阵的维数),其意义是去掉矩阵中相应的行和(或)列,如:
> A[-3, -2] [,1] [,2] [,3] [,4] [1,] 1 7 10 13 [2,] 201 203 204 205 > A[-1, ] [,1] [,2] [,3] [,4] [,5] [1,] 201 202 203 204 205 [2,] 3 6 9 12 15 > A[, -2] [,1] [,2] [,3] [,4] [1,] 1 7 10 13 [2,] 201 203 204 205 [3,] 3 9 12 15
也可以使用逻辑下标和字符串下标(如果定义了矩阵维的名称),关于这两种下标的使用就不列举了.
如果打算访问矩阵的行,或对矩阵的行赋值,则标出行的下标,而列下标默认. 同样,如果打算访问矩阵的列,或对矩阵的列赋值,则标出列的下标,而行下标默认,如:
> A[c(1, 3),] [,1] [,2] [,3] [,4] [,5] [1,] 1 102 7 10 13 [2,] 3 6 9 12 15 > A[2, ] <- 201:205; A [,1] [,2] [,3] [,4] [,5] [1,] 1 102 7 10 13 [2,] 201 202 203 204 205 [3,] 3 6 9 12 15
§2.3.5 数组
大家对数组并不陌生,实际上,向量是一维数组,矩阵是二维数组,这里所说的数组是指多维数组,当然所介绍的内容也适用于向量和矩阵.
1. 数组的生成
用array()函数生成数组,其使用格式为:
array(data = NA, dim = length(data), dimnames = NULL)
参数的名称、取值及意义如表2.7所示.
表2.7 array()函数中参数的名称、取值及意义
例如:
> X <- array(1:20, dim = c(4, 5)); X [,1] [,2] [,3] [,4] [,5] [1,] 1 5 9 13 17 [2,] 2 6 10 14 18 [3,] 3 7 11 15 19 [4,] 4 8 12 16 20 > Y <- array(1:24, dim = c(3, 4, 2)); Y , , 1 [,1] [,2] [,3] [,4] [1,] 1 4 7 10 [2,] 2 5 8 11 [3,] 3 6 9 12 , , 2 [,1] [,2] [,3] [,4] [1,] 13 16 19 22 [2,] 14 17 20 23 [3,] 15 18 21 24
也可以用dim()构造数组,如:
> Y <- 1:24 > dim(Y) <- c(3, 4, 2)
与刚才命令的结果是相同的.
2. 数组下标
数组与向量和矩阵一样,可以对数组中的某些元素进行访问或运算.
要访问数组的某个元素,只要写出数组名和方括号内的用逗号分开的下标即可,如a[2,1,2]. 例如:
> a <- 1:24 > dim(a) <- c(2, 3, 4) > a[2, 1, 2] [1] 8
更进一步,还可以在每一个下标位置写一个下标向量,表示这一维取出所有指定下标的元素,如a[1,2:3,2:3]取出所有第一维的下标为1,第二维的下标为2~3,第三维的下标为2~3的元素,如:
> a[1, 2:3, 2:3] [,1] [,2] [1,] 9 15 [2,] 11 17
注意,因为第一维只有一个下标,所以数组退化成一个2×2的矩阵.
另外,如果略写某一维的下标,则表示该维全选,如:
> a[1, , ] [,1] [,2] [,3] [,4] [1,] 1 7 13 19 [2,] 3 9 15 21 [3,] 5 11 17 23
取出所有第一维下标为1的元素,得到一个2维数组(3×4的矩阵).
> a[ , 2, ] [,1] [,2] [,3] [,4] [1,] 3 9 15 21 [2,] 4 10 16 22
取出所有第二维下标为2的元素得到一个2×4的矩阵.
> a[1, 1, ] [1] 1 7 13 19
则只能得到一个长度为4的向量.a[,,]或a[]都表示整个数组,如:
> a[] <- 0
可以在不改变数组维数的条件下把元素都赋成0.
§2.3.6 列表
1. 列表的构造
列表是一种特别的对象集合,它的元素也由序号(下标)区分,但是各元素的类型可以是任意对象,不同元素不必是同一类型. 元素本身允许是其他复杂数据类型,如列表的一个元素也允许是列表. 下面是如何构造列表的例子.
> Lst <- list(name = "Fred", wife = "Mary", no.children = 3, child.ages = c(4, 7, 9)) > Lst $name [1] "Fred" $wife [1] "Mary" $no.children [1] 3 $child.ages [1] 4 7 9
列表元素总可以用“列表名[[下标]]”的格式引用,如:
> Lst[[2]] [1] "Mary" > Lst[[4]][2] [1] 7
但是,列表不同于向量,每次只能引用一个元素,如Lst[[1:2]]的用法是不允许的.
注意
“列表名[下标]”或“列表名[下标范围]”的用法也是合法的,但其意义与用两重括号的记法完全不同,两重括号取出列表的一个元素,结果与该元素类型相同,如果使用一重括号,则结果是列表的一个子列表(结果类型仍为列表).
在定义列表时,如果指定了元素的名字(如Lst中的name,wife,no.children,child.ages),则引用列表元素还可以用它的名字作为下标,格式为“列表名[["元素名"]]”,如:
> Lst[["name"]] [1] "Fred" > Lst[["child.age"]] [1] 4 7 9
另一种格式是“列表名$元素名”,如:
> Lst$name [1] "Fred" > Lst$wife [1] "Mary" > Lst$child.ages [1] 4 7 9
2. 列表的修改
列表的元素可以修改,只要把元素引用赋值即可,如将Fred改成John.
> Lst$name <- John"
如果需要增加一项家庭收入,夫妻的收入分别为1980和1600,则输入:
> Lst$income <- c(1980, 1600)
如果要删除列表的某一项,则将该项赋空值(NULL).
几个列表可以用连接函数c()连接起来,结果仍为一个列表,其元素为各自变量的列表元素,如:
> list. ABC <- c(list. A, list. B, list. C)
§2.3.7 数据框
数据框(Dataframe)是R语言的一种数据结构. 它通常是矩阵形式的数据,但矩阵各列可以是不同类型的. 数据框每列为一个变量,每行为一个观测样本.
但是,数据框有更一般的定义. 它是一种特殊的列表对象,有一个值为“data.frame”的class属性,各列表成员必须是向量(数值型、字符型、逻辑型)、因子、数值型矩阵、列表或其他数据框. 向量、因子成员为数据框提供一个变量,非数值型向量会被强制转换为因子,而矩阵、列表、数据框这样的成员为新数据框提供了和其列数、成员数、变量数相同个数的变量. 作为数据框变量的向量、因子或矩阵必须具有相同的长度(行数).
尽管如此,一般还可以把数据框看成是一种推广了的矩阵,它可以用矩阵形式显示,可以用对矩阵的下标引用方法来引用其元素或子集.
1. 数据框的生成
数据框可以用data.frame()函数生成,其用法与list()函数相同,各自变量变成数据框的成分,自变量可以命名,成为变量名,如:
> df <- data.frame( Name = c("Alice", "Becka", "James", "Jeffrey", "John"), Sex = c("F", "F", "M", "M", "M"), Age = c(13, 13, 12, 13, 12), Height = c(56.5, 65.3, 57.3, 62.5, 59.0), Weight = c(84.0, 98.0, 83.0, 84.0, 99.5) ); df Name Sex Age Height Weight 1 Alice F 13 56.5 84.0 2 Becka F 13 65.3 98.0 3 James M 12 57.3 83.0 4 Jeffrey M 13 62.5 84.0 5 John M 12 59.0 99.5
2. 数据框的引用
引用数据框元素的方法与引用矩阵元素的方法相同,可以使用下标或下标向量,也可以使用列名或列名构成的向量,如:
> df[1:2, 3:5] Age Height Weight 1 13 56.5 84 2 13 65.3 98
数据框的各变量也可以按列表引用,即用双括号[[]]或$符号引用,如:
> df[["Height"]] [1] 56.5 65.3 57.3 62.5 59.0 > df$Weight [1] 84.0 98.0 83.0 84.0 99.5
3. 与数据框或列表有关的函数
数据框的主要用途是保存统计建模的数据. R语言的统计建模功能都需要以数据框为输入数据,也可以把数据框当成一种矩阵来处理. 在使用数据框的变量时,可以用“数据框名$变量名”的记法,但是这样使用比较麻烦.
R语言提供了attach()函数,可以把数据框中的变量“连接”到内存中,这样便于数据框数据的调用,如:
> attach(df) > r <- Height/Weight; r [1] 0.6726190 0.6663265 0.6903614 0.7440476 0.5929648
后一语句将在当前工作空间建立一个新变量r,它不会自动进入数据框df中,要把新变量赋值到数据框中,可以用:
> df$r <- Height/Weight
这样的格式.
为了取消连接,只要调用detach()(无参数即可).
注意
R语言中名字空间的管理是比较独特的. 它在运行时保持一个变量搜索路径表,在读取某个变量时,到这个变量搜索路径表中由前向后查找,找到最前的一个;在赋值时,总是在位置1赋值(除非特别指定在其他位置赋值).attach()的默认位置是在变量搜索路径表的位置2,detach()默认也是去掉位置2,所以R编程的一个常见问题是当误用了一个自己并没有赋值的变量时有可能不出错,因为这个变量已在搜索路径中某个位置有定义,这样不利于程序的调试,需要留心这样的问题.
attach()除了可以连接数据框,也可以连接列表.
如果对数据框中的变量只做少量的运算,也可以不使用attach()函数,而使用with()函数,其使用格式为:
with(data, expr, ...)
参数data为数据框,expr为计算表达式. 例如,上一句可改为:
> df$r <- with(df, Height/Weight)