Wrapping a simple R package

I want to wrap a simple ML package: sprintr.
In R the code is straight-forward:

library(sprintr); 
m = cv.sprinter(x = X, y = y)
pred = predict(m, newdata = XH)

In Julia it works in 1-step:

X = randn(100,5)
y = 7.0 .+ X[:,1] .+ 2.0 .* X[:,2] .* X[:,3] + 3*randn(100)
using RCall

function sprintr(X, y, XH)
  @rput y X XH # put data from Julia to R.
  R"""
  library(sprintr); 
  m = cv.sprinter(x = X, y = y)
  pred = predict(m, newdata = XH)
  """
  return @rget pred  
end
sprintr(X, y, X) #1 step works fine

Now in 2-steps fit() and predict():

function fitsprintr(X, y)
  @rput y X # put data from Julia to R.
  R"""
  library(sprintr); 
  m = cv.sprinter(x = X, y = y)
  """
  return @rget m  
end
function predictsprintr(m, XH)
  @rput m XH # put data from Julia to R.
  R"""
  library(sprintr); 
  pred = predict(m, newdata = XH)
  """
  return @rget pred  
end

It breaks in the 2nd step:

julia> ms = fitsprintr(X, y)
OrderedCollections.OrderedDict{Symbol,Any} with 12 entries:
  :n       => 100
  :p       => 5
  :a0      => 6.88335
  :compact => [0.0 1.0 0.981719; 0.0 2.0 0.801296; … ; 3.0 5.0 0.0285869; 2.0 3.0 1.46116]
  :fit     => OrderedCollections.OrderedDict{Symbol,Any}(:n=>100,:p=>5,:a0=>[6.91957, 6.91288, 6.91104, 6.91314, 6.91492, 6.91654, 6.91801, 6.91434, 6.91025, 6.90653  …  7.21865, 7.21867, 7.21869, 7.21871, 7.21873, 7.21874, 7.…
  :fitted  => [4.00406, 5.49018, 6.72736, 8.49206, 7.45682, 2.46833, 6.28563, 5.69402, 6.96177, 7.84651  …  6.00993, 7.37945, 7.11813, 7.2942, 5.92529, 8.71639, 7.63545, 5.86258, 7.62806, 6.30384]
  :lambda  => [1.74206, 1.5873, 1.44629, 1.3178, 1.20073, 1.09406, 0.996869, 0.90831, 0.827618, 0.754095  …  0.000402438, 0.000366686, 0.000334111, 0.00030443, 0.000277385, 0.000252743, 0.00023029, 0.000209831, 0.000191191, 0.…
  :cvm     => [3.90709, 3.86828, 3.81234, 3.7361, 3.64678, 3.55014, 3.46599, 3.39412, 3.33097, 3.28259  …  3.33651, 3.33662, 3.33672, 3.33682, 3.3369, 3.33698, 3.33705, 3.33711, 3.33717, 3.33722]
  :cvsd    => [0.425856, 0.42167, 0.417286, 0.399112, 0.384055, 0.366016, 0.353135, 0.345567, 0.341965, 0.33731  …  0.627155, 0.627222, 0.627282, 0.627338, 0.627388, 0.627434, 0.627476, 0.627514, 0.627549, 0.62758]
  :foldid  => [4, 4, 1, 3, 3, 1, 3, 5, 2, 3  …  3, 1, 2, 2, 1, 2, 1, 5, 2, 3]
  ⋮        => ⋮
julia> predictsprintr(ms, X)
ERROR: MethodError: no method matching sexpclass(::Expr)
Closest candidates are:
  sexpclass(::UInt8) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/convert/default.jl:236
  sexpclass(::Dates.Date) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/convert/default.jl:260
  sexpclass(::Dates.DateTime) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/convert/default.jl:265
  ...
Stacktrace:
 [1] sexp(::Expr) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/convert/default.jl:214
 [2] setindex!(::Ptr{VecSxp}, ::Expr, ::Int64) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/methods.jl:188
 [3] sexp(::Type{RCall.RClass{:list}}, ::OrderedCollections.OrderedDict{Symbol,Any}) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/convert/base.jl:281
 [4] sexp at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/convert/default.jl:214 [inlined]
 [5] setindex!(::Ptr{VecSxp}, ::OrderedCollections.OrderedDict{Symbol,Any}, ::Int64) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/methods.jl:188
 [6] sexp(::Type{RCall.RClass{:list}}, ::OrderedCollections.OrderedDict{Symbol,Any}) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/convert/base.jl:281
 [7] sexp at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/convert/default.jl:214 [inlined]
 [8] setindex!(::Ptr{EnvSxp}, ::OrderedCollections.OrderedDict{Symbol,Any}, ::Symbol) at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/methods.jl:546
 [9] setindex! at /Users/AZevelev/.julia/packages/RCall/eRsxl/src/methods.jl:555 [inlined]
 [10] predictsprintr(::OrderedCollections.OrderedDict{Symbol,Any}, ::Array{Float64,2}) at ./REPL[34]:2
 [11] top-level scope at REPL[43]:1

I think the problem is putting m back into R from Julia.
This is an issue for me bc to wrap an ML package I need separate functions for fit and predict

2 Likes

Hi Alex,

I think something as follows should work:

function fitsprintr(X, y)
    @rput y X # put data from Julia to R.
    R"""
    library(sprintr);
     m <- cv.sprinter(x = X, y = y)
    """
    @rget m
    delete!(m, :call)
    delete!(m[:fit], :call)
    m
end
function predictsprintr(m, XH)
    @rput m XH # put data from Julia to R.
    R"""
    library(sprintr);
    class(m) <- 'cv.sprinter'
    pred <- predict(m, newdata = XH)
    """
    return @rget pred
end

I am not sure why RCall cannot convert ::Expr from Julia to R (the first part takes care of this by removing all expressions). Furthermore, the R class information is lost when the R object is converted to a Julia object (the second function handles this). A more elegant solution could use RCall’s custom conversions (Custom Conversion · RCall.jl).

1 Like

@nignatiadis

  1. welcome to Discourse, it’s great to have you here
  2. it works well. Thank you!!!
  3. it’s strange this is so awkward to do in RCall.jl

Now I can slowly start wrapping my fav R packages into Julia!

1 Like