12 R矩阵和数组
12.1 R矩阵
矩阵用matrix函数定义,实际存储成一个向量,根据保存的行数和列数对应到矩阵的元素, 存储次序为按列存储。 定义如
<- matrix(11:16, nrow=3, ncol=2); print(A) A
## [,1] [,2]
## [1,] 11 14
## [2,] 12 15
## [3,] 13 16
<- matrix(c(1,-1, 1,1), nrow=2, ncol=2, byrow=TRUE); print(B) B
## [,1] [,2]
## [1,] 1 -1
## [2,] 1 1
matrix()
函数把矩阵元素以一个向量的形式输入,
用nrow
和ncol
规定行数和列数,向量元素填入矩阵的缺省次序是按列填入,
用byrow=TRUE
选项可以转换成按行填入。
用nrow()
和ncol()
函数可以访问矩阵的行数和列数,如
nrow(A)
## [1] 3
ncol(A)
## [1] 2
矩阵有一个dim属性,内容是两个元素的向量,
两个元素分别为矩阵的行数和列数。dim属性可以用dim()
函数访问。如
attributes(A)
## $dim
## [1] 3 2
dim(A)
## [1] 3 2
函数t(A)
返回A
的转置。
12.2 矩阵子集
用A[1,]
取出A的第一行,变成一个普通向量。
用A[,1]
取出A的第一列,变成一个普通向量。
用A[c(1,3),1:2]
取出指定行、列对应的子矩阵。
如
A
## [,1] [,2]
## [1,] 11 14
## [2,] 12 15
## [3,] 13 16
1,] A[
## [1] 11 14
1] A[,
## [1] 11 12 13
c(1,3), 1:2] A[
## [,1] [,2]
## [1,] 11 14
## [2,] 13 16
用colnames()
函数可以给矩阵每列命名,
也可以访问矩阵列名,
用rownames()
函数可以给矩阵每行命名,
也可以访问矩阵行名。如
colnames(A) <- c('X', 'Y')
rownames(A) <- c('a', 'b', 'c')
A
## X Y
## a 11 14
## b 12 15
## c 13 16
矩阵可以有一个dimnames属性,
此属性是两个元素的列表(列表见稍后部分的介绍),
两个元素分别为矩阵的行名字符型向量与列名字符型向量。
如果仅有其中之一,缺失的一个取为NULL
。
有了列名、行名后,矩阵下标可以用字符型向量, 如
'Y'] A[,
## a b c
## 14 15 16
'b',] A[
## X Y
## 12 15
c('a', 'c'), 'Y'] A[
## a c
## 14 16
注意在对矩阵取子集时,
如果取出的子集仅有一行或仅有一列,
结果就不再是矩阵而是变成了R向量,
R向量既不是行向量也不是列向量。
如果想避免这样的规则起作用,
需要在方括号下标中加选项drop=FALSE
,
如
1,drop=FALSE] A[,
## X
## a 11
## b 12
## c 13
取出了A
的第一列,
作为列向量取出,
所谓列向量实际是列数等于1的矩阵。
如果用常量作为下标,
其结果维数是确定的,不会出问题;
如果用表达式作为下标,
则表达式选出零个、一个、多个下标,
结果维数会有不同,
加drop=FALSE
则是安全的做法。
矩阵也可以用逻辑下标取子集,比如
A
## X Y
## a 11 14
## b 12 15
## c 13 16
1]>=12,'Y'] A[A[,
## b c
## 15 16
矩阵本质上是一个向量添加了dim
属性,
实际保存还是保存成一个向量,
其中元素的保存次序是按列填入,
所以,
也可以向对一个向量取子集那样,
仅用一个正整数向量的矩阵取子集。如
A
## X Y
## a 11 14
## b 12 15
## c 13 16
c(1,3,5)] A[
## [1] 11 13 15
为了挑选矩阵的任意元素组成的子集而不是子矩阵, 可以用一个两列的矩阵作为下标, 矩阵的每行的两个元素分别指定一个元素的行号和列号。 如
<- matrix(c(1,1, 2,2, 3,2), ncol=2, byrow=TRUE)
ind A
## X Y
## a 11 14
## b 12 15
## c 13 16
ind
## [,1] [,2]
## [1,] 1 1
## [2,] 2 2
## [3,] 3 2
A[ind]
## [1] 11 15 16
用c(A)
或A[]
返回矩阵A
的所有元素。
如果要修改矩阵A
的所有元素,
可以对A[]
赋值。
对矩阵A
,diag(A)
访问A
的主对角线元素组成的向量。
另外,若x
为正整数值标量,diag(x)
返回x
阶单位阵;
若x
为长度大于1的向量,
diag(x)
返回以x
的元素为主对角线元素的对角矩阵。
12.3 cbind()
和rbind()
函数
若x
是向量,cbind(x)
把x
变成列向量,
即列数为1的矩阵,
rbind(x)
把x
变成行向量。
若x1
, x2
, x3
是等长的向量,
cbind(x1, x2, x3)
把它们看成列向量并在一起组成一个矩阵。
cbind()
的自变量可以同时包含向量与矩阵,向量的长度必须与矩阵行数相等。
如
cbind(c(1,2), c(3,4), c(5,6))
## [,1] [,2] [,3]
## [1,] 1 3 5
## [2,] 2 4 6
cbind(A, c(1,-1,10))
## X Y
## a 11 14 1
## b 12 15 -1
## c 13 16 10
cbind()
的自变量中也允许有标量,
这时此标量被重复使用。
如
cbind(1, c(1,-1,10))
## [,1] [,2]
## [1,] 1 1
## [2,] 1 -1
## [3,] 1 10
rbind()
用法类似,
可以等长的向量看成行向量上下摞在一起,
可以是矩阵与长度等于矩阵列数的向量上下摞在一起,
向量长度为1也可以。
12.4 矩阵运算
12.4.1 四则运算
矩阵可以与标量作四则运算,结果为每个元素进行相应运算,如
A
## X Y
## a 11 14
## b 12 15
## c 13 16
<- A + 2; C1 C1
## X Y
## a 13 16
## b 14 17
## c 15 18
<- A / 2; C2 C2
## X Y
## a 5.5 7.0
## b 6.0 7.5
## c 6.5 8.0
当运算为矩阵乘以一个标量时, 就是线性代数中的矩阵的数乘运算。
两个同形状的矩阵进行加、减运算,
即对应元素相加、相减,
用A + B
,A - B
表示,如
+ C2 C1
## X Y
## a 18.5 23.0
## b 20.0 24.5
## c 21.5 26.0
- C2 C1
## X Y
## a 7.5 9.0
## b 8.0 9.5
## c 8.5 10.0
这就是线性代数中矩阵的加、减运算。
对两个同形状的矩阵,
用*
表示两个矩阵对应元素相乘(注意这不是线性代数中的矩阵乘法),
用/
表示两个矩阵对应元素相除。
如
* C2 C1
## X Y
## a 71.5 112.0
## b 84.0 127.5
## c 97.5 144.0
/ C2 C1
## X Y
## a 2.363636 2.285714
## b 2.333333 2.266667
## c 2.307692 2.250000
12.4.2 矩阵乘法
用%*%
表示矩阵乘法而不是用*
表示,
注意矩阵乘法要求左边的矩阵的列数等于右边的矩阵的行数。
如
A
## X Y
## a 11 14
## b 12 15
## c 13 16
B
## [,1] [,2]
## [1,] 1 -1
## [2,] 1 1
<- A %*% B; C3 C3
## [,1] [,2]
## a 25 3
## b 27 3
## c 29 3
12.4.3 向量与矩阵相乘
矩阵与向量进行乘法运算时, 向量按需要解释成列向量或行向量。 当向量左乘矩阵时,看成行向量; 当向量右乘矩阵时,看成列向量。 如
B
## [,1] [,2]
## [1,] 1 -1
## [2,] 1 1
c(1,1) %*% B
## [,1] [,2]
## [1,] 2 0
%*% c(1,1) B
## [,1]
## [1,] 0
## [2,] 2
c(1,1) %*% B %*% c(1,1)
## [,1]
## [1,] 2
注意矩阵乘法总是给出矩阵结果,
即使此矩阵已经退化为行向量、列向量甚至于退化为标量也是一样。
如果需要,可以用c()
函数把一个矩阵转换成按列拉直的向量。
12.4.4 内积
设x
, y
是两个向量,
计算向量内积,
可以用sum(x*y)
表示。
设\(A\), \(B\)是两个矩阵,
\(A^T B\)是广义的内积,
也称为叉积(crossprod),
结果是一个矩阵,
元素为\(A\)的每列与\(B\)的每列计算内积的结果。
\(A^T B\)在R中可以表示为crossprod(A, B)
,
\(A^T A\)可以表示为crossprod(A)
。
要注意的是,crossprod()
的结果总是矩阵,
所以计算两个向量的内积用sum(x,y)
而不用crossprod(x,y)
。
12.4.5 外积
R向量支持外积运算,
记为%o%
,
结果为矩阵。
x %o% y
的第\(i\)行第\(j\)列元素等于x[i]乘以y[j]。
如
c(1,2,3) %o% c(1, -1)
## [,1] [,2]
## [1,] 1 -1
## [2,] 2 -2
## [3,] 3 -3
这种运算还可以推广到x
的每一元素与y
的每一元素进行其它的某种运算,
而不限于乘积运算,可以用outer(x,y,f)
完成,
其中f
是某种运算,或者接受两个自变量的函数。
12.5 逆矩阵与线性方程组求解
用solve(A)
求A
的逆矩阵,如
solve(B)
## [,1] [,2]
## [1,] 0.5 0.5
## [2,] -0.5 0.5
用solve(A,b)
求解线性方程组\(A x = b\)中的\(x\),
如
solve(B, c(1,2))
## [1] 1.5 0.5
求解了线性方程组 \[ \left(\begin{array}{rr} 1 & -1 \\ 1 & 1 \end{array} \right) x = \left(\begin{array}{r} 1 \\ 2 \end{array} \right) \]
12.6 apply()
函数
apply(A, 2, FUN)
把矩阵A
的每一列分别输入到函数FUN中,
得到对应于每一列的结果,如
<- matrix(c(6,2,3,5,4,1), nrow=3, ncol=2); D D
## [,1] [,2]
## [1,] 6 5
## [2,] 2 4
## [3,] 3 1
apply(D, 2, sum)
## [1] 11 10
apply(A, 1, FUN)
把矩阵A
的每一行分别输入到函数FUN中,
得到与每一行对应的结果,如
apply(D, 1, mean)
## [1] 5.5 3.0 2.0
如果函数FUN返回多个结果,
则apply(A, 2, FUN)
结果为矩阵,
矩阵的每一列是输入矩阵相应列输入到FUN的结果,
结果列数等于A
的列数。如
apply(D, 2, range)
## [,1] [,2]
## [1,] 2 1
## [2,] 6 5
如果函数FUN返回多个结果,
为了对每行计算FUN的结果,
结果存入一个与输入的矩阵行数相同的矩阵,
应该用t(apply(A, 1, FUN))
的形式,
如
t(apply(D, 1, range))
## [,1] [,2]
## [1,] 5 6
## [2,] 2 4
## [3,] 1 3
12.7 多维数组
矩阵是多维数组(array)的特例。
矩阵是\(x_{ij}, i=1,2,\dots,n,\; j=1,2,\dots,m\)这样的两下标数据的存贮格式,
三维数组是\(x_{ijk}, i=1,2,\dots,n,\; j=1,2,\dots,m,\; k=1,2,\dots,p\)这样的三下标数据的存贮格式,
\(s\)维数组则是有\(s\)个下标的数据的存贮格式。
实际上,
给一个向量添加一个dim
属性就可以把它变成多维数组。
多维数组的一般定义语法为
数组名 <- array(数组元素,
dim=c(第一下标个数, 第二下标个数, ..., 第s下标个数))
其中数组元素的填入次序是第一下标变化最快, 第二下标次之, 最后一个下标是变化最慢的。 这种次序称为FORTRAN次序。
下面是一个三维数组定义例子。
<- array(1:24, dim=c(2,3,4)); ara ara
## , , 1
##
## [,1] [,2] [,3]
## [1,] 1 3 5
## [2,] 2 4 6
##
## , , 2
##
## [,1] [,2] [,3]
## [1,] 7 9 11
## [2,] 8 10 12
##
## , , 3
##
## [,1] [,2] [,3]
## [1,] 13 15 17
## [2,] 14 16 18
##
## , , 4
##
## [,1] [,2] [,3]
## [1,] 19 21 23
## [2,] 20 22 24
这样的数组保存了\(x_{ijk}, i=1,2,\; j=1,2,3,\; k=1,2,3,4\)。
三维数组ara可以看成是4个\(2 \times 3\)矩阵。
取出其中一个如ara[,,2]
(取出第二个矩阵)
2] ara[,,
## [,1] [,2] [,3]
## [1,] 7 9 11
## [2,] 8 10 12
多维数组可以利用下标进行一般的子集操作,
比如ara[,2, 2:3]
是\(x_{ijk}, i=1,2,\; j=2,\; k=2,3\)的值,
结果是一个\(2 \times 2\)矩阵:
2,2:3] ara[,
## [,1] [,2]
## [1,] 9 15
## [2,] 10 16
多维数组在取子集时如果某一维下标是标量,
则结果维数会减少,
可以在方括号内用drop=FALSE
选项避免这样的规则发生作用。
类似于矩阵, 多维数组可以用一个矩阵作为下标, 如果是三维数组,矩阵就需要有3列, 四维数组需要用4列矩阵。 下标矩阵的每行对应于一个数组元素。