R语言编程的高效方法
R语言编程的高效方法
学习资料来源:
- datacamp : writing-efficient-r-code
- 网络资源:
https://cosx.org/2016/09/r-and-parallel-computing
http://gforge.se/2015/02/how-to-go-parallel-in-r-basics-tips/
高效编程之细节知识点
- 1.使用最新的R版本 通过version指令查看
- 2.r中读取rds形式的文件速度快于csv文件. readRDS()
- 3.通过system.time查看函数的一次运行时间
- 4.通过microbenchmark包中的microbenchmark()查看函数的一次运行时间
library(microbenchmark)
compare <- microbenchmark(read.csv('x.csv'),
readRDS('x.rds'),times=10)
times=10表示过程重复10遍,一次运行函数需要的时间是随机的
- 5.电脑硬件越好当然越快
通过benchmarkme包可以查看电脑硬件性能
library(benchmarkme)
ram <- get_ram()
cpu <- get_cpu()
R的存储方式
从内存角度来看,R 采用的是内存计算模式(In-Memory),被处理的数据需要预取到主存(RAM)中。其优点是计算效率高、速度快,但缺点是这样一来能处理的问题规模就非常有限(小于 RAM 的大小)。另一方面,R 的核心(R core)是一个单线程的程序。因此,在现代的多核处理器上,R 无法有效地利用所有的计算内核。
- 6.向量化处理 (善用for循环)
常听闻的r中使用for loop的速度是缓慢的,这样的论断是错误的,往往是因为在for循环中,我们通过循环堆叠增长向量
错误示范:
x <- NULL
n <- 30000
for(i in 1:n) {
x <- c(x, rnorm(1))
}
- 7.data.frame&matrice
数据结构
dataframe行数据类型不同,列数据类型同
matrice行数据类型同,列数据类型同
可以理解:
读取matrice的速度快于data.frame
读取matrice列的速度快于读取dataframe的列
读取matrice行的速度远快于读取dataframe的行
- 8.用profvis查看每行函数的运行速度
# Load the data set
data(movies, package = "ggplot2movies")
# Load the profvis package
library(profvis)
# Profile the following code with the profvis function
profvis({
comedies <- movies[movies$Comedy == 1, ]
plot(comedies$year, comedies$rating)
model <- loess(rating ~ year, data = comedies)
j <- order(comedies$year)
lines(comedies$year[j], model$fitted[j], col = "red")
} )
并行计算
并行计算技术是为了在实际应用中解决单机内存容量和单核计算能力无法满足计算需求的问题而提出的。因此,并行计算技术将非常有力地扩充 R 的使用范围和场景。
1.查看核数
library(parallel)
no_of_cores <- detectCores()
2.可以使用并行计算的场景
The ith value does not depend on the previous value.
3.Parellel 包
用一元二次方程求解问题来介绍如何利用 * apply 和 foreach 做并行化计算
# Not vectorized function
solve.quad.eq <- function(a, b, c) {
# Not validate eqution: a and b are almost ZERO
if(abs(a) < 1e-8 && abs(b) < 1e-8) return(c(NA, NA) )
# Not quad equation
if(abs(a) < 1e-8 && abs(b) > 1e-8) return(c(-c/b, NA))
# No Solution
if(b*b - 4*a*c < 0) return(c(NA,NA))
# Return solutions
x.delta <- sqrt(b*b - 4*a*c)
x1 <- (-b + x.delta)/(2*a)
x2 <- (-b - x.delta)/(2*a)
return(c(x1, x2))
}
# Generate data
len <- 1e6
a <- runif(len, -10, 10)
a[sample(len, 100,replace=TRUE)] <- 0
b <- runif(len, -10, 10)
c <- runif(len, -10, 10)
apply 实现方式:下面的代码利用 lapply 函数将方程求解函数 solve.quad.eq 映射到每一组输入数据上,返回值保存到列表里。
# serial code
system.time(
res1.s <- lapply(1:len, FUN = function(x) { solve.quad.eq(a[x], b[x], c[x])})
)
接下来,我们利用 parallel 包里的 mcLapply (multicores)来并行化 lapply 中的计算。从 API 的接口来看,除了额外指定所需计算核心之外,mcLapply 的使用方式和原有的 lapply 一致,这对用户来说额外的开发成本很低。
对于Mac用户,可以使用 parallel 包里的 parLapply 函数来实现并行化。
在使用 parLapply 函数之前,我们首先需要建立一个计算组(cluster)。计算组是一个软件层次的概念,它指我们需要创建多少个 R 工作进程(parallel 包会创建新的 R 工作进程,而非 multicores 里 R 父进程的副本)来进行计算,理论上计算组的大小并不受硬件环境的影响。比如说我们可以创建一个大小为 1000 的计算组,即有 1000 个 R 工作进程。 但在实际使用中,我们通常会使用和硬件计算资源相同数目的计算组,即每个 R 工作进程可以被单独映射到一个计算内核。如果计算群组的数目多于现有硬件资源,那么多个 R 工作进程将会共享现有的硬件资源。
如下例我们先用 detectCores 确定当前电脑中的内核数目。值得注意的是 detectCores 的默认返回数目是超线程数目而非真正物理内核的数目。例如在我的笔记本电脑上有 2 个物理核心,而每个物理核心可以模拟两个超线程,所以 detectCores() 的返回值是 4。对于很多计算密集型任务来说,超线程对性能没有太大的帮助,所以使用logical=FALSE参数来获得实际物理内核的数目并创建一个相同数目的计算组。由于计算组中的进程是全新的 R 进程,所以在父进程中的数据和函数对子进程来说并不可见。因此,我们需要利用 clusterExport 把计算所需的数据和函数广播给计算组里的所有进程。最后 parLapply 将计算平均分配给计算组里的所有 R 进程,然后收集合并结果。
#Cluster on Windows
cores <- detectCores(logical = FALSE)
cl <- makeCluster(cores)
clusterExport(cl, c('solve.quad.eq', 'a', 'b', 'c'))
system.time(
res1.p <- parLapply(cl, 1:len, function(x) { solve.quad.eq(a[x], b[x], c[x]) })
)
stopCluster(cl)
吃机智豆长大的少女乙
发布了19 篇原创文章 · 获赞 98 · 访问量 13万+
私信
关注