有朋友问什么程度可以说R语言入门,如果你会用计算器,那你已经入门了。
那初级水平呢?如果能熟练使用各种apply,基本可以说是初级以上水平了。
本篇文章就是来介绍一下R语言中各种apply用法。
首先来说下R里面有哪些apply。
R中apply相关函数有不少,有的不常见,比如eapply,就不展开说了。
常见的有apply,lapply,sapply,tapply。
1. apply
查看R语言中apply函数的帮助文档,对apply函数是这样说明的:
Returns a vector or array or list of values obtained by applying a function to margins of an array or matrix.
这句话的意思就是apply会把一个函数同时作用于一个数组或者矩阵的一个margin,然后返回值存在一个向量或者数组中,也就是说把每一个margin作为一个函数的输入,对应一个输出,所有的输出放在一起返回来。
那么这个margin如何理解?margin可以是数组的每一行/每一列。值得注意的是这里的数组未必是2维的,更高维也可以。
一个具体例子,求一个2维数组每一列和每一行的平均值:
> x=array(rnorm(12),c(3,4))
> x
[,1] [,2] [,3] [,4]
[1,] 0.8972996 -0.8049946 -1.870765 -0.5850348
[2,] 0.7341293 -0.3148228 1.023150 -0.2756939
[3,] -2.0388308 -2.7536031 -1.500011 0.9308724
> y=apply(x,1,mean)
> y
[1] -0.5908738 0.2916906 -1.3403932
> y=apply(x,2,mean)
> y
[1] -0.13580066 -1.29114016 -0.78254226 0.02338122apply第一个参数是输入数据。
apply第二个参数,指定是哪一种margin,1对应每一列,2对应每一行。
如果有更高维,以此类推,比如对3维情况:
> x=array(c(1:24),c(2,3,4))
> x
, , 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
> apply(x,1,mean)
[1] 12 13
> apply(x,2,mean)
[1] 10.5 12.5 14.5
> apply(x,3,mean)
[1] 3.5 9.5 15.5 21.5apply第三个参数,指定了具体应用什么函数,上面例子计算mean,其实可以用更简单办法,colMeans和rowMeans函数,那如果是一个自己写的函数呢?看下面例子:
> myFun=function(x){sum(x^2)}
> apply(x,1,myFun)
[1] 5.295192 1.760902 14.855718上面例子计算每一行的平方和。
如果一个函数fun1有多个参数,margin只是这个函数的一个参数,想固定其他参数怎么办?很简单,自己定义一个只有一个参数的新函数fun2,在fun2里面调用fun1,并且对其他参数赋值,然后把fun2传递给apply。
通常情况大家使用apply之后是需要把apply的返回值作为输入在其他代码中使用的,这里尤其重要一点是apply的返回值的维度。上面例子计算每一行/列的mean,使用apply之后返回的都是一个向量,并不会因为apply计算行(列)的mean就会自动返回一个列(行)向量。
下面我们用apply函数来实现一个R的内置函数scale,就是标准化一个数组/矩阵。具体来说,是把每一列先减去这一列的中心,然后除以这一列的标准差。
> x=array(1:20,c(4,5))
> x
[,1] [,2] [,3] [,4] [,5]
[1,] 1 5 9 13 17
[2,] 2 6 10 14 18
[3,] 3 7 11 15 19
[4,] 4 8 12 16 20
> scale(x,center = T,scale = T)
[,1] [,2] [,3] [,4] [,5]
[1,] -1.1618950 -1.1618950 -1.1618950 -1.1618950 -1.1618950
[2,] -0.3872983 -0.3872983 -0.3872983 -0.3872983 -0.3872983
[3,] 0.3872983 0.3872983 0.3872983 0.3872983 0.3872983
[4,] 1.1618950 1.1618950 1.1618950 1.1618950 1.1618950
attr(,"scaled:center")
[1] 2.5 6.5 10.5 14.5 18.5
attr(,"scaled:scale")
[1] 1.290994 1.290994 1.290994 1.290994 1.290994
>
> myScale=function(x){
+ x.Mean=apply(x,2,mean)
+ x.sd=apply(x,2,sd)
+ #注意下面一行代码
+ t((t(x)-x.Mean)/x.sd)
+ }
> myScale(x)
[,1] [,2] [,3] [,4] [,5]
[1,] -1.1618950 -1.1618950 -1.1618950 -1.1618950 -1.1618950
[2,] -0.3872983 -0.3872983 -0.3872983 -0.3872983 -0.3872983
[3,] 0.3872983 0.3872983 0.3872983 0.3872983 0.3872983
[4,] 1.1618950 1.1618950 1.1618950 1.1618950 1.1618950可见myScale于scale函数返回值一样。
在上面例子中,最关键的代码是
t((t(x)-x.Mean)/x.sd)
因为在R语言中,一个矩阵和一个向量运算,向量是作为列向量,与矩阵的每一个元素(从一列开始,而不是第一行)进行运算。所以我们先转置矩阵,变成矩阵的元素按行来与向量的元素运算,然后把结果再转置回去。
2. lapply和sapply
这两个apply函数很像,都是应用于一个vector/list上面,上面的apply是用于一个数组/矩阵。所以通常apply需要三个参数,而lapply/sapply一般需要两个参数,第一个参数是输入数据,第二个是函数。两者的区别在于返回值上面,sapply返回的是一个vector,但是lapply返回的是一个list,见下例:
> sapply(x,function(x) {x^2})
[1] 1 4 9 16 25
> x=c(1:5)
> sapply(x,function(x) {x^2})
[1] 1 4 9 16 25
> lapply(x,function(x) {x^2})
[[1]]
[1] 1
[[2]]
[1] 4
[[3]]
[1] 9
[[4]]
[1] 16
[[5]]
[1] 25
sapply还有更复杂的用法,要使用第三个参数。在上面例子中,sapply应用的函数返回值是一个元素,也就是一个数值,然后sapply把这些数值放在一个向量中返回,但是如果sapply应用的函数返回的不是一个元素呢?这时sapply会把返回值作为一个vector,见下面例子:
> sapply(1:5,function(x) matrix(x,2,2))
[,1] [,2] [,3] [,4] [,5]
[1,] 1 2 3 4 5
[2,] 1 2 3 4 5
[3,] 1 2 3 4 5
[4,] 1 2 3 4 5显然上面例子的返回值并非我们想要的,要处理返回值是数组/矩阵的情况,在sapply里面使用第三个参数simplify:
> sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
, , 1
[,1] [,2]
[1,] 1 1
[2,] 1 1
, , 2
[,1] [,2]
[1,] 2 2
[2,] 2 2
, , 3
[,1] [,2]
[1,] 3 3
[2,] 3 3
, , 4
[,1] [,2]
[1,] 4 4
[2,] 4 4
, , 5
[,1] [,2]
[1,] 5 5
[2,] 5 5
3. tapply
tapply通常也有三个参数,第一个指定输入数据,第二个是指定输入数据如何分组,第三个参数指定在每一个分组内,应用什么函数,所以tapply的功能就是把数据按照某种分组,在每个组内进行某个运算,见下面例子:
> medical.example
patient age treatment
1 1 54.70208 Treatment
2 2 64.91654 Treatment
3 3 51.80260 Treatment
4 4 61.63020 Treatment
5 5 72.79920 Treatment
6 6 59.99898 Treatment
7 7 40.85752 Treatment
8 8 51.00623 Treatment
9 9 64.25137 Treatment
10 10 59.97824 Treatment
11 11 58.19639 Control
12 12 34.42546 Control
13 13 53.11569 Control
14 14 31.19479 Control
15 15 67.27431 Control
16 16 64.89839 Control
17 17 67.29181 Control
18 18 61.83809 Control
19 19 82.79484 Control
20 20 68.93832 Control
> tapply(medical.example$age,medical.example$treatment,mean)
Treatment Control
58.19430 58.99681