Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
* 1.0.12 - Use correct folders in NuGet package
* 1.0.13 - Fix the Print extension method
* 1.0.14 - Fix the module clash error in FsLab (#46). Fix assembly resolution (#117). Update NuGET and automatically update FAKE(#116).
* 1.0.15 - Fix bad upload to NuGet.org
* 1.0.15 - Fix bad upload to NuGet.org
* 1.0.16-rc1 - RemoteSession for Remote R session interaction
1 change: 1 addition & 0 deletions RProvider.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{33FD
docs\content\plugins.md = docs\content\plugins.md
docs\content\reading-rdata.fsx = docs\content\reading-rdata.fsx
docs\content\Statistics-QuickStart.fsx = docs\content\Statistics-QuickStart.fsx
docs\content\tutorial-RemoteR.fsx = docs\content\tutorial-RemoteR.fsx
docs\content\tutorial.fsx = docs\content\tutorial.fsx
docs\content\whatwhy.md = docs\content\whatwhy.md
EndProjectSection
Expand Down
178 changes: 178 additions & 0 deletions docs/content/tutorial-RemoteR.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
(*** hide ***)
// Include the right directories so that the documentation tool tips work
#nowarn "211" // Ignore warning that a search path does not exist on #I
#I "../../packages/FSharp.Data.1.1.10/lib/net40/"
#I "../../bin/"

(**
# RemoteR Provider Tutorial

## Introduction

Interaction with R sessions running in a separate process, or even a separate machine
on the network, can be performed using the RProvider.RemoteSession class. The
RemoteSession class can copy variables to the remote R session, as well as from the
remote R session, and invoke arbitrary R code in the remote session.

This can be very useful if you want to switch between working in Fsi and working in
RGui or RStudio, or if you want to offload some processing in R to another machine.

To make the experience of using RemoteSession instances more like using the R type
provided by RProvider the RemoteR type provider extends the RemoteSession class
with methods and properties corresponding to functions and properties defined by R packages
for the local R installation. R packages referenced by the module "open" syntax will have
their functions and properties automatically added to the RemoteSession class, just as they
are for the R type. It's important to remember that type discovery is performed using the
local R installation; if you are connected to an R session running on a separate machine
it may not have the package you reference installed and attempting to call a function in
the remote R session provided by a package that isn't installed on that machine will result
in a runtime error.

## Pre-requisites

The RemoteSession class requires the svSocket package in R. This package must be installed
in R for the local R installation. If you connect to a remote machine, the R installation
for that remote machine must also have the svSocket package installed.

As of 8/1/2014 a patched version of the compiler is required for the type provider that adds
the extension methods to RemoteSession. The source for this version and can be downloaded from
of the compiler can be downloaded and built from here:
[Extension Methods fork on CodePlex](https://visualfsharp.codeplex.com/SourceControl/network/forks/dsyme/cleanup/contribution/6853)

## Tutorial Setup

These instructions assume that you are running RGui or RStudio on the same machine as you
are running Fsi.

* Start RGui or RStudio
* Make sure that svSocket package is installed
* In R, `require(svSocket)`
* In R, `startSocketServer()`

## Referencing the provider

In order to use the RemoteR provider, you need to reference the `RDotNet.dll` library
(which is a .NET connector for R) and the `RProvider.dll` itself. For this tutorial,
we use `open` to reference a number of packages including `stats`, `tseries` and `zoo`:
*)
#I "../packages/RProvider.1.0.3/lib"
#r "RDotNet.dll"
#r "RDotNet.FSharp.dll"
#r "RDotNet.NativeLibrary.dll"
#r "RProvider.dll"
#r "RProvider.Runtime.dll"

open RDotNet
open RProvider
(**
These statements import the R packages named. Discovery of the packages is performed
using the local R installation.
*)
open RProvider.``base``
open RProvider.stats
open RProvider.tseries
open RProvider.graphics

open System
open System.Net

/// Constructs a new RemoteSession. If you've compiled with an F# compiler that supports
/// generating extension methods the RR instance will provide the same functions as the
/// R type would given the R package imports above: base, stats, tseries, and graphics
let RR = new RemoteSession()

(**
If either of the namespaces above are unrecognized, you need to install the package in R
using `install.packages("stats")`.

## Obtaining data

In this tutorial, we use [F# Data](http://fsharp.github.io/FSharp.Data/) to access stock
prices from the Yahoo Finance portal. For more information, see the documentation for the
[CSV type provider](http://fsharp.github.io/FSharp.Data/library/CsvProvider.html).

The following snippet uses the CSV type provider to generate a type `Stocks` that can be
used for parsing CSV data from Yahoo. Then it defines a function `getStockPrices` that returns
array with prices for the specified stock and a specified number of days:
*)
#r "FSharp.Data.dll"
open FSharp.Data

type Stocks = CsvProvider<"http://ichart.finance.yahoo.com/table.csv?s=SPX">

/// Returns prices of a given stock for a specified number
/// of days (starting from the most recent)
let getStockPrices stock count =
let url = "http://ichart.finance.yahoo.com/table.csv?s="
[| for r in Stocks.Load(url + stock).Take(count).Data -> float r.Open |]
|> Array.rev

/// Get opening prices for MSFT for the last 255 days
let msftOpens = getStockPrices "MSFT" 255

(**
## Calling R functions

Now, we're ready to call R functions using the type provider. The following snippet takes
`msftOpens`, calculates logarithm of the values using `R.log` and then calculates the
differences of the resulting vector using `R.diff`:
*)

/// Retrieve stock price time series and compute returns
let msft = msftOpens |> RR.log |> RR.diff
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this example work if you don't have the compiler patched to support provided extension methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the example will not work if you don't have the patched compiler or a later compiler version that includes the extension method support.


/// Alternately, the RemoteR class is provided which is equivalent to the R class; however,
/// it takes a RemoteSession instance as the first argument and uses that RemoteSession instance
/// to make all R calls.
///let msft = msftOpens |> (fun a -> RemoteR.log(RR, a) |> (fun a -> RemoteR.diff(RR, a)

(**
If you want to see the resulting values, you can call `msft.AsVector()` in F# Interactive.
Next, we use the `acf` function to display the atuo-correlation and call `adf_test` to
see if the `msft` returns are stationary/non-unit root:
*)

let a = RR.acf(msft)
let adf = RR.adf_test(msft)

(**
After running the first snippet, a window similar to the following should appear (note that
it might not appear as a top-most window).

<div style="text-align:center">
<img src="images/acf.png" />
</div>

Finally, we can obtain data for multiple different indicators and use the `R.pairs` function
to produce a matrix of scatter plots:
*)

// Build a list of tickers and get diff of logs of prices for each one
let tickers =
[ "MSFT"; "AAPL"; "X"; "VXX"; "SPX"; "GLD" ]
let data =
[ for t in tickers ->
printfn "got one!"
t, getStockPrices t 255 |> RR.log |> RR.diff ]

// Create an R data frame with the data and call 'R.pairs'
let df = RR.data_frame(namedParams data)
RR.pairs(df)

// Create variable in the remote session and copy a value to it
RR?foo <- "foo"
// Retrieve a remote variable as a RemoteSymbolicExpression
let remoteFoo = RR?foo
// Get a local SymbolicExpression for the remote one
let localFoo = remoteFoo.ToLocalValue()
// Get an unnamed RemoteSymbolicExpression for a value
(32).ToRemoteValue(RR)

(**
As a result, you should see a window showing results similar to these:

<div style="text-align:center">
<img src="images/pairs.png" />
</div>

*)
6 changes: 3 additions & 3 deletions src/Common/AssemblyInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ open System.Reflection
[<assembly: AssemblyCompanyAttribute("BlueMountain Capital")>]
[<assembly: AssemblyProductAttribute("RProvider")>]
[<assembly: AssemblyDescriptionAttribute("An F# Type Provider providing strongly typed access to the R statistical package.")>]
[<assembly: AssemblyVersionAttribute("1.0.15")>]
[<assembly: AssemblyFileVersionAttribute("1.0.15")>]
[<assembly: AssemblyVersionAttribute("1.0.16")>]
[<assembly: AssemblyFileVersionAttribute("1.0.16")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] Version = "1.0.15"
let [<Literal>] Version = "1.0.16"
16 changes: 16 additions & 0 deletions src/Common/ProvidedTypes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ open System.Reflection
open System.Reflection.Emit
open System.Linq.Expressions
open System.Collections.Generic
open System.Runtime.CompilerServices
open Microsoft.FSharp.Core.CompilerServices

type E = Quotations.Expr
Expand Down Expand Up @@ -77,6 +78,16 @@ module internal Misc =
type CustomAttributeData = Microsoft.FSharp.Core.CompilerServices.IProvidedCustomAttributeData
#endif

let mkExtensionAttributeData() =
#if FX_NO_CUSTOMATTRIBUTEDATA
{ new IProvidedCustomAttributeData with
#else
{ new CustomAttributeData() with
#endif
member __.Constructor = typeof<ExtensionAttribute>.GetConstructors().[0]
member __.ConstructorArguments = upcast [| |]
member __.NamedArguments = upcast [||] }

let mkEditorHideMethodsCustomAttributeData() =
#if FX_NO_CUSTOMATTRIBUTEDATA
{ new IProvidedCustomAttributeData with
Expand Down Expand Up @@ -132,6 +143,7 @@ module internal Misc =
let mutable xmlDocDelayed = None
let mutable xmlDocAlwaysRecomputed = None
let mutable hasParamArray = false
let mutable extensionAttribute = false

// XML doc text that we only compute once, if any. This must _not_ be forced until the ConstructorArguments
// property of the custom attribute is foced.
Expand All @@ -155,9 +167,11 @@ module internal Misc =
member __.AddXmlDocDelayed(xmlDoc : unit -> string) = xmlDocDelayed <- Some xmlDoc
member this.AddXmlDoc(text:string) = this.AddXmlDocDelayed (fun () -> text)
member __.HideObjectMethods with set v = hideObjectMethods <- v
member __.ExtensionAttribute with set v = extensionAttribute <- v
member __.AddCustomAttribute(attribute) = customAttributes.Add(attribute)
member __.GetCustomAttributesData() =
[| yield! customAttributesOnce.Force()
if extensionAttribute then yield mkExtensionAttributeData()
match xmlDocAlwaysRecomputed with None -> () | Some f -> customAttributes.Add(mkXmlDocCustomAttributeData (f())) |]
:> IList<_>

Expand Down Expand Up @@ -582,6 +596,7 @@ type ProvidedMethod(methodName: string, parameters: ProvidedParameter list, retu
member this.AddXmlDoc xmlDoc = customAttributesImpl.AddXmlDoc xmlDoc
member this.AddObsoleteAttribute (msg,?isError) = customAttributesImpl.AddObsolete (msg,defaultArg isError false)
member this.AddDefinitionLocation(line,column,filePath) = customAttributesImpl.AddDefinitionLocation(line, column, filePath)
member this.ExtensionAttribute with set v = customAttributesImpl.ExtensionAttribute <- v
member __.AddCustomAttribute(attribute) = customAttributesImpl.AddCustomAttribute(attribute)
member __.GetCustomAttributesDataImpl() = customAttributesImpl.GetCustomAttributesData()
#if FX_NO_CUSTOMATTRIBUTEDATA
Expand Down Expand Up @@ -1239,6 +1254,7 @@ type ProvidedTypeDefinition(container:TypeContainer,className : string, baseType
member this.AddObsoleteAttribute (msg,?isError) = customAttributesImpl.AddObsolete (msg,defaultArg isError false)
member this.AddDefinitionLocation(line,column,filePath) = customAttributesImpl.AddDefinitionLocation(line, column, filePath)
member this.HideObjectMethods with set v = customAttributesImpl.HideObjectMethods <- v
member this.ExtensionAttribute with set v = customAttributesImpl.ExtensionAttribute <- v
member __.GetCustomAttributesDataImpl() = customAttributesImpl.GetCustomAttributesData()
member this.AddCustomAttribute attribute = customAttributesImpl.AddCustomAttribute attribute
#if FX_NO_CUSTOMATTRIBUTEDATA
Expand Down
4 changes: 4 additions & 0 deletions src/Common/ProvidedTypes.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ type ProvidedMethod =
/// Add a custom attribute to the provided method definition.
member AddCustomAttribute : CustomAttributeData -> unit

member ExtensionAttribute : bool with set


/// Represents an erased provided property.
Expand Down Expand Up @@ -356,6 +357,8 @@ type ProvidedTypeDefinition =
/// Suppress System.Object entries in intellisense menus in instances of this provided type
member HideObjectMethods : bool with set

member ExtensionAttribute : bool with set

/// Get or set a flag indicating if the ProvidedTypeDefinition is erased
member IsErased : bool with get,set

Expand All @@ -382,6 +385,7 @@ type ProvidedAssembly =
new : assemblyFileName:string -> ProvidedAssembly
/// <summary>
/// Emit the given provided type definitions as part of the assembly

/// and adjust the 'Assembly' property of all provided type definitions to return that
/// assembly.
///
Expand Down
Loading