【上一篇:82.关于使用for循环时的四种问题及解决方案】
【下一篇:84.关于apply族的lapply/sapply/vapply】
For循环在R中不像在其他语言中那么重要,因为R是一种函数式编程语言。这意味着可以在函数中封装for循环,并调用该函数而不是直接使用for循环。
purrr包提供了很多函数,用来消除对for循环的需要。Base R的apply族函数有类似的功能,但purrr包中的函数更有一致性,也更容易学习。使用purrr函数的目标是允许你将常见的列表操作挑战分解成小部分:1)当你知道如何对list中的一个元素进行操作,purrr函数会自动将相同的操作推广至列表中的每一个元素。2)purrr还可以将复杂问题分解成小部分,每个部分分别解决,可以通过管道符一步一步向最终解决问题迈进。
purrr函数下篇再写,先写写一直以来怯怯地不愿意学习的apply族函数,看一次记不住,学一次还是不懂,以至于直到现在我还在各种for循环。本篇一定要把这些函数弄懂记住会应用。首先从apply开始.....
apply函数
Apply Functions Over Array Margins。
apply(X, MARGIN, FUN, ..., simplify = TRUE)
输入参数
X:输入,array或者matrix。如果输入不是array和matrix,而是一个有着non-null dim值的对象,apply将用as.matrix(适用二维对象,例如data.fame)或as.array()将其强制转换成array。
MARGIN:一个向量,指定FUN函数将被应用到X的哪个维度上。可以是整数:1=行,2=列,c(1,2)=行和列;可以是字符串向量,前提是输入对象X必须有named dimnames(这种情况更多地适用于超过2维的数组)。
FUN: 函数,可以是自定义函数;也可以是环境变量种的函数;也可以是符号(例如:+,%*%),这种情况下,符号必须是反引用的name。
...:传递给FUN的可选参数。这里面的参数不能和apply函数的其他参数有相同的名字,也要注意尽可能避免与MARGIN或FUN部分匹配。如果...中有参数,可以显式写明前三个参数的参数名。
simplify:逻辑值,指示是否结果应该尽可能简化,默认是TRUE。具体在下面的举例说明和总结中详细写。
输出值
Returns a vector or array or list of values obtained by applying a function to margins of an array or matrix.
1)如果每次调用FUN都返回一个长度为n的向量,当n>1时,apply()最终将返回一个维度为c(n, dim(X)[MARGIN])的数组;如果n=1,如果length(MARGIN)=1,apply()最终将返回一个向量,如果length(MARGIN)>1,则将返回一个维度为dim(X)[MARGIN]的数组。
2)如果每次调用FUN返回的向量长度不同,apply()将返回一个长度为prod(dim(X)[MARGIN])的list,如果列表长度>1,dim设置为MARGIN。
3)在所有情况下,在维度被设置之前,结果将被用as.vector()强制转换成一种基本的向量类型,因此因子将被转换成字符向量。
举例说明和总结
1)输入对象类型是array或matrix,之所以可以应用到数据框上是因为数据框可以被强制转成array。apply()函数是for循环的替代,所以其工作原理是(对二维对象):确定在输入对象的哪个维度上操作(按行还是按列)后,循环调用FUN dim(X)[MARGIN]次,每次调用FUN后返回的结果都存到一个list中(simplify=F的时候),最后根据每次返回结果的长度以及设置的simplify参数输出最终的结果。
先创建一个数组用于举例说明:
# 创建一个3 x 8的数组
> (x<-array(1:24,c(3,8)))
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,] 1 4 7 10 13 16 19 22
[2,] 2 5 8 11 14 17 20 23
[3,] 3 6 9 12 15 18 21 24
# 给数组的各个维度命名,1=行,2=列,名字以列表的形式传递进去
> (dimnames(x)<-list(letters[1:3],LETTERS[1:8]))
[[1]]
[1] "a" "b" "c"
[[2]]
[1] "A" "B" "C" "D" "E" "F" "G" "H"
# 得到命名后的数组
> x
A B C D E F G H
a 1 4 7 10 13 16 19 22
b 2 5 8 11 14 17 20 23
c 3 6 9 12 15 18 21 24
2)类似sum(求和)、mean(求平均值)、sd(求标准差)这类的函数。假如在x的"列"上调用sum()函数:每次调用只返回一个值(长度为1的向量),这个向量会被先存到list中。如果simplify=F,直接返回list,反之返回一个as.vector(list)之后的向量。这种思路和82.关于使用for循环时的四种问题及解决方案中"未知长度的输出"一节里描述的一致。
# List比atomic vector的结构复杂。list长度为8,与x的列数一致
> apply(x,2,sum,simplify = F)
$A
[1] 6
$B
[1] 15
$C
[1] 24
$D
[1] 33
$E
[1] 42
$F
[1] 51
$G
[1] 60
$H
[1] 69
# 返回长度为dim(x)[2]=8的向量
> apply(x,2,sum,simplify = T)
A B C D E F G H
6 15 24 33 42 51 60 69
3)类似sort(排序)、quantile(求分位数)、fivenum(求输入数字的五个汇总结果:minimum, lower-hinge, median, upper-hinge, maximum)这类的函数。每次调用将返回长度>1的向量,因此最终将返回维度为c(n,dim(x)[MARGIN])的数组。即最终array的列数由dim(x)[MARGIN]决定,行数由每次调用返回向量的长度决定。
> x
A B C D E F G H
a 1 4 7 10 13 16 19 22
b 2 5 8 11 14 17 20 23
c 3 6 9 12 15 18 21 24
# 在x的"行"上调用sort函数,每次返回排序后的结果存到List中
> apply(x,1,sort,simplify = F)
$a
A B C D E F G H
1 4 7 10 13 16 19 22
$b
A B C D E F G H
2 5 8 11 14 17 20 23
$c
A B C D E F G H
3 6 9 12 15 18 21 24
# 列表中有几个元素,最终返回结果中就有几列,行数由每次调用返回向量的长度决定
> apply(x,1,sort,simplify = T)
a b c
A 1 2 3
B 4 5 6
C 7 8 9
D 10 11 12
E 13 14 15
F 16 17 18
G 19 20 21
H 22 23 24
# 在"行"上求分位数,因为x有3行,所以结果有3列
> apply(x,1,quantile,simplify = T)
a b c
0% 1.00 2.00 3.00
25% 6.25 7.25 8.25
50% 11.50 12.50 13.50
75% 16.75 17.75 18.75
100% 22.00 23.00 24.00
# 在"列"上求分位数,因为x有8列,所以最终结果有8列
> apply(x,2,quantile,simplify = T)
A B C D E F G H
0% 1.0 4.0 7.0 10.0 13.0 16.0 19.0 22.0
25% 1.5 4.5 7.5 10.5 13.5 16.5 19.5 22.5
50% 2.0 5.0 8.0 11.0 14.0 17.0 20.0 23.0
75% 2.5 5.5 8.5 11.5 14.5 17.5 20.5 23.5
100% 3.0 6.0 9.0 12.0 15.0 18.0 21.0 24.0
3)自己编写一个函数用于每次调用输出不同长度的向量。每次调用FUN后返回向量的长度不一,最终返回一个长度为prod(dim(X)[MARGIN])的list,如果列表长度>1,dim设置为MARGIN。
# 自定义randomfun函数
> randomfun <- function(x){num<-sample(x,1); rnorm(num)}
# 最终返回长度为3的列表。
> apply(x,1,randomfun)
$a
[1] 0.021588044 0.154044569 1.007243515 0.721791760
[5] -0.002292179 -1.247562257 -0.127432402 1.590593188
[9] -0.321024620 -1.058287397
$b
[1] -0.52756211 1.78941808 -0.76817645 0.64014366 -0.81748389
[6] -0.11201013 0.24503978 0.74625223 0.33750790 0.83325951
[11] 1.91125363 -2.64110347 1.28993138 1.72833004 -0.56131632
[16] -0.18110910 -0.21715156 -0.33053400 1.24114389 -0.07563756
$c
[1] -1.628538306 0.923867726 -1.128612284 1.804218194
[5] 0.009244434 -0.456690775 1.669793035 0.420872252
[9] -0.454415607
4)传递给FUN的参数不为空的例子:
# x对象的构建和这里的例子见apply()函数的帮助文档即可
> x
col
row x1 x2
a 3 4
b 3 3
c 3 2
d 3 1
e 3 2
f 3 3
g 3 4
h 3 5
# 自定义函数cave:求x对象每行的x1列的平均值和x1、x2的平均值
> cave <- function(x, c1, c2) c(mean(x[c1]), mean(x[c2]))
# x有8行,所以最终结果有8列;cave函数返回一个长度为2的向量,所以最终结果有2行:第一行表示mean(x1),第二行为mean(x1,x2)
> apply(x, 1, cave, c1 = "x1", c2 = c("x1","x2"))
row
a b c d e f g h
[1,] 3.0 3 3.0 3 3.0 3 3.0 3
[2,] 3.5 3 2.5 2 2.5 3 3.5 4
5)当MARGIN=c(1,2)的时候,计算方法是?
> apply(x,c(1,2),quantile,simplify = T)
, , col = x1
row
a b c d e f g h
0% 3 3 3 3 3 3 3 3
25% 3 3 3 3 3 3 3 3
50% 3 3 3 3 3 3 3 3
75% 3 3 3 3 3 3 3 3
100% 3 3 3 3 3 3 3 3
, , col = x2
row
a b c d e f g h
0% 4 3 2 1 2 3 4 5
25% 4 3 2 1 2 3 4 5
50% 4 3 2 1 2 3 4 5
75% 4 3 2 1 2 3 4 5
100% 4 3 2 1 2 3 4 5
下一篇是apply族的其他函数......
【上一篇:82.关于使用for循环时的四种问题及解决方案】
【下一篇:84.关于apply族的lapply/sapply/vapply】
网友评论