这是最后一篇讲解有关矩阵操作的博客,介绍有关矩阵的函数,主要有rowSums()
, colSums()
, rowMeans()
, colMeans()
, apply()
, rbind()
, cbind()
, row()
, col()
, rowsum()
, aggregate()
, sweep()
, max.col()
。
下面通过例子来了解这些函数的用法:
1. 矩阵的行、列计算
我们知道,通过下标索引[i, j]
可以访问矩阵的某一部分,索引如果没有提供意味着“所有行”或“所有列”。来看个例子,比如:
> 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
> mean(x[,3]) ##求第三列的平均值,行索引i没提供,意味着“所有行”
[1] 10.5
> var(x[2,]) ##求第二行的方差,列索引j没提供,意味着“所有列”
[1] 16
在R中,可以用一些特殊的函数来进行矩阵的行、列计算。来看些例子:
> 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
> rowSums(x) ## 行和
[1] 15 18 21 24
> colSums(x) ## 列和
[1] 10 26 42
> rowMeans(x) ## 行平均
[1] 5 6 7 8
> colMeans(x) ## 列平均
[1] 2.5 6.5 10.5
上面四个函数都是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) x*a+b, a=2, b=1)
与x*2+1
效果相同,此处旨在说明如何应用apply()
函数
2. 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
3. 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
4. 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
上述代码将第一行和第三行作为A组,把第二行和第四行作为B组,然后组内每列求和。注意分组向量的长度必须与矩阵行数相同
你也可以用aggregate()
函数获得类似结果:
> aggregate(x, list(group), sum)
Group.1 V1 V2 V3
1 A 4 12 20
2 B 6 14 22
aggregate()
函数的功能很强大,后面讲“数据框”时再详细讲解。
有人就会问“为啥没有列分组求和的操作?”,其实你可以先将矩阵转置,然后行分组求和;这两步就等同于列分组求和。
5. 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()
函数可以实现很多不同操作,这里就不细讲了。
6. 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
R矩阵的最后一部分内容就讲到这。
如若有遗漏,后期将会添加至本博客。
感谢您的阅读!想了解更多有关R语言技巧,请关注我的微信公众号“R语言和Python学堂”,我将定期更新相关文章。
网友评论