提升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. CDQ分治&整体二分学习个人小结

    目录 小结 CDQ分治 二维LIS 第一道裸题 bzoj1176 Mokia bzoj3262 陌上花开 bzoj 1790 矩形藏宝地 hdu5126四维偏序 P3157 [CQOI2011]动态逆 ...

  2. CPU、内存、磁盘三者的关系

    参考:https://blog.csdn.net/weini1111/article/details/70849332 cpu是大脑,计算数据用的. 内存是草稿纸,开着电脑一直都在用里边的数据,如果断 ...

  3. (55)C# windows 消息

    窗体捕获消息 namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeC ...

  4. 在Stimulsoft Reports.Net运行时修改报表的连接字符串

    怎么在Stimulsoft Reports.Net运行时修改报表的连接字符串?怎么改呀 C# StiReport report = new StiReport(); report.Load(" ...

  5. PAT_A1089#Insert or Merge

    Source: PAT A1089 Insert or Merge (25 分) Description: According to Wikipedia: Insertion sort iterate ...

  6. 点读系列《流畅的python》

    第1章 python数据模型 python的写法是由背后的魔法方法实现的,比如obj[key],解释器实际调用的是obj.__getitem__(key) 作者把魔法方法叫做双下方法,因为有两个下划线 ...

  7. Java方法覆盖教程

    重新定义来自超类(父类)继承的类中的实例方法称为方法覆盖. 示例 现在来看看,类A和类B的以下声明,覆盖了 print() 方法 : 1 2 3 4 5 6 7 8 9 10 11 public cl ...

  8. Java对象finalize()方法

    Java提供了一种在对象即将被销毁时执行资源释放的方法.在Java中创建对象,但是不能销毁对象.JVM运行一个称为垃圾收集器的低优先级特殊任务来销毁不再引用的所有对象. 垃圾回收器给我们一个机会,在对 ...

  9. Uva10817_Headmaster's Headache

    大致题意就是: 一个学校招聘人,自带老师m个,n个求职的人,需要讲授s个课程,已经知道了每个人工资,问怎么才能让各科至少有两个老师(自带的必须要) 这题刚看的时候大概知道是要状态转移,可问题是,状态转 ...

  10. Cocos2d-x之Scene

    |   版权声明:本文为博主原创文章,未经博主允许不得转载. Scene场景也是cocos2dx中必不可少的元素,游戏中通常我们需要构建不同的场景(至少一个),游戏里关卡.版块的切换也就是一个一个场景 ...