R: 矩阵运算及常用函数 III - *apply Part I

来源:互联网 发布:手机视频制作软件 编辑:程序博客网 时间:2024/06/06 08:29
在”矩阵运算及常用函数I“里已经提到过,apply系列函数“主要用于某维度上某函数/方法的批量应用”,可以避免“控制流循环带来的高错误率以及漫长的响应时间”。

为了比较响应时间,我们可以先做一个简单的测试:为一列数据做一一系列幂的变形,依据指数的sequence,生成一系列的新数据。然后用system.time()来测试运行的时间(如何解读system.time()结果)。
test1. for循环
> power1 <- function(var = EuStockMarkets[,1]){
+   new.var <- NULL
+   for(i in seq(.001, .999, .001)){
+     if(is.null(new.var)){
+       new.var <- var^i
+     }else{
+       new.var <- data.frame(new.var, var^i)
+     }
+  }

+  return(new.var)
+ }
> system.time(power1())
   user  system elapsed 
   1.06    0.00    1.08 
这里elapsed说明调用和运行程序到整体执行完一共花费的时间,这段循环的function花费了1.08秒

test2. sapply
> power2<-function(i)EuStockMarkets[,1]^i
> system.time(sapply(seq(.001,.999,.001),power2))
   user  system elapsed
   0.42    0.00    0.42 
这个地方, 当我们使用sapply进行处理的时候,总共消耗了0.42秒,比for循环快了一倍多,虽然只快了不到一秒,但是执行效率有相当大幅度的提升。当处理的维度变得原来越多的时候(比如for的循环嵌套),apply函数的优势就会更明显。

-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-

在help system中搜索apply
> ??apply

可以看到在{base}中自带的跟apply相关的函数有7个:
base::applyApply Functions Over Array Marginsbase::byApply a Function to a Data Frame Split by Factorsbase::eapplyApply a Function Over Values in an Environmentbase::lapplyApply a Function over a List or Vectorbase::mapplyApply a Function to Multiple List or Vector Argumentsbase::rapplyRecursively Apply a Function to a Listbase::tapplyApply a Function Over a Ragged Array

其中,lapply下还有不同形式的函数: sapply, vapply, replicate, simplify2array。这样一来,*apply一族里一共有11个可用函数。接下来我们打乱顺序学习。

-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-

1. apply(X, MARGIN, FUN, ...)
对数组按指定维度以指定函数进行计算。

参数:
X: 一个数组,包括矩阵。
MARGIN: 可以是单一的数字,也可以是一个vector指定X的维度,同样,也可以是包含dimension names的字符向量。在一个2维数组中,1代表行,2代表列,c(1,2)则是同时应用FUN到行和列上。
FUN: 将要被应用的函数。
...: 如果FUN需要明确一些参数,则把参数转移到...处。

> apply(mtcars, 2, mean)[1:5]     'calculate the column mean
       mpg        cyl       disp         hp       drat 
 20.090625   6.187500 230.721875 146.687500   3.596563 

当然以上效果也可以直接通过colMeans(mtcars)来实现,这里只是提供一个使用思路。

当数组只有2维时MARGIN = c(1,2)对部分函数来说是无效的,如apply(mtcars, c(1,2), mean)得到的还是mtcars这个数组本身。但是维度达到3以上,MARGIN就会又更明显的作用。

> z <- array(1:24, dim = 2:4)
> zseq <- apply(z, 1:2, max)
> zseq
     [,1] [,2] [,3]
[1,]   19   21   23
[2,]   20   22   24

---------------------------------------------------------------------------------

2. tapply(X, INDEX, FUN = NULL, ..., simplify =T)
对基本因子进行分组统计

参数:
X: 基本对象。如vector或factor。
INDEX: 包含一个或多个factor的list,每个factor的长度都应该同X相当。INDEX用于将X归类。
simplify: 如果simplify = F, 则所有返回的结果均以list形式呈现,T表示简化返回的结果。

> fact <- factor(rep(1:4, length = 18))
> tapply(fact, fact, length)    #same as table(fact)
1 2 3 4 
5 5 4 4 

如果上面这段不足以解释tapply的作用,我们可以看看下面这段
> tapply(1:18, fact, sum)
 1  2  3  4 
45 50 36 40 

上面这段code的作用就是将fact展开(fact只包含1到4四个level),然后将1:18分别循环对应到1到4下面,然后将每列相加。换一种说法,就像是把1:18顺序放到列名为1,2,3,4的表格中,然后求colSums:
Col.Names 1  2  3  4 
Row1      1  2  3  4 
Row2      5  6  7  8 
Row3      9 10 11 12 
Row4     13 14 15 16 
Row5     17 18
colSums  45 50 36 40

当INDEX时一个多元的list的时候,情况会复杂一点。

> ind <- list(c(1, 2, 2, 2), c("A", "A", "B", "B"))
> table(ind)
     ind.2
ind.1 A B
    1 1 0
    2 1 2

上面那个对ind使用的table(),作用是将list中的两个对应起来,这样,A和1对应只有一次,A和2对应一次,B和2对应2次,而1和B没有对应,所以就有了上表(即维度为ind.1和ind.2的那个结果)。如果我这时调用tapply会有什么作用呢?

> tapply(1:4, ind)     #FUN is not specified
[1] 1 2 4 4
> tapply(1:4, ind, sum)     #FUN is specified
  A  B
1 1 NA
2 2  7

第一段的处理是:在FUN缺失时的情况下,tapply默认返回一系列下标,当FUN明确的时候,X的数值就按照这一系列下标所表示的对应位置做计算。需要注意的是,ind此时是一个2元的list,我们可以将table(ind)的结果看作一个matrix: M, 那么M[1] 就是A1,M[2]就是A2,M[3]就是B1,M[4]就是B2。
此例中由于M[3]位置为空,而M[4]的位置上有两个数据,所以tapply(1:4, ind)的后两个元素返回的下标都是4。当明确了FUN为sum的时候,1:4的后两个元素在M[4]的位置上相加,就得到7。

回想前一篇讲aggregate这个函数,其实也可以实现类似效果,对比一下tapply(1:4, ind, sum)也许更容易理解。

> aggregate(1:4, ind, sum)
  Group.1 Group.2 x
1       1       A 1
2       2       A 2
3       2       B 7

---------------------------------------------------------------------------------

3. by(data, INDICES, FUN, ..., simplify = T)
将tapply发扬光大到matrix和data frame
参数不再赘述,与tapply大同小异。

>  by(mtcars[,c(1,3,4)], mtcars$cyl, sum)
mtcars$cyl: 4
[1] 2358.8
------------------------------------------------------------ 
mtcars$cyl: 6
[1] 2277.4
------------------------------------------------------------ 
mtcars$cyl: 8
[1] 8083.8

> by(mtcars[,1], mtcars[,c(2,9)], sum)
cyl: 4
am: 0
[1] 68.7
------------------------------------------------------------ 
cyl: 6
am: 0
[1] 76.5
------------------------------------------------------------ 
cyl: 8
am: 0
[1] 180.6
------------------------------------------------------------ 
cyl: 4
am: 1
[1] 224.6
------------------------------------------------------------ 
cyl: 6
am: 1
[1] 61.7
------------------------------------------------------------ 
cyl: 8
am: 1
[1] 30.8

看到这样的结果,是不是让人忍俊不禁又想起了aggregate函数。。。

> aggregate(mtcars[,1], mtcars[,c(2,9)], sum)
  cyl am     x
1   4  0  68.7
2   6  0  76.5
3   8  0 180.6
4   4  1 224.6
5   6  1  61.7
6   8  1  30.8

---------------------------------------------------------------------------------

4. eapply(env, FUN, ..., all.names = F, USE.NAMES = T)
对一个环境中的变量进行计算

> .GlobalEnv$b <- 2:11     #Clear all variables in the global env. before using this clause
> .GlobalEnv$a <- 1:10
> eapply(.GlobalEnv, mean)
$a
[1] 5.5

$b
[1] 6.5

> unlist(eapply(.GlobalEnv, mean, USE.NAMES = FALSE))
[1] 5.5 6.5

不多讲这个了,目前我还没用过这个函数,不敢断言用途是否广泛。

-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+-

说到这里,我们可以暂停一下,lapply,rapply和mapply留待下回分解。

ref: 请自行搜索以上四个函数的R Documents

0 0
原创粉丝点击