Douglas Bates
February 24, 2015
In [1]:
using DataArrays,DataFrames,RCall
The file defining the C API for R is R.h
in the directory whose name is ENV["R_INCLUDE_DIR"]
. In my case this is
In [2]:
joinpath(ENV["R_INCLUDE_DIR"],"R.h")
Out[2]:
R.h
causes inclusion of several other header files from that directory, the most important of which is Rinternals.h
.
A typical function declaration in Rinternals.h
is
Rboolean Rf_isVector(SEXP);
We can call this function directly from Julia using ccall
. We define a Julia function isRVector
to do this.
In [3]:
isRVector(s::SEXPREC) = ccall((:Rf_isVector,libR),Bool,(Ptr{Void},),s.p)
Out[3]:
In [4]:
"library(robustbase)" |> rcopy
Out[4]:
In [5]:
m1 = reval("m1 <- lmrob(Y ~ ., coleman, setting = 'KS2011')")
Out[5]:
In [6]:
isRVector(m1) # true because m1 is a list
Out[6]:
In [7]:
isRVector(m1[1]) # true because the coefficients are a numeric vector
Out[7]:
In [8]:
typeof(m1[18]) # the "call" component is a LANGSXP (function call)
Out[8]:
In [9]:
isRVector(m1[18]) # a LANGSXP does not behave like a vector
Out[9]:
We can simplify many of the ccall
expressions by creating a convert
method to produce a Ptr{Void}
from the Julia SEXP
type
Base.convert(::Type{Ptr{Void}},s::SEXP) = s.p
In fact, because all these predicate functions have the same signature, we can (and do) define corresponding Julia functions through metaprogramming.
## predicates applied to an SEXP
for sym in (:isArray,:isComplex,:isEnvironment,:isExpression,
:isFactor,:isFrame,:isFree,:isFunction,:isInteger,
:isLanguage,:isList,:isLogical,:isSymbol,:isMatrix,
:isNewList,:isNull,:isNumeric,:isNumber,:isObject,
:isOrdered,:isPairList,:isPrimitive,:isReal,
:isS4,:isString,:isTs,:isUnordered,:isUnsorted,
:isUserBinop,:isValidString,:isValidStringF,
:isVector,:isVectorAtomic,:isVectorizable,
:isVectorList)
@eval $sym(s::SEXP) = ccall(($(string("Rf_",sym)),libR),Bool(Ptr{Void},),s)
end
Many of these functions; isComplex
, isExpression
, isList
, isLogical
, isNewList
,isSymbol
, isReal
and isS4
, are redundant because the Julia SEXP
type is a templated type with the code built-in. At present only a few of these functions are exported.
Note that the meaning of Array
is slightly different for R
and for Julia
. In Julia
a vector is an Array
. In R
a vector is an Array
only if it has a dim
attribute.
In [10]:
RCall.isArray(m1[1]) # coefficients are a vector but not an R array
Out[10]:
In [11]:
RCall.isArray(m1[22]) # the model matrix is an array
Out[11]:
There are several methods for the sexp
generic function that create a Julia SEXP from other types of Julia objects.
For example, an R symbol is retrieved (installing it in the symbol table if necessary) via a C function whose signature is
SEXP Rf_install(const char *);
We define an sexp
method for the Symbol
type in Julia as
function sexp(s::Symbol)
sexp(ccall((:Rf_install,libR),Ptr{Void},(Ptr{Uint8},),
string(s)))
end
(If you know about R's garbage collection and are wondering why the result is not protected, objects in the symbol table are never garbage collected.)
In [12]:
sexp(:foo)
Out[12]:
In [13]:
rprint(ans)