本文内容来自《R 语言编程艺术》(The Art of R Programming),有部分修改

时间和空间的权衡

编写快速 R 代码

  • 向量化,字节码编译,其他方法

  • 核心部分用编译型语言编写,如 C/C++(后续文章介绍)

  • 并行(后续文章介绍)

可怕的 for 循环

用向量化提升速度

示例1

 runif(10000000)y  runif(10000000)z  vector(length=10000000)

向量化版本

system.time(z 
   user  system elapsed    0.01    0.01    0.03

for 循环

注意:R 中每个操作都是函数

system.time(
   user  system elapsed    0.84    0.00    0.85

示例2

向量过滤?

 function(x) return(sum(x%%2 == 1))
 sample(1:10000000, 10000000, replace=TRUE)

向量化版本

system.time(
   user  system elapsed    0.22    0.00    0.22

for 版本

system.time(
   user  system elapsed    2.13    0.00    2.13

向量化函数举例

  • ifelse()

  • which()

  • where()

  • any()

  • all()

  • cumsum()

  • cumprod()

  • rowSums()

  • colSums()

  • combn()

  • outer()

  • lower.tri()

  • upper.tri()

  • expand.grid()

扩展案例:在蒙特卡罗模拟中获得更快的速度

示例1

for 循环

 function() {  sum  0  nreps  100000for (i in 1:nreps) {    xy  rnorm(2)    sum  sum + max(xy) }print(sum/nreps)}
system.time(
[1] 0.5652596   user  system elapsed    0.25    0.00    0.27

向量化,空间换时间

 function() {  nreps  100000  xymat  matrix(rnorm(2*nreps), ncol=2)  maxs  pmax(xymat[,1], xymat[,2])print(mean(maxs))}
system.time(
[1] 0.5616987   user  system elapsed    0.02    0.00    0.03

示例2

缸1:10 蓝球,8 黄球

缸2:6 篮球,6 黄球

从缸 1 中随机取 1 个球放到缸 2 中,再从缸 2 中取 1 个球,求第二球为蓝色的概率

for 循环版本

 function(nreps) {  nb1  10  n1  18  n2  13  count  0for (i in 1:nreps) { nb2  6if (runif(1) < nb1/n1) nb2  nb2 + 1if (runif(1) < nb2/n2) count  count + 1 }return(count/nreps)}
system.time(
[1] 0.50497   user  system elapsed    0.32    0.03    0.34

apply 版本

 function(nreps) {  nb1  10  nb2  6  n1  18  n2  13 u  matrix(c(runif(2*nreps)), nrow=nreps, ncol=2) simfun  function(rw) {if (rw[1] < nb1/n1) nb2  nb2 + 1return (rw[2] < nb2 / n2) } z  apply(u, 1, simfun)return(mean(z))}
[1] 0.49965   user  system elapsed    0.19    0.00    0.20
system.time(

可以看到,apply 版本速度提升不明显

向量化版本

 function(nreps) {  nb1  10  nb2  6  n1  18  n2  13 u  matrix(c(runif(2*nreps)), nrow=nreps, ncol=2) cndtn  u[,1] <= nb1/n1 & u[,2] <= (nb2+1)/n2 | u[,1] > nb1/n1 & u[,2] <= nb2/n2return(mean(cndtn))}
system.time(
[1] 0.50457   user  system elapsed    0.04    0.00    0.03

向量化版本速度有显著的提升

扩展案例:生成幂次矩阵

cbind() 版本

 function(x, dg) {  pw  matrix(x, nrow=length(x))  prod  xfor (i in 2:dg) {    prod  prod * x    pw  cbind(pw, prod) }return(pw)}

一次分配所有内存

 function(x, dg) {  pw  matrix(nrow=length(x), ncol=dg)  prod  x  pw[, 1]  prodfor (i in 2:dg) {    prod  prod * x pw[, i]  prod }return(pw)}

对比运行时间

 runif(10000000)
system.time(
   user  system elapsed    0.65    0.74    1.40
system.time(
   user  system elapsed    0.43    0.30    0.73

使用 outer() 函数

outer(X, Y, FUN) 将 FUN 函数应用与 X 和 Y 中元素的所有可能配对上

 function(x, dg) {return (outer(x, 1:dg, "^"))}
system.time(
   user  system elapsed    3.71    0.27    3.97

比前两个版本效果更差

使用 cumprod() 函数

 function(x, dg) {  repx  matrix(rep(x, dg), nrow=length(x))return(t(apply(repx, 1, cumprod)))}
system.time(
   user  system elapsed   21.69    1.07   23.38

效果更糟。

注意:性能有时是不可预测的

函数式编程和内存问题

绝大部分 R 对象都是不可变的。对变量进行重新赋值需要考虑性能问题。

向量赋值问题

rm(z)
[1] "<000001F081FDE578>"tracemem[0x000001f081fde578 -> 0x000001f093cc7ab8]: tracemem[0x000001f093cc7ab8 -> 0x000001f0be9b5a30]:
 "[(z, 3, value=8)

改变时拷贝

 runif(10)tracemem(z)z[3]  8tracemem(z)
[1] "<000001F0BE38AF10>"tracemem[0x000001f0be38af10 -> 0x000001f0be28a1b0]: [1] "<000001F0BE28A1B0>"
 1:10000000
system.time(z[3] 
   user  system elapsed    0.06    0.00    0.06
system.time(z[33] 
   user  system elapsed       0       0       0

扩展案例:避免内存拷贝

 50000n  1000

for 循环

 list()for (i in 1:m) z[[i]]  sample(1:10, n, replace=TRUE)system.time(for (i in 1:m) z[[i]][3]  8)
   user  system elapsed    0.11    0.00    0.11

向量化

 matrix(sample(1:10, m*n, replace=TRUE), nrow=m)system.time(z[,3]  8)
   user  system elapsed    0.15    0.00    0.14

效率与 for 循环相当?

lapply()

 function(lv) {  lv[3]  8return(lv)}z  list()for (i in 1:m) z[[i]]  sample(1:10, n, replace=TRUE)system.time(lapply(z, set3))
   user  system elapsed    0.22    0.00    0.21

效率不如向量化版本

利用 Rprof() 来寻找代码的瓶颈

利用 Rprof() 来进行监视

powers1() 主要瓶颈在 cbind

 runif(10000000)Rprof()invisible(powers1(x, 8))Rprof(NULL)summaryRprof()
$by.self          self.time self.pct total.time total.pct"cbind"        0.64    86.49       0.64     86.49"powers1"      0.08    10.81       0.74    100.00"matrix"       0.02     2.70       0.02      2.70

$by.total          total.time total.pct self.time self.pct"powers1"       0.74    100.00      0.08    10.81"cbind"         0.64     86.49      0.64    86.49"matrix"        0.02      2.70      0.02     2.70

$sample.interval[1] 0.02

$sampling.time[1] 0.74

powers2() 没有明显的瓶颈

Rprof()
$by.self          self.time self.pct total.time total.pct"powers2"      0.42     91.3       0.46     100.0"matrix"       0.04      8.7       0.04       8.7

$by.total          total.time total.pct self.time self.pct"powers2"       0.46     100.0      0.42     91.3"matrix"        0.04       8.7      0.04      8.7

$sample.interval[1] 0.02

$sampling.time[1] 0.46

powers3()

Rprof()
$by.self        self.time self.pct total.time total.pct"outer"      3.12    99.36       3.12     99.36"c"          0.02     0.64       0.02      0.64

$by.total          total.time total.pct self.time self.pct"outer"         3.12     99.36      3.12    99.36"powers3"       3.12     99.36      0.00     0.00"c"             0.02      0.64      0.02     0.64"hook"          0.02      0.64      0.00     0.00"Rprof"         0.02      0.64      0.00     0.00

$sample.interval[1] 0.02

$sampling.time[1] 3.14

Rprof() 的工作原理

每隔 0.02 秒检查一次函数调用栈,将结果写入到一个文件中,默认是 Rprof.out

powers4() 的输出结果很难解读

Rprof()
$by.self                self.time self.pct total.time total.pct"apply"             12.52    75.42      16.04     96.63"array"              1.06     6.39       1.06      6.39"FUN"                0.84     5.06       0.86      5.18"aperm.default"      0.76     4.58       0.76      4.58"unlist"             0.62     3.73       0.62      3.73"t.default"          0.28     1.69       0.28      1.69"matrix"             0.26     1.57       0.26      1.57"lengths"            0.18     1.08       0.18      1.08"tryCatch"           0.02     0.12       0.04      0.24"any"                0.02     0.12       0.02      0.12"as.list"            0.02     0.12       0.02      0.12"grepl"              0.02     0.12       0.02      0.12

$by.total                       total.time total.pct self.time self.pct"powers4"                   16.60    100.00      0.00     0.00"t"                         16.32     98.31      0.00     0.00"apply"                     16.04     96.63     12.52    75.42"array"                      1.06      6.39      1.06     6.39"FUN"                        0.86      5.18      0.84     5.06"aperm.default"              0.76      4.58      0.76     4.58"aperm"                      0.76      4.58      0.00     0.00"unlist"                     0.62      3.73      0.62     3.73"t.default"                  0.28      1.69      0.28     1.69"matrix"                     0.26      1.57      0.26     1.57"lengths"                    0.18      1.08      0.18     1.08"tryCatch"                   0.04      0.24      0.02     0.12"any"                        0.02      0.12      0.02     0.12"as.list"                    0.02      0.12      0.02     0.12"grepl"                      0.02      0.12      0.02     0.12""                0.02      0.12      0.00     0.00"base::try"                  0.02      0.12      0.00     0.00"cmpfun"                     0.02      0.12      0.00     0.00"compiler:::tryCmpfun"       0.02      0.12      0.00     0.00"doTryCatch"                 0.02      0.12      0.00     0.00"findLocalsList"             0.02      0.12      0.00     0.00"findLocalsList1"            0.02      0.12      0.00     0.00"funEnv"                     0.02      0.12      0.00     0.00"lapply"                     0.02      0.12      0.00     0.00"make.functionContext"       0.02      0.12      0.00     0.00"tryCatchList"               0.02      0.12      0.00     0.00"tryCatchOne"                0.02      0.12      0.00     0.00$sample.interval[1] 0.02$sampling.time[1] 16.6

Rprof.out 文件内容类似

sample.interval=20000"matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "matrix" "powers4" "aperm.default" "aperm" "apply" "t" "powers4" "aperm.default" "aperm" "apply" "t" "powers4" "aperm.default" "aperm" "apply" "t" "powers4" "aperm.default" "aperm" "apply" "t" "powers4" "aperm.default" "aperm" "apply" "t" "powers4" "aperm.default" "aperm" "apply" "t" "powers4" "aperm.default" "aperm" "apply" "t" "powers4"

字节码编译

 runif(10000000)y  runif(10000000)z  vector(length=10000000)
 function() for(i in 1:length(x)) z[i] < x[i] - y[i]system.time(g())
   user  system elapsed    2.11    0.00    2.11
library(compiler)
   user  system elapsed    2.11    0.00    2.12

内存无法装下数据怎么办

分块,例如使用 read.table() 的 skip 参数

使用 R 软件包来进行内存管理,例如 RMySQL,biglm,ff,bigmemory 等包

参考

学习 R 语言系列文章:

《快速入门》

《向量》

《矩阵和数组》

《列表》

《数据框》

《因子和表》

《编程结构》

《数学运算与模拟》

《面向对象编程》

《输入与输出》

《字符串操作》

《基础绘图》

本文代码请访问如下项目:

https://github.com/perillaroc/the-art-of-r-programming


题图由 Leonhard Niederwimmer 在 Pixabay 上发布。

R循环有两个_学习R语言:性能提升——速度和内存相关推荐

  1. R循环有两个_海德汉数控系统G代码、M代码、循环大全

    一.海德汉数控系统G代码大全 刀具运动 G00 快速直线移动 G01 进给直线移动 G02 顺时针圆弧 G03 逆时针圆弧 G05 圆弧 G06 圆弧,切线 G07 直线, 并行轴 G10 快速极坐标 ...

  2. R循环有两个_量化金融R语言【入门五味】五味:函数

    本章是此系列的最后一讲,也是衔接从入门到中阶的重要一节.众所周知,函数,不管是数学上定义的那种还是编程里叙述的那样,都意在表达逻辑.几乎写每个程序都或多或少地内嵌着函数在里面,即函数是程序的灵魂,没有 ...

  3. R循环有两个_循环流化床锅炉富氧燃烧与CO2捕集发电机组运行能耗影响因素分析...

    发电过程中燃烧化石燃料产生的大量CO2排放是引起全球气候变化和一系列环境问题的主要原因,已经引起世界各国的广泛关注.通过富氧燃烧与CO2捕集技术对燃煤电厂排放的CO2进行大规模捕集,是减少温室气体排放 ...

  4. R循环有两个_循环子群

    近 世 代 数 系 列 6本期导言回顾下Galois的定理: 假设f(x)为有理系数多项式.f(x) = 0可以根式解当且仅当f(x)的Galois群是可解群.这告诉我们, 要判断f(x)是否可以根式 ...

  5. R循环有两个_R语言for循环

    R语言for循环 for循环 本教程将针对初学者,探讨如何在R语言中编写基本的for循环和嵌套式for循环. 简单for循环 R 中for循环的基本语法是: for(i R简单for循环示例: # f ...

  6. r 字符串转化为数值_【R语言】数据结构Ⅰ—向量,矩阵,数组

    数据结构是为了便于存储不同类型的数据而设计的. R中常用的数据结构包括: 同质数据类型(homogeneous data types),即所存储的一定是相同类型的元素,包括向量.矩阵.数组: 异质数据 ...

  7. c 语言自行实现字符串常用库函数_学习c语言的7本书——你知道吗?

    下面给大家介绍7本书,如果不知道哪本适合自己,可以百度了解一下! C primer plus C primer plus作为一本被人推崇备至的c入门经典,C primer plus绝非浪得虚名.应该算 ...

  8. c语言不定长数组_学习C语言这三块“硬骨头”不搞定学了也是白学

    C语: C语言在嵌入式学习中是必备的知识,审核大部分操作都要围绕C语言进行,而其中有三块"难啃的硬骨头"几乎是公认级别的. 01指针 C语言 指针公认最难理解的概念,也是让很多初学 ...

  9. c语言数字灵活多变的访问形式_学习C语言你必须知道的事儿!

    是新朋友吗?记得先点蓝字关注我哦- 今日课程菜单 Java全栈开发 | Web前端+H5 大数据开发 | 大数据分析  人工智能+Python | 人工智能+物联网 有听过这样一段话: 在编程界,C语 ...

最新文章

  1. 2022-2028年中国化学纤维行业市场研究及前瞻分析报告
  2. centos7 安装telnet服务
  3. Java数据结构和算法:数组、单链表、双链表
  4. linux jdk1.4 安装,linux安装jdk1.4.2
  5. python六角形的绘制 编程_利用Python的turtle重复画六边形
  6. OP07高级电路图-摘自:Reza Moghim
  7. 关于SSH的分工(网友讨论集合贴)
  8. Python数据分析学习笔记04:Pandas基础
  9. POJ2823 Sliding Window 单调队列
  10. 由一个LED闪烁问题发现的MTK的LED driver中存在的问题
  11. 哈夫曼树Huffman
  12. WARNING:CPU :2 PID:593 at net/wireless/nl802.c:2883 nl80211_send_chandef+0x54/0x180
  13. python 接口自动化测试王浩然 pdf_Python接口自动化测试
  14. 【计算大于这个整数的最小质数】
  15. 你可以穿裙子,但是请放长你的裙摆
  16. BI 到底是什么,看看这篇文章怎么说
  17. divi模板下载_适用于任何WordPress主题的Divi Builder插件
  18. 【UOJ #390】【UNR #3】百鸽笼(指数型生成函数,二项式定理)
  19. 计算机网络开发与管理专业就业前景,计算机网络与安全管理专业就业前景和就业方向分析...
  20. HTTPS为啥子安全?

热门文章

  1. aes128 cmac java_使用PHP进行CMAC-AES散列
  2. jpg 与 png 的区别
  3. java中dynamic_介绍@dynamic的用法
  4. 代数学引论 课后习题解答1
  5. 【精彩回顾】迪拜BSV全球区块链大会Day1
  6. 如何学习AUTOSAR
  7. python如何自动缩进_python自动缩进
  8. ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY
  9. 利用tcp完成文件传输linux,Linux下基于TCP的文件传输
  10. R语言评价分类器性能