Suggest some Extension methods to allow fluent-style accessing R objects

Oct 24, 2013 at 1:19 AM
Edited Oct 24, 2013 at 12:49 PM
RDotNet provides SymbolicExpression object to get access to R objects. Most R objects are constructed as Lists.

For example, a t-test result is a list of statistics and other things, a linear model (lm) is a list of coefficients, residuals and other useful list members. However, it is quite inconvenient to extract the members from a list without chaining several methods and convert the value to appropriate types.

In some packages, the R functions returns S4 object. For example, fUnitRoots package provides a unitrootTest function which returns S4 object that stores much richer information than offered by official R stats packages. But it's a nightmare to extract wanted information from these Lists or the slots of S4 objects. R methods package provide slot() function to extract slots from S4 objects.

As a user of C# and F#, I just want to conveniently get access to the inner information of the R objects without caring too much about the nature of the R object.

Here I developed a very rough RDotNet.Extensions project using F# and RProvider to extend SymbolicExpression:
module RDotNet.Extensions

open RDotNet
open RDotNet.Internals
open RProvider
open RProvider.``base``
open RProvider.methods

type SymbolicExpression with
    /// Get the member symbolic expression of List or S4 object.
    member this.Member (name: string) =
        match this.Type with
        | SymbolicExpressionType.List -> this.AsList().[name]
        | SymbolicExpressionType.S4 -> R.slot(this,name)
        | _ -> invalidOp "Unsupported operation on R object"

    /// Convert the symbolic expression to a typed array.
    member this.ToArray<'a> () = this.Value :?> 'a[]

    /// Get the value from the typed vector by name.
    member this.ValueOf<'a> (name: string) =
        match this.Type with
        | SymbolicExpressionType.NumericVector -> box(this.AsNumeric().[name]) :?> 'a
        | SymbolicExpressionType.CharacterVector -> box(this.AsCharacter().[name]) :?> 'a
        | SymbolicExpressionType.LogicalVector -> box(this.AsLogical().[name]) :?> 'a
        | SymbolicExpressionType.IntegerVector -> box(this.AsInteger().[name]) :?> 'a
        | SymbolicExpressionType.ComplexVector -> box(this.AsComplex().[name]) :?> 'a
        | SymbolicExpressionType.RawVector -> box(this.AsRaw().[name]) :?> 'a
        | _ -> invalidOp "Unsupported operation on R object"

    /// Get the value from the typed vector by index.
    member this.ValueAt<'a> (index: int) = box(this.ToArray<'a>().[index]) :?> 'a

    /// Get the first value from the typed vector.
    member this.First<'a> () = this.ValueAt<'a>(0)

    /// Assign this symbolic expression to a string symbolic identifier in current R environment.
    member this.AssignTo(name:string) = R.assign(name,this) |> ignore
This extension currently allows me to write code in a fluent style like this:
    let dic = dict [("ma",R.c(0.6,0.4,0.1).Value)]
    let list = R.list(dic)
    let data1 = R.arima_sim(model=list,n=500)
    let model = R.arima(x=data1,order=R.c(0,0,5))
    let coef:double[] = model.Member("coef").ToArray()
    let data = R.rnorm(1000)
    let test = R.unitrootTest(data)
    let pvalue:double = test.Member("test").Member("p.value").ValueOf("n") // test is S4 object
I suggest that RDotNet adopt some extensions to allow such fluent-style of accessing the data members of R objects.
Oct 24, 2013 at 12:14 PM
Thank you for the feedback, and the sample code. That really makes me want to really try F#, too. Have not managed to explore it via project work.

I agree some additional syntactic facilities, via C# extension methods or other means, would be beneficial. But there is also some more work to do to remove some more basic difficulties that many users have, document, and make testing more comprehensive. While I cannot speak for others, time is my limiting factor for R.NET contributions.

If you were to contribute some code in F# for fluent R.NET programming, what would be available to C# programmers? I understand that type providers is F# only, but are extension methods working across languages?

If you have a pretty good idea of the API you think of, fork/pull request would be appreciated. Note however that the code version infrastructure is likely to change in the coming weeks/months, so holding back a bit may be preferable.
Oct 24, 2013 at 2:06 PM
Thank you for the suggestion.

I agree R.NET is not a lightweight library. R.NET is designed to give type safety to R types, to some extent. The extent could be decided by users themselves. Some users may want to do many in R, and others may do many in .NET.

Such a wrapper is welcome. But I don't think R.NET itself should become so.