从rpy2传递给R的对象是什么?
我无法让下面的代码正常工作,虽然在R语言中我没有看到这个错误。
from rpy2.robjects.packages import importr
from rpy2 import robjects
import numpy as np
forecast = importr('forecast')
ts = robjects.r['ts']
y = np.random.randn(50)
X = np.random.randn(50)
y = ts(robjects.FloatVector(y), start=robjects.IntVector((2004, 1)), frequency=12)
X = ts(robjects.FloatVector(X), start=robjects.IntVector((2004, 1)), frequency=12)
forecast.Arima(y, xreg=X, order=robjects.IntVector((1, 0, 0)))
这让我特别困惑,因为下面的代码运行得很好。
forecast.auto_arima(y, xreg=X)
无论我给X什么值,我都能看到以下的错误追踪信息,不管我是否使用numpy接口。有什么想法吗?
---------------------------------------------------------------------------
RRuntimeError Traceback (most recent call last)
<ipython-input-20-b781220efb93> in <module>()
13 X = ts(robjects.FloatVector(X), start=robjects.IntVector((2004, 1)), frequency=12)
14
---> 15 forecast.Arima(y, xreg=X, order=robjects.IntVector((1, 0, 0)))
/home/skipper/.local/lib/python2.7/site-packages/rpy2/robjects/functions.pyc in __call__(self, *args, **kwargs)
84 v = kwargs.pop(k)
85 kwargs[r_k] = v
---> 86 return super(SignatureTranslatedFunction, self).__call__(*args, **kwargs)
/home/skipper/.local/lib/python2.7/site-packages/rpy2/robjects/functions.pyc in __call__(self, *args, **kwargs)
33 for k, v in kwargs.iteritems():
34 new_kwargs[k] = conversion.py2ri(v)
---> 35 res = super(Function, self).__call__(*new_args, **new_kwargs)
36 res = conversion.ri2py(res)
37 return res
RRuntimeError: Error in `colnames<-`(`*tmp*`, value = if (ncol(xreg) == 1) nmxreg else paste(nmxreg, :
length of 'dimnames' [2] not equal to array extent
编辑:
问题在于以下几行代码没有返回一个列名,这似乎是R语言那边的期望。
sub = robjects.r['substitute']
deparse = robjects.r['deparse']
deparse(sub(X))
我对这段代码在R语言中的期望不是很了解,但我找不到一个RPy2对象能通过这个检查,返回的结果长度是length == 1
。这在我看来真的像是个bug。
R> length(deparse(substitute((rep(.2, 1000)))))
[1] 1
但是在Rpy2中
[~/]
[94]: robjects.r.length(robjects.r.deparse(robjects.r.substitute(robjects.r('rep(.2, 1000)'))))
[94]:
<IntVector - Python:0x7ce1560 / R:0x80adc28>
[ 78]
2 个回答
有一种方法可以简单地把你的变量传递给R,而不需要复杂的情况,然后再把结果返回给Python。你可以在这里找到一个简单的例子 https://stackoverflow.com/a/55900840/5350311。这样你会更清楚地知道你传给R的是什么,以及你会得到什么结果,特别是当你在使用For循环和处理大量变量的时候。
这是一个表现形式(可以看看这个相关问题作为例子),它反映了同一个根本问题:R语言中的表达式是懒惰求值的,也就是说它们在需要的时候才会被计算,并且可以在R中进行操作。这导致了一些用法在其他语言中不太好理解(而在Python中,表达式是立即求值的,如果想要操作代码,就得转到抽象语法树(AST))。
接下来回答你问题的第二部分。在R中,substitute(rep(.2, 1000))
是将未计算的表达式rep(.2, 1000)
传递给substitute()
。在rpy2
中这样做:
substitute('rep(.2, 1000)')`
实际上是传递了一个字符串;而在R中相应的写法是:
substitute("rep(.2, 1000)")
接下来是让你接近R的deparse(substitute())
的方式:
from rpy2.robjects.packages import importr
base = importr('base')
from rpy2 import rinterface
# expression
e = rinterface.parse('rep(.2, 1000)')
dse = base.deparse(base.substitute(e))
>>> len(dse)
1
>>> print(dse) # not identical to R
"expression(rep(0.2, 1000))"
目前,有一种方法可以解决这个问题,就是将R对象绑定到R符号上(最好是在一个专门的环境中,而不是在全局环境中),然后在用字符串写的R调用中使用这些符号:
from rpy2.robjects import Environment, reval
env = Environment()
for k,v in (('y', y), ('xreg', X), ('order', robjects.IntVector((1, 0, 0)))):
env[k] = v
# make an expression
expr = rinterface.parse("forecast.Arima(y, xreg=X, order=order)")
# evaluate in the environment
res = reval(expr, envir=env)
我对这个解决方案并不满意,但我一直没时间去找更好的解决办法。
编辑:在rpy2-2.4.0版本中,可以使用R符号并做到以下几点:
RSymbol = robjects.rinterface.SexpSymbol
pairlist = (('x', RSymbol('y')),
('xreg', RSymbol('xreg')),
('order', RSymbol('order')))
res = forecast.Arima.rcall(pairlist,
env)
不过这还不是最直观的接口。也许使用上下文管理器会更好。