R语言多任务处理与并行运算包——foreach

相信大部分R语言初学者,在刚开始入门之处,都曾被告诫在处理多重复任务时,尽量不要使用显式的for循环,而要尽可能的使用R语言内置的apply组函数,这样可以极大地提高代码运行效率。

但是实际上除了内的apply组函数之外,你还有另外一个更好地选择,就是利用一些支持并行运算的扩展包,来发挥本地计算机的多和计算优势。

本篇要讲解的包是foreach包,这是一个支持在R语言中调用多进程功能的第三方包,之前在对比显式循环、矢量化函数以及多进程在数据抓取的效率一文中,曾经演示过具体的代码。

library("foreach")
library("doParallel")

foreach包执行任务的核心理念与传统的apply组函数基本一致,都是与split – apply – combine一致的流程,不过foreach比传统apply组函数的优越之处在于,它可以通过调用操作系统的多核运行性能来执行并行任务,这样特别是对于I/O密集型任务而言,可以大大节省代码执行效率。

foreach(...,            #待输入的参数
       .combine,        #结果返回后执行的数据合并操作(c代表合并为向量,list代表合并为列表,rbind代表合并为数据框)
       .packages=NULL,  #在多进程共享的程序包(仅对于非系统安装包必备)
       .export=NULL,    #未在当前环境中定义的数据对象
       .verbose=FALSE   #是否打印运行信息
       )

以上函数中,第1个参数是必备参数,即必须有输入参数,结果默认返回list。

foreach函数用于定义执行多进程任务的函数,任务的执行则需要使用%do%/%dopar%函数,前者执行的是普通的单进程任务(与apply组函数一样),后者则可以执行多进程任务。

接下来我们演示一遍整个多进程任务的过程:

首先定义一个执行函数:

library("httr") 
library("jsonlite")
library("magrittr")
GETPDF <-  function(i){
  url<-"https://index.toutiao.com/api/report"
  headers<-c(        
    "Host"="index.toutiao.com", 
    "User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
  )
  payload <-list("page"=1,"size"=12)
    payload[["page"]]=i
    web <- GET(url,add_headers(.headers = headers),query = payload)
    content <- web %>% content(as="text",encoding="UTF-8") %>% fromJSON() %>% `[[`(9)  
}

2、执行多进程函数

方案1——使用%do%函数执行普通的向量运算

library("doParallel")      #加载doParallel包用于之后注册进程
library("foreach")         #导入foreach包

system.time({
  cl<- makeCluster(4)      
  registerDoParallel(cl)       #进行进程注册
  mydata1 <- foreach(
              i=1:16,          #输入等待请求的参数
              .combine=rbind,  #返回结果的整合
              .packages = c("httr", "jsonlite","magrittr") 
              #多个进程共享的系统环境
  ) %dopar% GETPDF(i)
  stopCluster(cl)
})

用户 系统 流逝 
0.08 0.01 2.18

方案2——使用%dopar%函数执行多进程的运算

system.time({
  cl<-makeCluster(4)
  registerDoParallel(cl)
  mydata2 <- foreach(i=1:16,.combine=rbind) %do% GETPDF(i)
  })

用户 系统 流逝 
0.39 0.03 4.53

因为%do%操作是单进程的,因而即便启动多进程环境也是徒劳。

DT::datatable(mydata1)

可以看到,%dopar%操作比%do%操作仅仅节省了0.04秒左右,但是鉴于抓包的请求频率比较高,这样多进程所节省的时间效率感知不够明显。

system.time(
mydata3 <- plyr::ldply(1:16,GETPDF)
)

用户 系统 流逝 
0.32 0.00 4.54

整体时间效率比较:

ldply > %do% >  %dopar%
4.54  > 4.53 >  2.18

从时间效率上来看,的确节省了将近50%的时间。如此高逼格的神器,怎能不学呢~~

原文发布于微信公众号 - 数据小魔方(datamofang)

原文发表时间:2018-01-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

pwnable.tw刷题之dubblesort

前言 上一篇中我介绍了phttp://www.freebuf.com/articles/others-articles/134271.htmlwnable.tw...

3076
来自专栏企鹅号快讯

python中any和all如何使用

python中any()和all()如何使用 和 对于检查两个对象相等时非常实用,但是要注意, 和 是python内置函数,同时numpy也有自己实现的 和 ,...

2005
来自专栏AI研习社

Python 3.7.0 发布,包含多项新特性和优化

Python 3.7.0 版本于 6 月 27 号正式发布,该版本有多项重大的更新和改进,主要内容如下如下:

871
来自专栏idealclover的填坑日常

C++ cin清理缓冲区

C++在用cin做输入的时候,尤其是需要输入int型值的时候,往往需要做验证与处理。而对于异常值,则需要先清理缓冲区,再准备下一次输入。

1182
来自专栏Golang语言社区

Golang实现带优先级的channel

一般Go语言同时使用多个channel的方法是使用select/case语句配合<-操作符,比如 select {case <- chan1: // do...

4367
来自专栏IT派

Python 性能优化的20条招数

算法的时间复杂度对程序的执行效率影响最大,在 Python 中可以通过选择合适的数据结构来优化时间复杂度,如 list 和 set 查找某一个元素的时间复杂度分...

953
来自专栏程序员阿凯

JDK10 揭秘

1495
来自专栏猿湿Xoong

一个bit一个bit的进行 Base64 白话科普,看不懂算你输

1084
来自专栏机器学习算法与Python学习

Python再次更新! 解锁与优化多项新特性......

Python 3.7.0 版本于 6 月 27 号正式发布,该版本有多项重大的更新和改进,主要内容如下如下:

1030
来自专栏三丰SanFeng

Linux和Windows的换行符

一直对换行符这个东西概念比较模糊,直到最近花了一点时间仔细研究了一下,才彻底搞清楚这个问题,本文前面介绍部分是外文转载,后面例子是个人总结,希望能对大家有一些帮...

2457

扫码关注云+社区