为了账号安全,请及时绑定邮箱和手机立即绑定

使用 rpy2 库,R 中的 For 循环比 Python 中的循环快几倍

使用 rpy2 库,R 中的 For 循环比 Python 中的循环快几倍

慕姐8265434 2023-03-16 16:09:06
以下简单的for块大约需要 ~3 秒才能完成R:library(MASS)nruns <- 2000nelems <- 50maxX <- 1maxY <- 1for(i in 1:nruns) {    dataX <- runif(nelems, 0, maxX)    dataY <- runif(nelems, 0, maxY)    kde2d(dataX, dataY, n=50, lims=c(0, maxX, 0, maxY) )}rpy2通过库在 Python 中运行相同的代码需要 4-5 倍的时间:from rpy2.robjects import rfrom rpy2.robjects.packages import importrimportr('MASS')nruns = 2000r.assign('nelems', 50)r.assign('maxX', 1)r.assign('maxY', 1)for _ in range(nruns):    r('dataX <- runif(nelems, 0, maxX)')    r('dataY <- runif(nelems, 0, maxY)')    r('kde2dmap <- kde2d(dataX, dataY, n=50, lims=c(0, maxX, 0, maxY))')这仅仅是因为我正在使用rpy2图书馆进行交流R还是有其他原因在起作用?这可以以任何方式改进吗(同时仍然在 Python 中运行代码)?
查看完整描述

2 回答

?
慕娘9325324

TA贡献1783条经验 获得超4个赞

慢 4 到 5 倍似乎有点多,但如果您使用自定义转换(rpy2 可以动态地将 R 对象转换为任意 Python 对象 - 请参阅文档)。


或者可能是您在 HPC 上对安装 Python 和包的位置进行较慢的 NFS 访问,而 R 在更快的本地磁盘上(这可能会对启动时间产生很大影响)。


否则,也可以将循环保留在 R 中以评估这是否会改变运行时间:


from rpy2.robjects import r

from rpy2.robjects.packages import importr


# importr('MASS')

# Calling 'importr' will perform quite a bit of work behind the

# scene. That works allows a more intuitive/pythonic use of the

# content of the R library "MASS", but if you are just passing

# a string to be evaluated for R evaluation you can skip it

# replace it with the following:

r('library("MASS")')


nruns = 2000

r.assign('nelems', 50)

r.assign('maxX', 1)

r.assign('maxY', 1)

r.assign('nruns', nruns)

r("""

for(i in 1:nruns) {

  dataX <- runif(nelems, 0, maxX)

  dataY <- runif(nelems, 0, maxY)

  kde2dmap <- kde2d(dataX, dataY, n=50, lims=c(0, maxX, 0, maxY) )

}

""")

速度改进将来自以下方面:


您问题中的代码在每次迭代中传递 Python 字符串。每次在评估该字符串之前都必须(通过 R)对其进行解析。这可能会增加一些长循环的开销。在我提供的代码中,解析只执行一次。


importr()@Parfait 答案中的代码利用了为要使用的 R 函数创建 Python 对象代理这一事实。但是,在创建映射时(为 R 包中的所有importr()对象创建映射)以及从 Python 到 R 的每次迭代(对象检查和转换、构建要评估的 R 表达式)时,仍然存在开销。对代码进行概要分析可以让您准确了解时间花在了哪里。有一些方法可以在保留更多性能的同时保留一些 pythonic 方面。例如:


 import rpy2.rinterface as ri

 ri.initr()

 ri.baseenv['library']("MASS")

 # early bindings for R functions:

 runif = ri.globalenv.find('runif')

 kde2d = ri.globalenv.find('kde2d')

 # create constant values in loop as R objects

 maxX = ri.IntVector((1, ))

 maxY = ri.IntVector((1, ))

 nelems = ri.IntVector((50, ))

 zero = ri.IntVector((0, ))

 limits = ri.IntVector((0, maxX[0], 0, maxY[0]))

 for i in range(nruns):

     dataX = runif(nelems, zero, maxX)

     dataY = runif(nelems, zero, maxY)

     kde2dmap = kde2d(dataX, dataY, n=nelems, lims=limits)

关于性能的另一个评论是,rpy2 从 C 扩展到的过渡cffi导致使用 R 的 C API 管理对话的代码结构有了显着改进(并修复了许多棘手的错误),但代价是暂时的在这里和那里的表现。正在逐步重新引入速度优化。


查看完整回答
反对 回复 2023-03-16
?
紫衣仙女

TA贡献1839条经验 获得超15个赞

自然地,连接到外部语言 API 比直接运行外部语言要慢。但是,请考虑将所有内容都保留在 Python 层中并避免r()调用。


from rpy2.robjects.packages import importr


base = importr("base")

stats = importr("stats")

mass = importr("MASS")


nruns = 2000

nelems = 50

maxX = 1

maxY =  1


for _ in range(nruns):

    dataX = stats.runif(nelems, 0, maxX)

    dataY = stats.runif(nelems, 0, maxY)

    kde2dmap = mass.kde2d(dataX, dataY, n=50, lims=base.c(0, maxX, 0, maxY))


查看完整回答
反对 回复 2023-03-16
  • 2 回答
  • 0 关注
  • 68 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信