在 R 中,for 循环比在 Python 中使用 rpy2 库快几倍
For loop is several times faster in R than in Python using the rpy2 library
下面的简单 for
块大约需要 ~3 秒才能在 R
中完成:
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 到 rpy2
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 中的代码)?
当然,连接到外部语言 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))
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 包中的 all 对象创建映射)以及从 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 的对话的代码结构的显着改进(以及一些棘手的错误已修复),但会以暂时的性能为代价。正在逐步重新引入速度优化。
下面的简单 for
块大约需要 ~3 秒才能在 R
中完成:
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 到 rpy2
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 中的代码)?
当然,连接到外部语言 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))
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 包中的 all 对象创建映射)以及从 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 的对话的代码结构的显着改进(以及一些棘手的错误已修复),但会以暂时的性能为代价。正在逐步重新引入速度优化。