北京大学生物信息平台论坛

 找回密码
 立即注册
搜索
热搜: 通知 活动

R 学习笔记:不要for循环

[复制链接]
wolf 发表于 2016-5-20 09:41:00 | 显示全部楼层 |阅读模式
本帖最后由 wolf 于 2016-5-20 09:43 编辑

本帖最后由 wolf 于 2016-5-20 09:38 编辑


在 R 语言中, for 循环不像 C 语言中的 for 那么快速. 因为在 R 的 for 循环中, 需要 R 解释器去解析每个字符, 而 C 则通过编译把代码转为了电脑可以直接运行的机器码. 在 R 中, 单纯地在循环中增加括号, 就能够使得循环的速度下降.

  1. m <- 0
  2. system.time({
  3.     for (i in 1:10000000) {
  4.         m <- m + i
  5.     }
  6. })
  7. # user  system elapsed
  8. # 4.266   0.014   4.289
  9. m <- 0
  10. system.time({
  11.     for (i in 1:10000000) {
  12.         (((((((m <- m + i)))))))
  13.     }
  14. })
  15. # user  system elapsed
  16. # 10.920   0.013  10.953
复制代码
有的时候我们需要做的只是简单地对列表或者数据框的每一行或列数据使用同样的函数, 在 C 语言中, 同样的问题通过 for loop 或者 while 来解决是十分直观和方便高效的. 而由于上面提到了 R 的特点, 使用 for loop 尤其是多层 for loop 则会很慢. 这样的情况下, 就应该取考虑映射 (mapping), 即在一系列数据上重复地去使用同一个函数, 即把该函数映射到数据上.

还应该注意的是, R 中对数据集合的单个元素的改变并不是在原来的位置上直接变换, 而是重新产生一个数据集合, 和之前的数据集合在需要变换的地方不同. 这样的效率就相对来说比较低下. 所以应该尽量避免频繁的复制改变. 那么一般的解决办法是使用一些在中间计算过程中不会赋值的函数, 如下面说道的 apply 系列, 还有就是使用 C++ 语言 和 RCpp 包来完成需要频繁改变的代码部分.


apply 函数

在 R 的基础包中有一类函数叫 apply, 包括 apply, lapply, sapply, vapply, mapply, replacate 等. 这些函数相同的地方都是接受一个 list, matrix 或者 data.frame 作为参数, 同时接受一个函数作为参数, 然后对数据的每一行或列都使用函数进行处理, 相当于 "把函数应用 (apply) 到每一个单元".


apply

apply 函数本身的运算速度其实和 for loop 很接近, 但是由于其语句少, 结构简单, 在进行大量重复应用相同函数到同一个数据集的时候效率似乎要比 for loop 要高一些. apply 函数有三个主要参数: 一个是数据集, array, matrix, data.frame, list 都可以; 一个是 MARGIN, MARGIN 为 1 的时候对 matrix 的每一行运行函数, MARGIN 为 2 的时候对 matrix 的每一列运行函数, 如果是 array 则可能是更高维; 一个是要对数据集运行的函数.

有的时候我需要对一个向量计算若干项的平均值, 比如说要把这个向量按顺序分成不重和的若干组, 每组三个, 然后计算其平均值. 这个时候就可以把该向量先转换成一个有一个维度为 3 的矩阵, 然后使用 apply 进行计算.

  1. x <- 1:12
  2. apply(matrix(x,ncol=3,byrow=TRUE), 1, mean)
  3. #[1]  2  5  8 11
复制代码
除了使用 apply 可以使用不同的函数之外, 如果目的仅仅是需要得到几个值的和或者均值, 还可以在把向量转换为矩阵之后使用 colSums, rowSums, colMeans, rowMeans 等函数.


sweep

有的时候, 所需要对一个数据集映射的函数需要有两个值, 比如说 "/" 除法操作符等双目操作符, 对于一列或者一行, 所用的值也不太一样. 这个时候 apply 可能就不是很方便了, 这个时候, 使用 sweep 就更方便一些. sweep 相对于 apply 要多接受一个参数, STAT, STAT 就是用来接受如双目的操作符后面的一个数字的.

  1. x <- 1:12
  2. y <- seq(2, 24, by = 2)
  3. sweep(matrix(x, nrow = 1), 2, y, "/")
  4. #      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
  5. #[1,]  0.5  0.5  0.5  0.5  0.5  0.5  0.5  0.5  0.5   0.5   0.5   0.5
复制代码

lapply, sapply, vapply, replicate


lapply, sapply, vapply, replicate 有很多相似的地方, 实际上, lapply 是最为基础的, sapply 是对 lapply 的一种方便使用的包装, 可以理解为 "simple apply". 而 replicate 则是对 sapply 的一个更方便使用的包装.

lapply 可以理解为 "list apply", 其总会返回一个 list 结果, 这个 list 的长度和输入的 list x 的长度是一样. 其接受一个数据集, 列表 x, 并接受一个需要做映射的函数, 同时也可以接受该函数的参数.

sapply 是对 lapply 的包装, 其结果不再一定是一个 list, 也会根据方便程度返回向量等, 其有参数 simplify = TRUE 可以设置是否采取简化输出.

vapply 则是限定输出的结果为 vector, 其返回结果会和 FUN.VALUE 所设定的一致, 如设定每一项的名字 c("one" = 0, "two" = 0, "three" = 0). 有的时候 sapply 输出的值和 vapply 输出的值是相似的.

replicate 是简单地把一个函数重复若干次, 并把每次重复函数的输出的值整合起来, 如可以重复一个输出五个随机数的函数, 这样设置 replicate 函数的参数 n 为 100, 就能输出一个 5 * 100 的矩阵. replicate 可以用于去构建测试数据集.


mapply

mapply 可以理解为 "multi sapply", 它接受一个函数作为参数 FUN, 另外接受若干数据集做为输入, 同时每个数据集对应的行会作为 FUN 的参数

  1. mapply(sum, 1:4, 4:1)
  2. #[1] 5 5 5 5
复制代码

aggregate, tapply

有的时候我们需要根据某种分组来对数据集进行处理, 比如说根据性别来计算相对身高等. 这个时候就可以使用 aggregate, tapply 等函数.

aggregate 函数也可以接受一个 list 或 data.frame 作为输入数据集, 也接受一个函数做参数. 相对前面提到的 sapply 有一点重要不同是, 接受一个分组的 list 作为 by 参数的值. 所有的计算将根据 by 的分组进行. 所输出的结果的维度将会和分组的维度相关.

tapply 和 aggregate 类似, 不过其 INDEX 参数可以接受若干对数据集进行分组的向量, 输出结果将和分组相关. 也可以使用面向对象的 tapply, by 函数, 其参数和 tapply 类似. 对于 tapply, 就是可以把数据集按照一定规则分成几类, 那么相似的结果也可以通过 sapply 函数和 split 函数实现.
  1. n <- 17; fac <- factor(rep(1:3, length = n), levels = 1:5)
  2. aggregate(1:n, list(fac), fivenum)
  3. #  Group.1  x.1  x.2  x.3  x.4  x.5
  4. #1       1  1.0  4.0  8.5 13.0 16.0
  5. #2       2  2.0  5.0  9.5 14.0 17.0
  6. #3       3  3.0  6.0  9.0 12.0 15.0
  7. tapply(1:n, list(fac), fivenum)
  8. #1'
  9. #[1]  1.0  4.0  8.5 13.0 16.0

  10. #2'
  11. #[1]  2.0  5.0  9.5 14.0 17.0

  12. #3'
  13. #[1]  3  6  9 12 15

  14. #4'
  15. #NULL

  16. #5'
  17. #NULL
复制代码

回复

使用道具 举报

北京大学生物信息平台论坛

GMT+8, 2017-11-19 22:10 , Processed in 0.085116 second(s), 23 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表