提升R代码运算效率的11个实用方法

众所周知,当我们利用R语言处理大型数据集时,for
循环语句的运算效率非常低。有许多种方法可以提升你的代码运算效率,但或许你更想了解运算效率能得到多大的提升。本文将介绍几种适用于大数据领域的方法,包括简单的逻辑调整设计、并行处理和
Rcpp 的运用,利用这些方法你可以轻松地处理1亿行以上的数据集。

让我们尝试提升往数据框中添加一个新变量过程(该过程中包含循环和判断语句)的运算效率。下面的代码输出原始数据框:

# Create the data frame

col1 <- runif (12^5, 0, 2)

col2 <- rnorm (12^5, 0, 2)

col3 <- rpois (12^5, 3)

col4 <- rchisq (12^5, 2)

df <- data.frame (col1, col2, col3, col4)

逐行判断该数据框 (df) 的总和是否大于 4 ,如果该条件满足,则对应的新变量数值为 ’greaterthan4’ ,否则赋值为
’lesserthan4’ 。

# Original R code: Before vectorization and pre-allocation

system.time({

  for (i in 1:nrow(df)) { # for every row

    if ((df[i,
'col1'] + df[i, 'col2'] + df[i, 'col3'] + df[i, 'col4']) > 4) {
# check if > 4

     
df[i, 5] <- "greater_than_4" # assign 5th column

    } else
{

     
df[i, 5] <- "lesser_than_4" # assign 5th column

    }

  }

})

本文中所有的计算都在配置了 2.6Ghz 处理器和 8GB 内存的 MAC OS X 中运行。

1.向量化处理和预设数据库结构

for (i in 1:nrow(df)) {

    if ((df[i,
'col1'] + df[i, 'col2'] + df[i, 'col3'] + df[i, 'col4']) > 4)
{

     
output[i] <- "greater_than_4"

    } else
{

     
output[i] <- "lesser_than_4"

    }

  }

df$output})

2.将条件语句判断条件移至循环外

将条件判断语句移至循环外可以提升代码的运算速度,接下来本文将利用包含 100,000行数据至 1,000,000
行数据的数据集进行测试:

# after vectorization and pre-allocation, taking the condition
checking outside the loop.

output <- character (nrow(df))

condition <- (df$col1 + df$col2 + df$col3 + df$col4) >
4  # condition check outside the loop

system.time({

  for (i in 1:nrow(df)) {

    if
(condition[i]) {

     
output[i] <- "greater_than_4"

    } else
{

     
output[i] <- "lesser_than_4"

    }

  }

  df$output <- output

})

3.只在条件语句为真时执行循环过程

另一种优化方法是预先将输出变量赋值为条件语句不满足时的取值,然后只在条件语句为真时执行循环过程。此时,运算速度的提升程度取决于条件状态中真值的比例。

本部分的测试将和 case(2) 部分进行比较,和预想的结果一致,该方法确实提升了运算效率。

output <- c(rep("lesser_than_4", nrow(df)))

condition <- (df$col1 + df$col2 + df$col3 + df$col4) >
4

system.time({

    for (i in
(1:nrow(df))[condition]) {  # run loop only for
true conditions

       
if (condition[i]) {

           
output[i] <- "greater_than_4"

       


    }

   
df$output 

})

4.尽可能地使用 ifelse() 语句

利用 ifelse() 语句可以使你的代码更加简便。 ifelse() 的句法格式类似于 if()
函数,但其运算速度却有了巨大的提升。即使是在没有预设数据结构且没有简化条件语句的情况下,其运算效率仍高于上述的两种方法。

system.time({

  output <- ifelse ((df$col1 + df$col2 +
df$col3 + df$col4) > 4, "greater_than_4", "lesser_than_4")

  df$output <- output

})

5.使用 which() 语句

利用 which() 语句来筛选数据集,我们可以达到
Rcpp 三分之一的运算速率。

# Thanks to Gabe Becker

system.time({

  want = which(rowSums(df) > 4)

  output = rep("less than 4", times =
nrow(df))

  output[want] = "greater than 4"

}) 

# nrow = 3 Million rows (approx)

   user  system
elapsed 

  0.396  
0.074   0.481

6.用 apply 族函数替代 for 循环语句

本部分将利用 apply() 函数来计算上文所提到的案例,并将其与向量化的循环语句进行对比。该方法的运算效率优于原始方法,但劣于
ifelse() 和将条件语句置于循环外端的方法。该方法非常有用,但是当你面对复杂的情形时,你需要灵活运用该函数。

# apply family

system.time({

  myfunc <- function(x) {

    if
((x['col1'] + x['col2'] + x['col3'] + x['col4']) > 4) {

     
"greater_than_4"

    } else
{

     
"lesser_than_4"

    }

  }

  output <- apply(df[, c(1:4)], 1,
FUN=myfunc)  # apply 'myfunc' on every row

  df$output <- output

})

7.利用compiler包编译函数cmpfun()

这可能不是说明字节码编译有效性的最好例子,但是对于更复杂的函数而言,字节码编译将会表现地十分优异,因此我们应当了解下该函数。

# byte code compilation

library(compiler)

myFuncCmp <- cmpfun(myfunc)

system.time({

  output <- apply(df[, c (1:4)], 1,
FUN=myFuncCmp)

})

8.利用Rcpp

截至目前,我们已经测试了好几种提升运算效率的方法,其中最佳的方法是利用ifelse()函数。如果我们将数据量增大十倍,运算效率将会变成啥样的呢?接下来我们将利用Rcpp来实现该运算过程,并将其与ifelse()进行比较。

下面是利用C++语言编写的函数代码,将其保存为“MyFunc.cpp”并利用sourceCpp进行调用。

// Source for MyFunc.cpp

#include 

using namespace Rcpp;

// [[Rcpp::export]]

CharacterVector myFunc(DataFrame x) {

  NumericVector col1 = as(x["col1"]);

  NumericVector col2 = as(x["col2"]);

  NumericVector col3 = as(x["col3"]);

  NumericVector col4 = as(x["col4"]);

  int n = col1.size();

  CharacterVector out(n);

  for (int i=0; i 4){

     
out[i] = "greater_than_4";

    } else
{

     
out[i] = "lesser_than_4";

    }

  }

  return out;

}

9.利用并行运算

并行运算的代码:

# parallel processing

library(foreach)

library(doSNOW)

cl <- makeCluster(4, type="SOCK") # for 4 cores machine

registerDoSNOW (cl)

condition <- (df$col1 + df$col2 + df$col3 + df$col4) >
4

# parallelization with vectorization

system.time({

  output <- foreach(i = 1:nrow(df), .combine=c)
%dopar% {

    if
(condition[i]) {

     
return("greater_than_4")

    } else
{

     
return("lesser_than_4")

    }

  }

})



df$output <- output

10.尽早移除变量并恢复内存容量

在进行冗长的循环计算前,尽早地将不需要的变量移除掉。在每次循环迭代运算结束时利用gc()函数恢复内存也可以提升运算速率。 http://www.cda.cn/view/18841.html

11.利用内存较小的数据结构

在进行冗长的循环计算前,尽早地将不需要的变量移除掉。在每次循环迭代运算结束时利用gc()函数恢复内存也可以提升运算速率。

data.table()是一个很好的例子,因为它可以减少数据的内存,这有助于加快运算速率。

dt <- data.table(df)  # create the
data.table

system.time({

  for (i in 1:nrow (dt)) {

    if ((dt[i,
col1] + dt[i, col2] + dt[i, col3] + dt[i, col4]) > 4) {

     
dt[i, col5:="greater_than_4"]  # assign the output
as 5th column

    } else
{

     
dt[i, col5:="lesser_than_4"]  # assign the output
as 5th column

    }

  }

})

总结

方法:速度, nrow(df)/time_taken = n 行每秒

原始方法:1X, 856.2255行每秒(正则化为1)

向量化方法:738X, 631578行每秒

只考虑真值情况:1002X,857142.9行每秒

ifelse:1752X,1500000行每秒

which:8806X,7540364行每秒

Rcpp:13476X,11538462行每秒

提升R代码运算效率的11个实用方法的更多相关文章

  1. 提升R代码运算效率的11个实用方法——并行、效率

    转载于36大数据,原文作者:Selva Prabhakaran  译者:fibears 众所周知,当我们利用R语言处理大型数据集时,for循环语句的运算效率非常低.有许多种方法可以提升你的代码运算效率 ...

  2. 【R】提升R代码运算效率的11个实用方法

    低.有许多种方法可以提升你的代码运算效率,但或许你更想了解运算效率能得到多大的提升.本文将介绍几种适用于大数据领域的方法,包括简单的逻辑调整设计.并行处理和Rcpp的运用,利用这些方法你可以轻松地处理 ...

  3. R语言学习笔记(二十一五):如何如何提升R语言运算的性能以及速度

    在R中获得快速运行代码的方法 使用向量化运算 R语言的并行计算可以用parallel和foreach包 加快R运行速度还可以使用cmpfun()函数即字节码编译器 再者就是在R中调用C或C++ 同时还 ...

  4. iOS:Xcode代码块,提升敲代码的效率

    一.代码块在哪里? 看下图 或者 快捷键:command+shift+L 长这样: 二.如何创建代码块: 1.先选中要创建的代码片段,然后点击右键,选中 Create Code Snippet 然后会 ...

  5. 高效完成R代码

    为什么R有时候运行慢? 参考https://www.cnblogs.com/qiaoyihang/p/7779144.html 一.为什么R程序有时候会很慢? 1.计算性能的三个限制条件 cpu ra ...

  6. Unity3d代码及效率优化总结

    1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU. 2.如果你用U3D自带的SHADER,在表现不差的情况下选择Mobile或Unli ...

  7. 值得细读!如何系统有效地提升Android代码的安全性?

    众所周知,代码安全是Android开发工作中的一大核心要素. 11月3日,安卓巴士全球开发者论坛线下系列沙龙第七站在成都顺利举办.作为中国领先的安卓开发者社区,安卓巴士近年来一直致力于在全国各大城市举 ...

  8. java 性能优化:35 个小细节,让你提升 java 代码的运行效率

    前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没 ...

  9. atitit.提升软件开发的效率and 质量的那些强大概念and方法总结

    atitit.提升软件开发的效率and 质量的那些强大概念and方法总结 1. 主流编程中三个最糟糕的问题 1 1.1. 从理解问题后到实现的时间很长 1 1.2. 理解和维护代码  2 1.3. 学 ...

随机推荐

  1. 20175203 2018-2019-2《Java程序设计》第五周学习总结

    20175203 2018-2019-2<Java程序设计>第五周学习总结 第六章:接口与实现 本周学习了<Java程序设计>第六章的内容:接口与实现,以下为本周学习总结. 知 ...

  2. 55、saleforce 学习笔记二

    String goodsName = 'abcd1123汉字显示';//测试文本 System.debug('简化后的字符串名称为:'+goodsName.abbreviate(5)); //返回简化 ...

  3. 03 java语言基础逻辑运算符

    03.01_Java语言基础(逻辑运算符的基本用法) A:逻辑运算符有哪些 &,|,^,! &&,|| B:案例演示 逻辑运算符的基本用法 注意事项: a:逻辑运算符一般用于连 ...

  4. Codeforces Round #499 (Div. 2) Problem-A-Stages(水题纠错)

    CF链接  http://codeforces.com/contest/1011/problem/A Natasha is going to fly to Mars. She needs to bui ...

  5. servlet项目找不到java文件(由于照抄代码造成的bug)

    其实这个bug非常简单,实际上是由于路径配置问题,也是由于我照抄代码造成的(好孩子不要学!) 情况是这样的:java文件没有问题但是启动项目的时候出现了bug: 然后当我把GetData.java和J ...

  6. 在apache hadoop2.6 上部署hive 并将hive数据源存储于Mysql

    集成hive 的前提是apache hadoop 集群能够正常启动. hadoop 版本 apach2.6.0  hive 版本:1.2.1 1.安装mysql 并赋予权限: 1.1:创建hive 用 ...

  7. Sql Server 表结构相关

    1.库表列信息 --取所有库 SELECT Name FROM Master..SysDatabases ORDER BY Name --查询所有表 select name from 库名..syso ...

  8. ARC103

    ARC103E Tr/ee 首先没有叶子显然不科学,\(s_n\)是1也不怎么科学,\(s_i != s_{n-i}\)同样不怎么科学 特判掉上述情况后先把root记为1,链接(root,i+1)如果 ...

  9. byte为什么要与0xff

    面对带正负号的数,会采用符号扩展,如果原值是正数,则高位补上0:如果原值是负数,高位补1.二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.当前的计算机系统使用的基本上是二 ...

  10. 防御 CSRF

    我还针对这个问题请教了 @c4605 , 他对防御 CSRF 提出了两种解决方案: 在每个表单中包含一个 CSRF Token.不将用于认证的 Token 或 Seesion ID 储存在 Cooki ...