Setting-up Your Project

  1. Add reference of RDotNet and RDotNet.NativeLibrary to your project. Note that RDotNet.NativeLibrary is OS dependent. For Windows, get it from RDotNet.Windows.zip or NuGet gallery; for Mac OS and Linux, choose RDotNet.Unix.zip.
  2. Now, just write out your codes in your language.

Getting started

There is a page gathering Software Prerequisites for the supported platforms.

An "Hello World" sample follows. Before creating and initializing the R engine you must set the search path of R.dll on Windows, libR.dylib on Mac OS X, or libR.so on Linux (hereafter referred to as 'R library'). As this is a stumbling block for many users, work is underway to simplify and automate this step and its platform specifics. As you read these lines you may still need to set things up manually, as illustrated in the sample code. This document will be updated with the next release.

static void Main(string[] args)
{
    //REngine.SetEnvironmentVariables(); // Currently under development - coming soon
    SetupPath(); // current process, soon to be deprecated
    using (REngine engine = REngine.CreateInstance("RDotNet"))
    {
        engine.Initialize(); // required since v1.5
        CharacterVector charVec = engine.CreateCharacterVector(new[] { "Hello, R world!, .NET speaking" });
        engine.SetSymbol("greetings", charVec);
        engine.Evaluate("str(greetings)"); // print out in the console
        string[] a = engine.Evaluate("'Hi there .NET, from the R engine'").AsCharacter().ToArray();
        Console.WriteLine("R answered: '{0}'", a[0]);
        Console.WriteLine("Press any key to exit the program");
        Console.ReadKey();
    }
}

public static void SetupPath()
{
   var oldPath = System.Environment.GetEnvironmentVariable("PATH");
   var rPath = System.Environment.Is64BitProcess ? @"C:\Program Files\R\R-3.0.2\bin\x64" : @"C:\Program Files\R\R-3.0.2\bin\i386";
   // Mac OS X
   //var rPath = "/Library/Frameworks/R.framework/Libraries";
   // Linux (in case of libR.so exists in the directory)
   //var rPath = "/usr/lib";
   if (Directory.Exists(rPath) == false)
      throw new DirectoryNotFoundException(string.Format("Could not found the specified path to the directory containing R.dll: {0}", rPath));
   var newPath = string.Format("{0}{1}{2}", rPath, System.IO.Path.PathSeparator, oldPath);
   System.Environment.SetEnvironmentVariable("PATH", newPath);
   // NOTE: you may need to set up R_HOME manually also on some machines
   string rHome = "";
   var platform = Environment.OSVersion.Platform;
   switch (platform)
   {
      case PlatformID.Win32NT:
         break; // R on Windows seems to have a way to deduce its R_HOME if its R.dll is in the PATH
      case PlatformID.MacOSX:
         rHome = "/Library/Frameworks/R.framework/Resources";
         break;
      case PlatformID.Unix:
         rHome = "/usr/lib/R";
         break;
      default:
         throw new NotSupportedException(platform.ToString());
   }
   if (!string.IsNullOrEmpty(rHome))
      Environment.SetEnvironmentVariable("R_HOME", rHome);
}

For your information, on Windows, you may find the directory via Windows registry.

Data Types

All expressions in R are represented as SymbolicExpression objects in R.NET. For data access, the following special classes are defined. Note that there is no direct equivalent in .NET for 'NA' values in R. Special values are used for some types but pay attention to the behaviour, so as not to risk incorrect calculations.

Table. Classes in R.NET bridges between R and .NET Framework.
R R.NET .NET Framework Note
character vector RDotNet.CharacterVector System.String[]
integer vector RDotNet.IntegerVector System.Int32[] The minimum value in R is -2^31+1 while that of .NET Framework is -2^31. Missing values are int.MinValue
real vector RDotNet.NumericVector System.Double[] Missing values are represented as double.NaN
complex vector RDotNet.ComplexVector System.Numerics.Complex[] System.Numerics assembly is required for .NET Framework 4.
raw vector RDotNet.RawVector System.Byte[]
logical vector RDotNet.LogicalVector System.Boolean[]
character matrix RDotNet.CharacterMatrix System.String[, ]
integer matrix RDotNet.IntegerMatrix System.Int32[, ] The minimum value in R is -2^31+1 while that of .NET Framework is -2^31.
real matrix RDotNet.NumericMatrix System.Double[, ]
complex matrix RDotNet.ComplexMatrix System.Numerics.Complex[, ] Reference to System.Numerics assembly is required.
raw matrix RDotNet.RawMatrix System.Byte[, ]
logical matrix RDotNet.LogicalMatrix System.Boolean[, ]
list RDotNet.GenericVector From version 1.1.
data frame RDotNet.GenericVector From version 1.1. RDotNet.DataFrame class is also available (below).
data frame RDotNet.DataFrame From version 1.3. And from version 1.5.3, DataFrameRowAttribute and DataFrameColumnAttribute are available for data mapping.
function RDotNet.Function From version 1.4. Including closure, built-in function, and special function.
factor RDotNet.Factor System.Int32[] From version 1.5.2.
S4 RDotNet.S4Object Not Available Yet. See S4 branch in the source control.

Sample code

The following sample code illustrate the most use capabilities. It is extracted from the sample code 2 at https://github.com/jmp75/rdotnet-onboarding, as of 2013-10-20

You usually interact with the REngine object with the methods Evaluate, GetSymbol, and SetSymbol. To create R vector and matrices, the REngine object has methods such as CreateNumericVector, CreateCharacterMatrix, etc. Finally, you can invoke R functions in a variety of ways, using Evaluate, and also more directly.

Let's re-emphasize the need to set up the environment variables, creating the engine, and also initializing it.
SetupHelper.SetupPath(); // See earlier sample code
REngine engine = REngine.CreateInstance("RDotNet")
engine.Initialize();

Basic operations with numeric vectors
var e = engine.Evaluate("x <- 3");
// You can now access x defined in the R environment
NumericVector x = engine.GetSymbol("x").AsNumeric();
engine.Evaluate("y <- 1:10");
NumericVector y = engine.GetSymbol("y").AsNumeric();

While you may evaluate function calls by generating a string and call the Evaluate method, this is quickly unwieldy for cases where you pass large amounts of data. The following demonstrates how you may call a functions, a bit like how you would invoke a function reflectively in .NET. Note that the sample is designed for R.NET 1.5.5; there are syntactic improvements under development that should be available in the next release.
// Invoking functions
// invoking expand.grid directly would not work as of R.NET 1.5.5, 
// because it has a '...' pairlist argument. We need a wrapper function.
var expandGrid = engine.Evaluate("function(x, y) { expand.grid(x=x, y=y) }").AsFunction();
var v1 = engine.CreateIntegerVector(new[] { 1, 2, 3 });
var v2 = engine.CreateCharacterVector(new[] { "a", "b", "c" });
var df = expandGrid.Invoke(new SymbolicExpression[] { v1, v2 }).AsDataFrame();

Continuing with the results of our use of expand.grid, the following code illustrate that while R.NET tries to emulate the coercion behavior of R, you should be circumspect with it, notably when you know you have factors as columns in your data frame.
engine.SetSymbol("cases", df);
// Not correct: the 'y' column is a "factor". This returns "1", "1", "1", "2", "2", etc. 
var letterCases = engine.Evaluate("cases[,'y']").AsCharacter().ToArray();
// This returns something more intuitive for C#  Returns 'a','a','a','b','b','b','c' etc.
letterCases = engine.Evaluate("as.character(cases[,'y'])").AsCharacter().ToArray();
// In the manner of R.NET, try
letterCases = engine.Evaluate("cases[,'y']").AsFactor().GetFactors();

To reuse whole scripts, the simplest method is to use the 'source' function in R
engine.Evaluate("source('c:/src/path/to/myscript.r')");

Last edited Oct 20, 2013 at 5:28 PM by kos59125, version 19

Comments

touchbutton Oct 12, 2013 at 2:58 PM 
I tested R.NET 1.5.5 with R 3.0.1 x64 on Windows 7, it worked perfectly well. I tested round-tripping dotnet IEnumerable<int>, <string>, <DateTime>, <decimal>, <double> with REngine numeric, POSIXCT, character vectors, creating data.frame in REngine by combinding vectors. It worked efficiently and I managed to create vectors and data.frame with length larger than 2 million quite quickly. I conclude that the library is very stable now. Thanks.

billybond Sep 24, 2013 at 12:18 PM 
@jdizzy
what a load of utter nonsense. I have had great success with this project and find it far more useable that the COM method. You clearly need to get laid or have a drink or something.

BenWiseman Sep 20, 2013 at 12:13 PM 
This set up guide doesn't work - even if you copy it directly :/

touchbutton Aug 16, 2013 at 4:16 AM 
It's great to see new updates. This can potentially be an extremely useful library. Keep up with the work!

viprenkun Aug 13, 2013 at 9:18 AM 
The new version here perfectly works with R 3.0.1 as I tested.

kulong995 Aug 10, 2013 at 9:28 AM 
thanks for new version.!!

Gravitas Jan 5, 2013 at 8:48 PM 
Has anyone managed to get it to work with VS2012?

jdizzy769 Aug 16, 2012 at 7:06 PM 
I return hat in hand after my previous post. Although I have not yet tested the memory management and garbage collection, which was the cause of all of my frustrations last time, v1.5 is just plain AWESOME. I am giving it another shot. Thank you for the update!!

QuidProMS Mar 26, 2012 at 11:36 PM 
In response to jdizzy769, i think you have it wrong. This is a great adult project, albeit a work in progress with some bugs, that works quite well if the programmer avoids the buggy usages, which seem to involve multiple R instances (see below). The whole idea of anything dotnet is NOT to have to *explicitly* use COM, e.g. R(D)-COM. R.Net seems to manage the memory involved across the managed/unmanaged boundaries quite well, but more testing on my part will tell, and that makes programming a lot easier. There are some features I of R.Net I don't understand, but the idea of getting delegate for an R.dll internal, var cos = engine.GetSymbol("cos").AsFunction(), is very powerful.
In response to those who want multiple instances of the REngine, just glancing at the code, it seems like the problem is that the R.dll is LoadLibrary() loaded multiple times into the same process and upon calling yet again "setup_Rmainloop()", the application is locked. I don't know much about the R.dll code, but I can guess it is not threadsafe, and it is an error to call "setup_Rmainloop()" again before exiting the main loop. So the multiple instance issue seems to demand either one process per instance, with the concomitant added complexity to R.Net, or a revision of the R.dll to support multiple instances.

aoldevstat Oct 20, 2011 at 2:14 AM 
I've quickly become a fan of the R.NET and started using it in real solutions even though it took some additional work. This is pretty fast, much more faster than RDCOM library. Please don't leave it and develop it better and better. I agree that error handling still needs work. Why not through the ParseException? Using ICharacterDevice to do that isn't the best way in my opinion...

FrankyHollywood Oct 13, 2011 at 3:37 PM 
The idea is very good, however it keeps crashing constantly. Once a crash has occured I have to restart VS2010 again before I can send any command to the engine before it responds without error (probably the com component which need te reset or something). I'm trying to build a webservice, which needs multiple R sessions. Maybe a winforms app with just 1 engine instance works better.

conclusion, nice project, pretty simple and straightforward interface, but session management and engine response/error handling need some work. That would make it production worthy :)

tylt Aug 19, 2011 at 8:03 AM 
I think it's a worthwhile project, and I'm glad it exists. Definitely needs a lot more work, but I think it'll evolve to be more useful. COM is reliable, but the performance is pretty poor.

jdizzy769 Aug 18, 2011 at 3:31 PM 
VERY buggy and terribly supported. This is merely a hobby project, and a juvenile one at that. Use R(D)-COM if you need it to actually work.