背景
提升R代码运行速度并不需要很高级的优化技术, 例如代码并行化, 使用数据库, 使用c++等. 实际上, 通过简单的操作, 就能够是R的运算速度显著的加快, 下面介绍几种方法.
1, 向量化
R语言允许用户进行向量化编程, 这样速度更快.
比如我们计算100万随机数, 计算他们的平方, 这里使用两种方法: 第一种, for循环; 第二种, 向量化
set.seed(123)
dat= rnorm(1000000)
for循环
system.time({
dd = NULL
for(i in 1:length(dat)){
dd[i] = dat[i]^2
}
})
user system elapsed
0.31 0.03 0.35
head(dat);head(dd)
- -0.560475646552213
- -0.23017748948328
- 1.55870831414912
- 0.070508391424576
- 0.129287735160946
- 1.71506498688328
- 0.314132950378121
- 0.0529816766648255
- 2.4295716085976
- 0.00497143326128122
- 0.016715318463047
- 2.94144790923295
直接在原向量上计算平方
system.time({
dd2 =dat^2
})
user system elapsed
0 0 0
head(dat);head(dd2)
- -0.560475646552213
- -0.23017748948328
- 1.55870831414912
- 0.070508391424576
- 0.129287735160946
- 1.71506498688328
- 0.314132950378121
- 0.0529816766648255
- 2.4295716085976
- 0.00497143326128122
- 0.016715318463047
- 2.94144790923295
速度由0.3s到0.02s
2, 预分配内存
R语言是动态分布内存的, 不需要预先生成变量, 可以直接使用. 这种方法比较简单, 但是数据量大时, 会影响速度. 所以在使用变量时, 提前声明变量的大小, 会提升速度
不预先分布内存
这个程序, 不知道dat的长度是多少, 因此是动态的内存结构.
N = 1e5;N
1e+05
system.time({
dat =1
for(i in 2:N){
dat = c(dat,dat[i-1]+sample(1:2,size = 1))
}
})
user system elapsed
8.88 0.15 9.07
预先分配内存
dat的长度是1e5, 因此我们可以生成这一个为0的向量, 这样他的内存就固定了
N = 1e4;N
dat = rep(0,N)
10000
system.time({
dat =1
for(i in 2:N){
dat = c(dat,dat[i-1]+sample(1:2,size = 1))
}
})
user system elapsed
0.14 0.00 0.14
可以看到, 时间由7.5s降到0.11s
3, 使用apply函数代替for循环
apply函数及其变种:
- lapply
- sapply
- tapply
如果各个迭代之间相互独立, 那么apply函数是可以代替for循环的
for循环
set.seed(123)
dat= rnorm(1000000)
system.time({
dd = NULL
for(i in 1:length(dat)){
dd[i] = dat[i]^2
}
})
user system elapsed
0.28 0.00 0.28
square = function(x){
return(x^2)
}
system.time({tt = lapply(dat, FUN=square)})
user system elapsed
1.00 0.00 1.02
t = rep(0,length(dat))
system.time({t = lapply(dat, FUN=square)})
user system elapsed
0.62 0.03 0.66
4, 使用matrix而不是data.table
在矩阵运算时, 尽量转化为matrix格式
使用matrix
dat = matrix(rnorm(1e4*1000),1e4,1000)
dd = as.data.frame(dat)
system.time( rowSums(dd))
user system elapsed
0.05 0.00 0.05
使用data.frmae
system.time( rowSums(dat))
user system elapsed
0.04 0.00 0.03