在R中,For循环的速度是使用rpy2库的Python中的几倍

2024-05-16 12:55:12 发布

您现在位置:Python中文网/ 问答频道 /正文

以下简单的for块在R中完成大约需要3秒:

library(MASS)
nruns <- 2000
nelems <- 50
maxX <- 1
maxY <- 1
for(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) )
}

在Python中通过^{} library运行相同的代码需要4-5倍的时间:

from rpy2.robjects import r
from rpy2.robjects.packages import importr
importr('MASS')

nruns = 2000
r.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中运行代码)


Tags: inforlibrarymasslimsassignrpy2nruns
2条回答

当然,连接到外部语言API的速度会比直接运行外部语言慢。但是,请考虑将所有内容保持在Python层,避免^ {< CD1> }调用。

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))

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)进行解析,然后才能对其进行求值。对于长循环,这可能相当于一些开销。在我提供的代码中,解析只执行一次

  • @Parfait答案中的代码利用了importr()为您想要使用的R函数创建Python对象代理这一事实。但是,在使用importr()创建映射时(为R包中的所有对象创建映射),以及在从Python到R的每次迭代中(对象检查和转换,构建要计算的R表达式),仍然存在开销。分析代码将为您提供花费时间的确切明细。有一些方法可以在保留更多性能的同时保留一些python方面。例如:

     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管理对话框的代码结构的显著改进(以及修复的一些棘手的错误),但这是以临时性能为代价的。正在逐步重新引入速度优化

相关问题 更多 >