RProvider
RProvider copied to clipboard
Suggest some Extension methods to allow fluent-style accessing R objects
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 or RProvider adopt some extensions to allow such fluent-style of accessing the data members of R objects. This issue is raised in RDotNet community, I hope RProvider can also do something about it.
I think the Member
extension could probably be a dynamic operator, so that you can write:
test?test?p_value
There is also an issue for better support for S3/S4 values: https://github.com/BlueMountainCapital/FSharpRProvider/issues/8
Generally speaking, I think these are all useful extensions. Would you be able to submit a pull request with these?
Seems like a dup of #63.
Yes, closing this one.