R语言初级教程(15): 矩阵(下篇)

R语言初级教程(15): 矩阵(下篇)

这是最后一篇讲解有关矩阵操作的博客,介绍有关矩阵的函数,主要有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内建函数,当矩阵中没有NANaN时,计算效率非常高。

上述矩阵的行、列计算,还可以使用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学堂”,我将定期更新相关文章。

编辑于 2018-11-06 21:42