R语言多线程运算操作(解决R循环慢的问题)

已经大半年没有更新博客了。。最近都跑去写分析报告半年没有R

这次记录下关于R循环(百万级以上)死慢死慢的问题,这个问题去年就碰到过,当时也尝试过多线程,but failed......昨天试了下,终于跑通了,而且过程还挺顺利

step1

先查下自己电脑几核的,n核貌似应该选跑n个线程,线程不是越多越好,线程个数和任务运行时间是条开口向下的抛物线,最高点预计在电脑的核数上。

detectCores( )检查当前电脑可用核数 我的是4所以step2选的是4

library(parallel)
cl.cores <- detectCores()

step 2

多线程计算

setwd("C:\\Users\\siyuanmao\\Documents\\imdada\\0-渠道投放和新人券联动模型\\测算")
options(scipen=3)  ##取消科学计数法
channel_ad_ios_data<-seq(0,50000,5000)
channel_ad_android_data<-seq(0,100000,10000)
library(parallel)
func <- function(n){#n=1
  result_data<-read.csv("发券方案.csv",stringsAsFactors=FALSE)
  total_coupon_solution_data<-read.csv("结果表框架.csv",stringsAsFactors=FALSE)
  coupon_solution_data<-subset(result_data,solution== paste('方案',n,sep=""))

  for (i in 1:11){#i=3
    coupon_solution_data$channel_ad_cost[3]<-5000*(i-1)

    for (j in 1:11){#j=5
      coupon_solution_data$channel_ad_cost[4]<-10000*(j-1)
      solution_mark<-paste('方案',n,i,j,sep="-")
      coupon_solution_data$solution<-solution_mark

      total_coupon_solution_data<-rbind(total_coupon_solution_data,coupon_solution_data)
    }
  }
  print(solution_mark)
  return(total_coupon_solution_data)
}
#func(10)
system.time({
x <- 1:7776
cl <- makeCluster(4) # 初始化四核心集群
results <- parLapply(cl,x,func) # lapply的并行版本
res.df <- do.call('rbind',results) # 整合结果
stopCluster(cl) # 关闭集群
})
df=as.data.frame(res.df)

原来非多线程的时候,我预计要跑12个小时以上,电脑发出呼呼~~的响声,查了下Python循环会快点,然后改为python版(已经很久没有用了,连个range都不会写,摸索了大半天才改好,但是速度还是慢==),于是改成多线程,运行25分钟就出结果了~~

补充:R语言 多线程

parallel包

包的安装

install.packages("parallel")
library(parallel)

包中常用函数

detectCores() 检查当前的可用核数

clusterExport() 配置当前环境

makeCluster() 分配核数

stopCluster() 关闭集群

parLapply() lapply()函数的并行版本

其实R语言本来就是一门向量化语言,如果是对于一个向量的操作,使用apply函数一族能获得比较高的效率,相比于for循环,这种高效来自于:

用C实现了for循环

减少对于data.frame等数据结构等不必要的拷贝

但是很多时候,如果想更快的话,光apply函数一族还不足够,这时候就能用上多线程。

R语言parallel包可以帮助实现多线程。

parLapply的简单代码实战

检查当前核数

cl.cores <- detectCores()
#结果
> cl.cores
[1] 8

启动集群和关闭集群

cl <- makeCluster(4) # 初始化四核心集群
###并行任务
stopCluster(cl) # 关闭集群

parLapply执行多线程计算

#定义计算平方函数
square <- function(x)
{
    return(x^2)
}
#利用并行计算计算平方函数
num <- c(1:3)
cl <- makeCluster(4) # 初始化四核心集群
results <- parLapply(cl,num,square)#调用parLapply并行计算平方函数
final <- do.call('c',results)#整合结果
stopCluster(cl) # 关闭集群
#结果
> final
[1] 1,4,9

思考:在如此小的计算方式下,开4个核计算是否比开一个核要快

答案:当然是不一定,因为涉及到调度方式等额外开销,所以不一定快,因为真正并行起作用的地方在于大数据量的计算。

时间开销对比

两段对比代码

#定义计算平方函数
square <- function(x)
{
   #########
   #一段冗余代码增加执行时间
    y = 2*x
    if(y <300)
    {z = y}
    else
    {z = x}
   ##########
    return(x^2)
}
num <- c(1:10000000)
#并行计算
print(system.time({
    cl <- makeCluster(4) # 初始化四核心集群
    results <- parLapply(cl,num,square)#调用parLapply并行计算平方函数
final <- do.call('c',results)#整合结果
stopCluster(cl) # 关闭集群
}))
#结果
用户  系统  流逝
 7.89  0.27 19.01  
#普通计算
print(system.time({
    results <- lapply(num,square)
    final <- do.call('c',results)#整合结果
}))
#结果
用户  系统  流逝
29.74  0.00 29.79

显然在数据量比较大的时候,并行计算的时间几乎就是于核数反比。不过,也不是多开几个核就好,注意内存很容易超支的,每个核都分配相应的内存,所以要注意内存开销。出现内存问题的时候,需要检查是否代码是否合理,R语言版本(64位会比32位分配的内存大),核分配是否合理。

上一级环境中变量的引入

R语言里边对于环境变量有着有趣的定义,一层套一层,这里不做深入展开。

类似于在c语言函数中使用全局变量,R在执行并行计算的时候,如果需要计算的函数出现在全局(上一级),那么就需要声明引入这个变量,否则将会报错。

#定义计算幂函数
base = 2
square <- function(x)
{
    return(x^base)
}
num <- c(1:1000000)
#利用并行计算计算幂函数
cl <- makeCluster(4) # 初始化四核心集群
results <- parLapply(cl,num,square)#调用parLapply并行计算平方函数
final <- do.call('c',results)#整合结果
stopCluster(cl) # 关闭集群
#结果报错
Error in checkForRemoteErrors(val) :
  4 nodes produced errors; first error: 找不到对象'base'
#利用并行计算计算幂函数
cl <- makeCluster(4) # 初始化四核心集群
clusterExport(cl,"base",envir = environment())
results <- parLapply(cl,num,square)#调用parLapply并行计算平方函数
final <- do.call('c',results)#整合结果
stopCluster(cl) # 关闭集群
#结果
> final
[1] 1,4,9,16,25.......

foreach包

除了parallel包以外,还有针对并行for循环的foreach包,foreach()的使用也与parLapply()类似,两个功能也类似,其中遇到的问题也类似。

包的安装

install.packages("foreach")
library(parallel)

foreach的使用

#定义计算幂函数
square <- function(x)
{
    return(x^2)
}

非并行情况的使用:

参数中的combine就是整合结果的函数,可以是c,可以是rbind,也可以是+等

results = foreach(x = c(1:3),.combine = 'c') %do% square(x)
#结果
> results
[1] 1,4,9

并行情况的使用:

注意并行情况的时候,需要与parallel包进行配合,引入library(doParallel)。同时%do%需要改成%dopar%。另外与parallel包不一样的是,需要多加一句registerDoParallel(cl)来注册核进行使用。

cl <- makeCluster(4)
registerDoParallel(cl)
results = foreach(x = c(1:100000),.combine = 'c') %dopar% square(x)
stopCluster(cl)

上一级环境中变量的引入

同parallel包并行计算前需要clusterExport()来引入全局变量一样,foreach也同样需要声明,不同的是,foreach声明方式直接写在foreach()的参数export里边。

#定义计算幂函数
base = 2
square <- function(x)
{
    return(x^base)
}
cl <- makeCluster(4)
registerDoParallel(cl)
results = foreach(x = c(1:100000),.combine = 'c',.export ='base' ) %dopar% square(x)
stopCluster(cl)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • 在R语言中实现Logistic逻辑回归的操作

    逻辑回归是拟合回归曲线的方法,当y是分类变量时,y = f(x).典型的使用这种模式被预测Ÿ给定一组预测的X.预测因子可以是连续的,分类的或两者的混合. R中的逻辑回归实现 R可以很容易地拟合逻辑回归模型.要调用的函数是glm(),拟合过程与线性回归中使用的过程没有太大差别.在这篇文章中,我将拟合一个二元逻辑回归模型并解释每一步. 数据集 我们将研究泰坦尼克号数据集.这个数据集有不同版本可以在线免费获得,但我建议使用Kaggle提供的数据集. 目标是预测生存(如果乘客幸存,则为1,否则为0)基于

  • R语言中cut()函数的用法说明

    R语言cut()函数使用 cut()切割将x的范围划分为时间间隔,并根据其所处的时间间隔对x中的值进行编码. 参数:breaks:两个或更多个唯一切割点或单个数字(大于或等于2)的数字向量,给出x被切割的间隔的个数. breaks采用fivenum():返回五个数据:最小值.下四分位数.中位数.上四分位数.最大值. labels为区间数,打标签 ordered_result 逻辑结果应该是一个有序的因素吗? 先用fivenum求出5个数,再用labels为每两个数之间,贴标签,采用(]的区间,

  • 基于R语言赋值符号的区别说明

    R语言赋值可以用=或<-,一般都建议使用<-,那你知道这两个之间的区间吗?那你有没有见过'<-'和'='这种赋值方法吗?今天就来和大家聊聊这基本的赋值符号都有哪些区别. 首先我们来看看符号的优先级,和java,c这些编程语言的优先级类似.下面这些都取自R帮助文档,输入?Syntax即可查看,它是根据优先级从高到低排列的. :: ::: access variables in a namespace $ @ component / slot extraction [ [[ indexing

  • R语言-summary()函数的用法解读

    summary():获取描述性统计量,可以提供最小值.最大值.四分位数和数值型变量的均值,以及因子向量和逻辑型向量的频数统计等. 结果解读如下: 1. 调用:Call lm(formula = DstValue ~ Month + RecentVal1 + RecentVal4 + RecentVal6 + RecentVal8 + RecentVal12, data = trainData) 当创建模型时,以上代码表明lm是如何被调用的. 2. 残差统计量:Residuals Min 1Q M

  • R语言-有负下标里才能有零介绍

    1.只有负下标里才能有零 先看一个例子 >a<-c(1,2,3,4) >a[-1:1] > a[-1:1] Error in a[-1:1] : 只有负下标里才能有零 (1)只有负下标里才能有零,在这里的意思为: a[-1:0] 可行 a[0:4]也可行 a[-1:1]不可行 也就是说要么是负索引到0,或者0到正索引,但不能同时出现正负索引. (2)a[0]结果为numberic(0),结果没有意义,如 > a[0]+10.9 numeric(0) > a[1]+10.

  • R语言通过parallel包实现多线程运行方式

    总的来说,R的运算速度不算快,不过类似并行运算之类的改进可以提高运算的性能.下面非常简要地介绍如何利用R语言进行并行运算 library(parallel) cl.cores <- detectCores() cl <- makeCluster(cl.cores) detectCores( )检查当前电脑可用核数. makeCluster(cl.cores)使用刚才检测的核并行运算.R-Doc里这样描述makeCluster函数:Creates a set of copies of R run

  • R语言中c()函数与paste()函数的区别说明

    c()函数:将括号中的元素连接起来,并不创建向量 paste()函数:连接括号中的元素 例如 c(1, 2:4),结果为1 2 3 4 paste(1, 2:4),结果为"1 2" "1 3" "1 4" c(2, "and"),结果为"2" "and" paste(2, "and"),结果为"2 and" 补充:R语言中paste函数的参数sep

  • R语言向量下标操作

    向量下标即元素在向量中的位置,在实践中我们可以利用下标(元素的位置)来找出自己想要的数. 利用runif函数生成包含10个正整数的向量x. options(digits = 1) set.seed(1234) x <- runif(10,min = 1,max = 20) x [1] 3 13 13 13 17 13 1 5 14 11 正整数下标 我们可以输入正整数作为下标来找出对应位置的元素. 在[]内输入下标. #向量x的第一位置的元素 x[1] [1] 3 #向量x的第2位置的元素 x[

  • R语言多线程运算操作(解决R循环慢的问题)

    已经大半年没有更新博客了..最近都跑去写分析报告半年没有R 这次记录下关于R循环(百万级以上)死慢死慢的问题,这个问题去年就碰到过,当时也尝试过多线程,but failed......昨天试了下,终于跑通了,而且过程还挺顺利 step1 先查下自己电脑几核的,n核貌似应该选跑n个线程,线程不是越多越好,线程个数和任务运行时间是条开口向下的抛物线,最高点预计在电脑的核数上. detectCores( )检查当前电脑可用核数 我的是4所以step2选的是4 library(parallel) cl.

  • R语言对二进制文件操作详解

    二进制文件是包含仅以位和字节(0和1)的形式存储的信息的文件.它们不是人类可读的,因为它中的字节转换为包含许多其他不可打印字符的字符和符号.尝试使用任何文本编辑器读取二进制文件将显示如Ø和ð的字符. 二进制文件必须由特定程序读取才能使用.例如,Microsoft Word程序的二进制文件只能通过Word程序读取到人类可读的形式.这表示,除了人类可读的文本之外,还有更多的信息,例如字符和页码等的格式化,它们也与字母数字字符一起存储.最后一个二进制文件是一个连续的字节序列.我们在文本文件中看到的换行

  • R语言数据预处理操作——离散化(分箱)

    一.项目环境 开发工具:RStudio R:3.5.2 相关包:infotheo,discretization,smbinning,dplyr,sqldf 二.导入数据 # 这里我们使用的是鸢尾花数据集(iris) data(iris) head(iris) Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 5.1 3.5 1.4 0.2 setosa 2 4.9 3.0 1.4 0.2 setosa 3 4.7 3.2 1.

  • R语言编码问题的解决

    今天博客主要是解决一个学R语言里面最最痛苦的问题(嗯,python2.x里面也有),就是中文乱码问题: 一般用R语言的同学,多半遇见过以下这个问题: 反正虾神我每次遇见这个问题,立刻就是: 实际上这个问题写代码的人一般都能遇见,不同字符编码的问题,如果是从文件里面读的还好说,可以带这个字符编码集一起读,但是如果这个数据是来自于数据库的,如果经验的话,那就真得各种凉拌了. 下面我简单把我今天通过R语言从Postgresql数据库中处理中文问题的整个流程说说,如果哪位同学也遇上了,可以参考我的整个解

  • R语言实现二进制文件读写操作

    二进制文件是一个文件,其中包含仅以位和字节形式存储的信息(0和1),它们是不可读的,因为其中的字节转换为包含许多其他不可打印字符的字符和符号,随便我们尝试使用任何文本编辑器读取二进制文件将显示为类似Ø和ð这样的字符. 但是二进制文件必须由特定程序读取才能使用.例如,Microsoft Word程序的二进制文件只能通过Word程序读取到人类可读的形式.这表明,除了人类可读的文本之外,还有更多的信息,如格式化的字符和页码等,它们也与字母数字字符一起存储.最后,二进制文件是一个连续的字节序列. 我们在

  • R语言中向量和矩阵简单运算的实现

    一.向量运算 向量是有相同基本类型的元素序列,一维数组,定义向量的最常用办法是使用函数c(),它把若干个数值或字符串组合为一个向量. 1.R语言向量的产生方法 > x <- c(1,2,3) > x [1] 1 2 3 2.向量加减乘除都是对其对应元素进行的,例如下面 > x <- c(1,2,3) > y <- x*2 > y [1] 2 4 6 (注:向量的整数除法是%/%,取余是%%.) 3.向量的内积,有两种方法. 第一种方法:%*% > x

  • R语言对CSV文件操作实例讲解

    在 R 语言中,我们可以从存储在 R 语言环境外的文件中读取数据. 我们还可以将数据写入将被操作系统存储和访问的文件. R 语言可以读取和写入各种文件格式,如​csv​,​excel​,​xml​等. 在本章中,我们将学习从​csv​文件读取数据,然后将数据写入​csv​文件. 该文件应该存在于当前工作目录中,以便 R 语言可以读取它. 当然我们也可以设置我们自己的目录并从那里读取文件. 获取和设置工作目录 您可以使用​getwd()​函数检查R语言工作区指向的目录. 您还可以使用​setwd(

  • R语言操作XML文件实例分析

    XML是一种文件格式,它使用标准ASCII文本共享万维网,内部网和其他地方的文件格式和数据. 它代表可扩展标记语言(XML). 类似于HTML它包含标记标签. 但是与HTML中的标记标记描述页面的结构不同,在xml中,标记标记描述了包含在文件中的数据的含义. 您可以使用"XML"包读取R语言中的xml文件. 此软件包可以使用以下命令安装. install.packages("XML") 输入数据 通过将以下数据复制到文本编辑器(如记事本)中来创建XMl文件. 使用.

  • R语言的Dataframe常用操作使用

    上节我们简单介绍了Dataframe的定义,这节我们具体来看一下Dataframe的操作 首先,数据框的创建函数为 data.frame( ),参考R语言的帮助文档,我们来了解一下data.frame( )的具体用法: Usage data.frame(..., row.names = NULL, check.rows = FALSE, check.names = TRUE, fix.empty.names = TRUE, stringsAsFactors = default.stringsAs

随机推荐