Logo RProvider
#r "nuget: RProvider, 0.0.1-local"

Reading and writing R data files

When using R, you can save and load data sets as *.rdata and *.rds files. RProvider has an RData provider that enables you to work with statically typed rdata files (.rds are not supported yet; PRs welcome).

Working with .rdata files

Let's say that you have some data in R and want to pass them to F#. To do that, you can use the save function in R. The following R snippet creates a simple *.rdata file containing a couple of symbols from the sample volcano data set:

require(datasets)
volcanoList <- unlist(as.list(volcano))
volcanoMean <- mean(volcanoList)
symbols <- c("volcano", "volcanoList", "volcanoMean")
save(list=symols, file="C:/data/sample.rdata")

Loading in F#

To import the data on the F# side, you can use the RData type provider that is available in the RProvider namespace. It takes a static parameter specifying the path of the file and generates a type that exposes all the saved values as static members.

Note. You may use absolute or relative paths. If using a relative path, it is best practice to include ResolutionFolder = __SOURCE_DIRECTORY__ as below, because this makes the file resolution happen deterministically relative to the source file, whether that is an F# script, library, or app.

open RProvider

type Sample = RData<"data/sample.rdata", ResolutionFolder = __SOURCE_DIRECTORY__>
let sample = Sample()

// Access saved values directly as F# values
sample.volcano
sample.volcanoList
sample.volcanoMean

When accessed, the type provider automatically converts the data from the R format to F# format. In the above example, volcanoList is imported as float[] and the volcanoMean value is a float, as it is a single-item list in R. For the sample.volcano value, the R provider does not have a default conversion and so it is exposed as SymbolicExpression.

When you have multiple *.rdata files containing data in the same format, you can pick one of them as a sample (which will be used to determine the fields of the type) and then pass the file name to the constructor of the generated type to load it. For example, if we had files data/sample_1.rdata to data/sample_10.rdata, we could read them as:

let means = 
  [ for i in 1 .. 10 ->
      let data = Sample(sprintf "data/sample_%d.rdata" i)
      data.volcanoMean.[0] ]

Saving from F#

If you perform data acquisition in F# and then want to pass the data to R, you can use the standard R functions for saving the *.rdata files. The easiest option is to call the R.assign function to define named values in the R environment and then use R.save to save the environment to a file:

// Calculate sum of square differences
let avg = sample.volcanoList |> Array.choose id |> Array.average
let sqrs = 
  sample.volcanoList 
  |> Array.map (fun v -> pown (v.Value - avg) 2)

// Save the squares to an RData file
R.assign("volcanoDiffs", sqrs)
R.save(list=[ "volcanoDiffs" ], file="volcano.rdata")

It is recommended to use the list parameter of the save function to specify the names of the symbols that should be saved, rather than saving all symbols. The R provider uses additional temporary symbols and so the saved file would otherwise contain unnecessary fileds.

Once you save the file using the above command, you can re-load it again using the RData type provider, such as: new RData<"C:/temp/volcano.rdata">().

namespace RProvider
type Sample = RData<...>
type RData
<summary>Typed representation of an .rdata file.</summary> <param name='FileName'>Location of an .rdata file from which to infer structure.</param> <param name='ResolutionFolder'>If using a relative path, the folder from which to resolve the relative path.</param>
val sample: RData<...>
property RData<...>.volcano: Option<float> array2d with get
property RData<...>.volcanoList: Option<float> array with get
property RData<...>.volcanoMean: Option<float> array with get
val means: Option<float> list
val i: int
val data: RData<...>
val sprintf: format: Printf.StringFormat<'T> -> 'T
val avg: float
module Array from Microsoft.FSharp.Collections
val choose: chooser: ('T -> 'U option) -> array: 'T array -> 'U array
val id: x: 'T -> 'T
val average: array: 'T array -> 'T (requires member (+) and member DivideByInt and member Zero)
val sqrs: float array
val map: mapping: ('T -> 'U) -> array: 'T array -> 'U array
val v: Option<float>
val pown: x: 'T -> n: int -> 'T (requires member One and member ( * ) and member (/))
property Option.Value: float with get
type R = static member ``!`` : ?paramArray: obj array -> RExpr + 2 overloads static member ``!=`` : ?paramArray: obj array -> RExpr + 2 overloads static member ``!_hexmode`` : ?a: obj -> RExpr + 2 overloads static member ``!_octmode`` : ?a: obj -> RExpr + 2 overloads static member ``$`` : ?paramArray: obj array -> RExpr + 2 overloads static member ``$<-`` : ?paramArray: obj array -> RExpr + 2 overloads static member ``$<-_POSIXlt`` : ?x: obj * ?name: obj * ?value: obj -> RExpr + 2 overloads static member ``$<-_data_frame`` : ?x: obj * ?name: obj * ?value: obj -> RExpr + 2 overloads static member ``$_DLLInfo`` : ?x: obj * ?name: obj -> RExpr + 2 overloads static member ``$_package__version`` : ?x: obj * ?name: obj -> RExpr + 2 overloads ...
Base R functions.
R.assign(paramsByName: List<string * obj>) : Abstractions.RExpr
Assign a Value to a Name
R.assign(paramsByName: System.Collections.Generic.IDictionary<string,obj>) : Abstractions.RExpr
Assign a Value to a Name
R.assign(?x: obj, ?value: obj, ?pos: obj, ?envir: obj, ?inherits: obj, ?immediate: obj) : Abstractions.RExpr
Assign a Value to a Name
R.save(paramsByName: List<string * obj>) : Abstractions.RExpr
Save R Objects
R.save(paramsByName: System.Collections.Generic.IDictionary<string,obj>) : Abstractions.RExpr
Save R Objects
R.save(?list: obj, ?file: obj, ?ascii: obj, ?version: obj, ?envir: obj, ?compress: obj, ?compression__level: obj, ?eval_promises: obj, ?precheck: obj, ?paramArray: obj array) : Abstractions.RExpr
Save R Objects
type 'T list = List<'T>

Type something to start searching.