使用 rpy2 从 python 动态调用 R 库

dynamically calling R library from python using rpy2

基于:

我有一个 R 例程,我需要以动态方式从我的 python 代码中调用它。 为此,我打算使用 rpy2。

首先我想使用的 R 代码来自 python(第一次 R 用户):

设置虚拟数据以展示 R 例程用法

 set.seed(101)
 data_sample <- c(5+ 3*rt(1000,df=5),
        10+1*rt(10000,df=20))

 num_components <- 2

例程本身

library(teigen)
 tt <- teigen(data_sample,
        Gs=num_components,  
        scale=FALSE,dfupdate="numeric",
        models=c("univUU") 
 )

df = c(tt$parameters$df)
mean = c(tt$parameters$mean)
scale = c(tt$parameters$sigma)

参数 data_samplenum_components 由我的 python 代码动态计算,其中 num_components 它只是一个整数, data_sample 一个 numpy 数组。

作为最终目标,我希望 dfmeanscale 作为列表或 numpy 数组返回 "python world" 以进一步处理和使用它们在我的程序逻辑中。

到目前为止我用 rpy2 解决这个问题的第一个实验:

import rpy2
from rpy2.robjects.packages import importr
from rpy2 import robjects as ro

numpy_t_mix_samples = get_student_t_data(n_samples=10000)

r_t_mix_samples = ro.FloatVector(numpy_t_mix_samples)

teigen = importr('teigen')
rres = teigen.teigen(r_t_mix_samples, Gs=2, scale=False, dfupdate="numeric", models=c("univUU"))

此处 Gs 的参数仍然是硬编码的,但应该如上文所述是动态的。

rres 然后打印大部分难以理解的输出(我猜是因为它还没有用 rpy2 正确地转换):

R object with classes: ('teigen',) mapped to:
<ListVector - Python:0x11e3fdc48 / R:0x7ff7d229dcb0>
[Float..., Matrix, ListV..., ..., Float..., ListV..., ListV...]
  iter: <class 'rpy2.robjects.vectors.FloatVector'>
  R object with classes: ('numeric',) mapped to:
<FloatVector - Python:0x11e3fdd08 / R:0x7ff7cced0a28>
[156.000000]
  fuzzy: <class 'rpy2.robjects.vectors.Matrix'>
  R object with classes: ('matrix',) mapped to:
<Matrix - Python:0x11e3fd8c8 / R:0x118e78000>
[0.000000, 0.917546, 0.004050, ..., 0.077300, 0.076273, 0.091252]
R object with classes: ('teigen',) mapped to:
<ListVector - Python:0x11e3fdc48 / R:0x7ff7d229dcb0>
[Float..., Matrix, ListV..., ..., Float..., ListV..., ListV...]
  ...
  iter: <class 'rpy2.robjects.vectors.FloatVector'>
  R object with classes: ('numeric',) mapped to:
<FloatVector - Python:0x11d632508 / R:0x7ff7cfa81658>
[-25365.912426]
R object with classes: ('teigen',) mapped to:
<ListVector - Python:0x11e3fdc48 / R:0x7ff7d229dcb0>
[Float..., Matrix, ListV..., ..., Float..., ListV..., ListV...]
R object with classes: ('teigen',) mapped to:
<ListVector - Python:0x11e3fdc48 / R:0x7ff7d229dcb0>
[Float..., Matrix, ListV..., ..., Float..., ListV..., ListV...]

总而言之,我希望获得与第一个代码框中原始 R 示例相同的结果,只是 df、均值和比例变量是 python lists/numpy 数组。 我根本不知道 R 的事实使得使用 rpy2 变得非常困难,也许有更优雅的方法来动态调用这个例程并将结果返回 python world.

考虑使用 x.names.index('myname') 来引用 R 对象中的嵌套命名元素。参见 rpy2 docs。作为提醒和下面的演示,您仍然可以使用数字索引引用 R 和 Python 嵌套对象。

要使用精确的随机数据重现您的 R 对象,我们需要 运行 R 端的 set.seed,因为没有简单的方法可以跨语言找到等效的随机数生成器。参见相关 post。最后,基数 R 的 as.vector() 用于将数组对象转换为向量。 Python 中的所有 returns 都是 R FloatVectors: <class 'rpy2.robjects.vectors.FloatVector'>.

Python

from rpy2.robjects.packages import importr

base = importr('base')
stats = importr('stats')
teigen = importr('teigen')

base.set_seed(101)
data_sample = base.as_numeric([(5+3*i) for i in stats.rt(1000,df=5)] + \
                              [(10+1*i) for i in stats.rt(10000,df=20)])

num_components = 2

rres = teigen.teigen(data_sample, Gs=num_components, scale=False, 
                     dfupdate="numeric", models="univUU")

# BY NUMBER INDEX
df = rres[2][0]
mean = base.as_vector(rres[2][1])
scale = base.as_vector(rres[2][3])

print(df)
# [1]  3.578491 47.059841
print(mean)
# [1]  4.939179 10.002038
print(scale)
# [1] 8.763076 1.041588


# BY NAME INDEX 
# (i.e., find corresponding number to name in R object)
params = rres[rres.names.index('parameters')]

df = params[params.names.index('df')]
mean = base.as_vector(params[params.names.index('mean')])
scale = base.as_vector(params[params.names.index('sigma')])

print(df)
# [1]  3.578491 47.059841
print(mean)
# [1]  4.939179 10.002038
print(scale)
# [1] 8.763076 1.041588

R (等效脚本)

library(teigen)

set.seed(101)
data_sample <- c(5+ 3*rt(1000,df=5),
                 10+1*rt(10000,df=20))
num_components <- 2

tt <- teigen(data_sample, Gs=num_components, scale=FALSE, 
             dfupdate="numeric", models="univUU")    

# BY NUMBER INDEX
df = tt[[3]][[1]]
mean = as.vector(tt[[3]][[2]])
scale = as.vector(tt[[3]][[4]])

print(df)
# [1]  3.578491 47.059841     
print(mean)
# [1]  4.939179 10.002038     
print(scale)
# [1] 8.763076 1.041588

# BY NAME INDEX
df = c(tt$parameters$df)
mean = c(tt$parameters$mean)
scale = c(tt$parameters$sigma)

print(df)
# [1]  3.578491 47.059841    
print(mean)
# [1]  4.939179 10.002038    
print(scale)
# [1] 8.763076 1.041588