注意:本篇文章是源自以下博客,为了方便查看特意做成一篇博客
参考博客:R语言初级教程(13): 矩阵(上篇)
R语言初级教程(15): 矩阵(下篇)
注:如有侵权,请告之,删之。
序言
R中有5种基本数据结构,分别是向量(vector)、矩阵(matrix)、数组(array)、数据框(data frame)和列表(list)。它们的结构如下图:
注意:其中向量、矩阵和数组中的数据类型必须是相同的;而数据框和列表中的数据类型可以是不一样的。
首先介绍矩阵,矩阵是一个二维数组。这篇博客将主要介绍矩阵的创建、元素的访问以及元素的修改…
1. 创建矩阵
在R中,使用matrix()函数来创建矩阵是最常用的方式。matrix()的原型为:matrix(data=NA, nrow=1, ncol = 1, byrow=FALSE, dimnames=NULL),其中参数的意义分别为:
data:包含了矩阵的元素,一般是个向量,默认情况下是NA
nrow和ncol:设定矩阵的行、列数目;一般这两个值只需设定一个,另外一个值可根据元素个数自动给出
byrow:设定矩阵是按行(byrow=TRUE)填充还是按 列(byrow=FALSE)填充,默认情况下按列填充
dimnames:包含了以字符型向量表示的行名和列名,是一个列表,默认情况下没有行列名
来看些例子:
c <- matrix(1:24, nrow = 3)#默认按列填充
结果:
c = matrix(1:24,byrow = TRUE, nrow = 3)#按行填充
结果:
rnames <- c('R1', 'R2') ##行名
cnames <- c('C1', 'C2', 'C3') ##列名
mat3 <- matrix(1:6, nrow=2, dimnames=list(rnames, cnames)) ##通过设定dimnames参数添加行列名
也可通过使用rownames()、colnames()函数来给矩阵添加行、列名
rownames(mat) <- c("R1", "R2", "R3")
colnames(mat) <- c("C1","C2")
结果:
此外,也可通过使用dim()函数来创建矩阵,其原理是通过改变维度使向量变为矩阵。比如:
mat4 <- 1:6 ##向量
> dim(mat4) <- c(3, 2) ##变为3行2列的矩阵
> is.matrix(mat4) ##判断是否为矩阵
[1] TRUE
> mat4
结果:
> mat4 <- 1:6
> dim(mat4) <- c(2, 3) ##与上面不一样
> mat4 <- t(mat4) ##矩阵转置
> mat4
结果:
2. 矩阵的属性
> mat <- matrix(1:6, nrow=2, dimnames=list(rnames, cnames))
> mat
C1 C2 C3
R1 1 3 5
R2 2 4 6
> class(mat) ##结构类型
[1] "matrix"
> typeof(mat) ##元素数据类型
[1] "integer"
> dim(mat) ##维度,2行3列
[1] 2 3
> length(mat) ##元素个数
[1] 6
> rownames(mat) ##获取行名
[1] "R1" "R2"
> colnames(mat) ##获取列名
[1] "C1" "C2" "C3"
3. 访问矩阵中的元素
类似于向量元素的访问,只是增加了一个维度而已。来看一些例子:
> mat <- matrix(1:9, nrow=3, dimnames=list(c('r1', 'r2', 'r3'), c('c1', 'c2', 'c3')))
> mat
c1 c2 c3
r1 1 4 7
r2 2 5 8
r3 3 6 9
> mat[2, 2] ##访问第2行第2列元素
[1] 5
> mat[2, ] ##访问第2行元素
c1 c2 c3
2 5 8
> is.vector(mat[2, ]) ##返回为向量
[1] TRUE
> mat[, 2] ##访问第2列元素
r1 r2 r3
4 5 6
> mat[c(2, 3), c(2, 3)] ##访问第2、3行第2、3列元素,返回为矩阵
c2 c3
r2 5 8
r3 6 9
### 也可通过行、列名来访问元素
> mat['r2', 'c2']
[1] 5
> mat['r2', ]
c1 c2 c3
2 5 8
> mat[c('r2','r3'), c('c2','c3')]
c2 c3
r2 5 8
r3 6 9
值得注意的是,当用上面的方式获取某个或某行或某列元素时,返回的是一个向量。如何使返回值也为矩阵呢?来看个例子:
> mat[2, 2, drop=FALSE]
c2
r2 5
> mat[2, , drop=FALSE]
c1 c2 c3
r2 2 5 8
> is.matrix(mat[2, 2, drop=FALSE]) ##返回的是个矩阵
[1] TRUE
> is.matrix(mat[2, , drop=FALSE]) ##返回的是个矩阵
[1] TRUE
通过将参数drop设定为FALSE,返回的将会是个矩阵
4. 修改矩阵中的元素
类似于向量,我们可以通过赋值运算来改变矩阵中的内容,比如:
> mat <- matrix(1:9, nrow=3)
> mat
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
> mat[2, 2] <- 20 ##将第2行第2列元素改为20
> mat
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 20 8
[3,] 3 6 9
> mat[ ,3] <- 10 ##将第3列元素都改为10
> mat
[,1] [,2] [,3]
[1,] 1 4 10
[2,] 2 20 10
[3,] 3 6 10
> mat[mat<6] <- 7 ##将小于6的元素都改为7
> mat
[,1] [,2] [,3]
[1,] 7 7 10
[2,] 7 20 10
[3,] 7 6 10
> mat <- mat[-2, ] ##删掉第二行
> mat
[,1] [,2] [,3]
[1,] 7 7 10
[2,] 7 6 10
另外,通过rbind()和cbind()函数可添加行和列,来看个例子:
> mat <- matrix(1:9, nrow=3)
> mat
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
> rbind(mat, c(12, 13, 14)) ##在原矩阵mat后面添加一行
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
[4,] 12 13 14
> cbind(mat, c(12, 13, 14)) ##在原矩阵mat后面添加一列
[,1] [,2] [,3] [,4]
[1,] 1 4 7 12
[2,] 2 5 8 13
[3,] 3 6 9 14
5、矩阵的行、列计算
我们知道,通过下标索引[i, j]可以访问矩阵的某一部分,索引如果没有提供意味着“所有行”或“所有列”。来看个例子,比如:
x <- matrix(1:12, ncol=3)
![Refused](https://img-blog.csdnimg.cn/20200324220653370.png)
> mean(x[,3]) ##求第三列的平均值,行索引i没提供,意味着“所有行”
[1] 10.5
> var(x[2,]) ##求第二行的方差,列索引j没提供,意味着“所有列”
在R中,可以用一些特殊的函数来进行矩阵的行、列计算。来看些例子:
> x <- matrix(1:12, ncol=3)
rowSums(x) ## 行和
colSums(x) ## 列和
rowMeans(x) ## 行平均
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200324221103564.png)
colMeans(x) ## 列平均
注意:上面四个函数都是R内建函数,当矩阵中没有NA和NaN时,计算效率非常高。
上述矩阵的行、列计算,还可以使用apply()函数来实现。apply()函数的原型为apply(X, MARGIN, FUN, …),其中:X为矩阵或数组;MARGIN用来指定是对行运算还是对列运算,MARGIN=1表示对行运算,MARGIN=2表示对列运算;FUN用来指定运算函数;…用来指定FUN中需要的其它参数。来看些例子:
用apply()函数来实现上面的例子
> x <- matrix(1:12, ncol=3)
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
> apply(x, 1, sum) ## 行和
[1] 15 18 21 24
> apply(x, 2, sum) ## 列和
[1] 10 26 42
> apply(x, 1, mean) ## 行平均
[1] 5 6 7 8
> apply(x, 2, mean) ## 列平均
[1] 2.5 6.5 10.5
apply()函数功能很强大,我们可以对矩阵的行或列进行其它运算,例如:
> apply(x, 2, var) ##每列方差
[1] 1.666667 1.666667 1.666667
> apply(x, 1, max) ##每行最大值
[1] 9 10 11 12
如果矩阵存在NA值,可通过设置na.rm=TRUE来忽略NA值,然后再计算。比如:
> x <- matrix(c(1:5,NA, 7:12), ncol=3)
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 NA 10
[3,] 3 7 11
[4,] 4 8 12
> apply(x, 1, mean)
[1] 5 NA 7 8
> apply(x, 1, mean, na.rm=TRUE)
[1] 5 6 7 8
其中上面的na.rm参数来自mean()函数
甚至我们还可以自定义运算函数,来看个例子:
> x <- matrix(c(1:5,NA, 7:12), ncol=3)
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 NA 10
[3,] 3 7 11
[4,] 4 8 12
> apply(x, 2, function(x, a, b) x*a+b, a=2, b=1) ##自定义函数
[,1] [,2] [,3]
[1,] 3 11 19
[2,] 5 NA 21
[3,] 7 15 23
[4,] 9 17 25
> x*2+1
[,1] [,2] [,3]
[1,] 3 11 19
[2,] 5 NA 21
[3,] 7 15 23
[4,] 9 17 25
注意:apply(x, 2, function(x, a, b) xa+b, a=2, b=1)与x2+1效果相同,此处旨在说明如何应用apply()函数
6.rbind()和cbind()函数
在R中,rbind()和cbind()函数可分别为矩阵添加行和列,来看一个例子:
> x <- matrix(1:12, ncol=3)
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
> x <- rbind(x, apply(x, 2, mean)) ##添加一行,元素分别为每列平均值
> x
[,1] [,2] [,3]
[1,] 1.0 5.0 9.0
[2,] 2.0 6.0 10.0
[3,] 3.0 7.0 11.0
[4,] 4.0 8.0 12.0
[5,] 2.5 6.5 10.5
> x <- cbind(x, apply(x, 1, sum)) ##添加一列,元素分别为每行求和值
> x
[,1] [,2] [,3] [,4]
[1,] 1.0 5.0 9.0 15.0
[2,] 2.0 6.0 10.0 18.0
[3,] 3.0 7.0 11.0 21.0
[4,] 4.0 8.0 12.0 24.0
[5,] 2.5 6.5 10.5 19.5
> rownames(x) <- c(1:4, 'mean') ## 添加行名
> colnames(x) <- c(1:3, 'sum') ## 添加列名
> x
1 2 3 sum
1 1.0 5.0 9.0 15.0
2 2.0 6.0 10.0 18.0
3 3.0 7.0 11.0 21.0
4 4.0 8.0 12.0 24.0
mean 2.5 6.5 10.5 19.5
7.row()和col()函数
在R中,row()和col()函数将分别返回元素的行和列下标矩阵,来看个例子:
> x <- matrix(1:12, ncol=3)
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
> row(x) ##返回元素的行下标矩阵
[,1] [,2] [,3]
[1,] 1 1 1
[2,] 2 2 2
[3,] 3 3 3
[4,] 4 4 4
> col(x) ##返回元素的列下标矩阵
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 1 2 3
[3,] 1 2 3
[4,] 1 2 3
通过这两个函数,可以获取矩阵的对角元素以及上下三角矩阵,例如:
> x <- matrix(1:12, ncol=3)
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
> dx <- x[row(x)==col(x)] ## 获取对角元素
> dx
[1] 1 6 11
> diag(x) ##也可通过diag()函数获取对角元素,速度将更快、更简单
[1] 1 6 11
> x[row(x)>col(x)] <- 0 ##结果为上三角矩阵,通过赋值运算将所有下三角元素变为0
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 0 6 10
[3,] 0 0 11
[4,] 0 0 0
8.rowsum()和aggregate()函数
有时,你可能需要对每行进行分组,然后组内每列求和。在R中可以用rowsum()函数来解决,而且效率也非常高。先看个例子:
> x <- matrix(1:12, ncol=3)
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
> group <- c('A', 'B', 'A', 'B') ##分组向量
> rowsum(x, group) ##组内每列求和
[,1] [,2] [,3]
A 4 12 20
B 6 14 22
你也可以用aggregate()函数获得类似结果:
> aggregate(x, list(group), sum)
Group.1 V1 V2 V3
1 A 4 12 20
2 B 6 14 22
aggregate()函数的功能很强大。
有人就会问“为啥没有列分组求和的操作?”,其实你可以先将矩阵转置,然后行分组求和;这两步就等同于列分组求和。
9. sweep()函数
sweep()函数的原型为sweep(x, MARGIN, STATS, FUN = “-”, check.margin = TRUE, …),其中:x为矩阵或数组;MARGIN用来指定是对行运算还是对列运算,MARGIN=1表示对行运算,MARGIN=2表示对列运算;STATS表示想要清除的统计量;FUN用来指定运算函数,默认为减法-;check.margin用来核实x的维度是否与STATS的匹配,如果事先知道它们匹配的话,将其设为FALSE将提高运算速度; …用来指定FUN中需要的其它参数。来看些例子:
> x <- matrix(1:12, ncol=3)
> x
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
> cols <- apply(x, 2, mean) ##列平均
> cols
[1] 2.5 6.5 10.5
> sweep(x, 2, cols) ##每列减去其平均值
[,1] [,2] [,3]
[1,] -1.5 -1.5 -1.5
[2,] -0.5 -0.5 -0.5
[3,] 0.5 0.5 0.5
[4,] 1.5 1.5 1.5
> sweep(x, 2, cols, '+') ##每列加上其平均值
[,1] [,2] [,3]
[1,] 3.5 11.5 19.5
[2,] 4.5 12.5 20.5
[3,] 5.5 13.5 21.5
[4,] 6.5 14.5 22.5
> sweep(x, 1, 1:4) ##每行减去对应值,比如第一行元素都减1,第二行减2,第三行减3,第四行减4
[,1] [,2] [,3]
[1,] 0 4 8
[2,] 0 4 8
[3,] 0 4 8
[4,] 0 4 8
从上面的例子可以看出,sweep()函数的功能非常强,它可以对矩阵的行或列减去(默认情况)或加上不同的值
事实上,通过改变FUN参数的具体形式或自定义函数,sweep()函数可以实现很多不同操作,这里就不细讲了。
10. max.col()函数
max.col()函数返回矩阵每行最大值所在的列位置(即列下标),其原型为max.col(m, ties.method = c(“random”, “first”, “last”)),其中:m为矩阵;当存在多个最大值时,ties.method指定用哪种方式来处理这种情况,默认为"random"(随机),"first"指使用第一个最大值,"last"指使用最后一个最大值。来看个官网例子:
> set.seed(1) ##通过设定随机数种子,使下面的结果可重复
> mm <- rbind(x = round(2*stats::runif(12)),
y = round(5*stats::runif(12)),
z = round(8*stats::runif(12)))
> mm
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
x 1 1 1 2 0 2 2 1 1 0 0 0
y 3 2 4 2 4 5 2 4 5 1 3 1
z 2 3 0 3 7 3 4 5 4 1 7 5
> max.col(mm) ##random
[1] 4 6 5
> max.col(mm) ##random,跟上面的结果不一样
[1] 6 6 5
> max.col(mm, 'first')
[1] 4 6 5
> max.col(mm, 'last')
[1] 7 9 11
我们也可以结合apply()和which.max()函数来实现max.col(mm, ‘first’)。看个例子,
> apply(mm, 1, which.max)
x y z
4 6 5
> unname(apply(mm, 1, which.max)) ##通过unname函数去掉向量的名称
[1] 4 6 5