New developments in RLink

Leonid Shifrin, Wolfram

(RowDefault[, (Spacer1), , (Spacer1), ])  #WolframTechConf

ABSTRACT

We will describe recent developments and new features in RLink, for both the core language connectivity and the computing infrastructure. The list of new features includes R object references, WL side management of R packages,  and several other features.

Overview of RLink—Starting Examples

Starting Examples

Example: Computing word frequencies and positions

Needs["RLink`"]
InstallR[]

This is our text:

txt  = "the here means that the first item in this line of output is
item in this case our output consists of only one line and one
item so this is redundant but this notation helps to read
voluminous output that consists of many items spread over many
lines for example if there were two rows of output with six items
per row the second row would be labeled";

Execute R code and define a new user-defined R function. This finds positions of individual words and groups together positions of common words. The result (function in this case) is returned to Wolfram Language:

findWordsWL = REvaluate["findwords <- function(txt){
	txt <- scan(text = txt, what = \"character\")
	split(1:length(txt), txt)
}"]

We can now apply this function:

findWordsWL[txt]

Since we have defined the function in R workspace, we can use it directly:

RFunction["findwords"][txt]

You can also use ExternalEvaluate:

findwordsViaExternalEvaluateCodeCell <- function(txt){
	txt <- scan(text = txt, what = "character")
	split(1:length(txt), txt)
}

RFunction["findwordsViaExternalEvaluateCodeCell"][txt]

Example: Chi-square distribution: data sample, plot and histogram

Create a random sample:

data = REvaluate["{
require(stats)
set.seed(14)
rchisq(100, df = 4)
}"]

Plot in Wolfram Language:

Show[{Histogram[data], Plot[100 * PDF[ChiSquareDistribution[4], x], {x, 0, 20}]}]

RLink currently does not support R plotting out of the box (something that is likely to be improved in future versions). But we can rather easily add this support, at least for the set of use cases we are interested in here.

This defines an R function that takes two parameters: the file path where the plot is saved and another R function that will perform the main work regarding the plotting:

mathematicaRPlotWrapper = 
RFunction["function(filename, plotfun){
pdf(filename)
plotfun()
dev.off()
}"];

This function automates R plot import via a temporary file:

Clear[getRPlot];
getRPlot[plotFun_RFunction]:=
With[{tempfile = FileNameJoin[{$TemporaryDirectory,"temp.pdf"}]},
If[FileExistsQ[tempfile],Quiet@DeleteFile[tempfile]];
mathematicaRPlotWrapper[tempfile,plotFun];
If[!FileExistsQ[tempfile],Return[$Failed]];
First[Import[tempfile], $Failed]
];

We now need data back on the R side:

RSet["chisqdata", data]

Now we can plot this in R:

Show[#, ImageSize -> {600, 400}]&@getRPlot[RFunction["function(){
	require(stats)
	hist(chisqdata, freq = FALSE, ylim = c(0, 0.2))
	curve(dchisq(x, df = 4), col = 2, lty = 2, lwd = 2,add = TRUE)
	}"
]]

Example: Running SQLite queries via R

Here is a sample SQLite database of scaled model cars, which we would like to query:

db = FindFile["ExampleData/ecommerce-database.sqlite"]

Start by installing the RSQLite package:

14.1:

RInstallPackage["RSQLite"]

Pre-Version 14.1:

REvaluate["install.packages('RSQLite', repos='https://cloud.r-project.org')"]

Load the package:

REvaluate["library(RSQLite)"]

Construct an anonymous R function that takes the SQLite file path and the query, opens an SQLite connection, executes the query, closes the connection and returns the result:

rSQLExecute= RFunction["function(file, query){
	conn <- dbConnect(RSQLite::SQLite(), file)
	res <- dbGetQuery(conn, query)
	dbDisconnect(conn)
	res
}"]

Execute the query:

qres = rSQLExecute[
FindFile["ExampleData/ecommerce-database.sqlite"], 
"SELECT * FROM offices"
]

This will work in the future versions of RLink.

Dataset[qres]

Overview of RLink—R Session Lifecycle

R Session Lifecycle

Standard workflow

Load the package:

Needs["RLink`"]

Install auto-detected R runtime:

InstallR[]

Test that it works:

REvaluate["R.Version()"]
REvaluate["R.home()"]

Uninstall R runtime:

UninstallR[]

Use specific R installation

This will load a different R installation:

InstallR["RHomeLocation"-> "/Library/Frameworks/R.framework/Versions/4.2/Resources"]

Test

REvaluate["R.Version()"]
REvaluate["R.home()"]

Uninstall R/end RLink session:

UninstallR[]

The “JRINativeLibraryLocation” option

This option was most useful before RLink started to auto-configure R installations. It may still occasionally be useful:

InstallR[
"RHomeLocation"-> "/Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources",
"JRINativeLibraryLocation" -> "/Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/rJava/jri"
]

Test

REvaluate["R.home()"]

UninstallR[]

Use specific JDK/JVM

This may be useful in various circumstances (e.g. to change/add JVM arguments etc.):

InstallR["JRELocation" -> "/Library/Java/JavaVirtualMachines/openjdk-11.jdk/Contents/Home/bin/java"]

Test

REvaluate["R.home()"]

UninstallR[]

The default Java used by RLink lives in the Wolfram Language layout:

FileNames[
"java"| "java.exe",
FileNameJoin[{$InstallationDirectory, "SystemFiles", "Java", $SystemID, "bin"}]
]

Last successfully used R installation is cached

By default, RLink will use the last successfully used R installation:

InstallR[]
REvaluate["R.home()"]

UninstallR[]

To have a different R installation be cached, call InstallR with explicit value passed by "RHomeLocation".

InstallR["RHomeLocation"-> "/Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources"]

Test

REvaluate["R.home()"]

UninstallR[]

Errors

To do any computations with R, you need a running R session:

REvaluate["R.Version()"]

Trying to launch a different version of R will fail if there is an already running session:

InstallR["RHomeLocation"-> "/Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources"]
InstallR["RHomeLocation"-> "/Library/Frameworks/R.framework/Versions/4.2/Resources"]

UninstallR[]

Calling UninstallR[] will produce a warning if there isn’t a running R session but does not result in an error:

UninstallR[]

Notes

R installation needs to be specially configured the first time it is used with RLink

RLink tries to configure it automatically.

There is also a documented manual configuration procedure, which basically consists of installing the rJava R package.

More details on that further in the talk

RLink currently only supports a single running session, but this will soon change

Honorable mention—R data frames / Dataset

Status

Experimental

Not all forms of data frames / shapes of Dataset yet supported

To be included in 14.2

To be documented after 14.2

Example

InstallR[]

Dataset`$DatasetTargetRowCount = 10

REvaluate["mtcars"]//Shallow[#,4]&

REvaluate["mtcars"]//Dataset

mtcds = REvaluate["mtcars"]//Dataset

mtcds//Normal//Short[#, 5]&

RDataFrame @ mtcds

This second data set is bundled with R

mtcars =Dataset @ REvaluate["mtcars"]

This is how data frame looks in WL

RDataFrame @ mtcars

New feature—R Installation Discovery/Management

Status

Experimental

Exists in 14.1+

Likely to be documented in 14.3

Discovering R Installations

This attempts to find all R installations on your system:

RInstallationFind[]

You can limit the number of R installations to find:

RInstallationFind[1]

Your Installation Not Found? Register It

You can manually register your R installations:

RInstallationRegister["/Users/leo/Leo/Temp/R/4.4-x86_64/Resources"]

They can now be found:

RInstallationFind["IncludeCachedOnly" -> True]

Using RInstallationObject[] in InstallR

UninstallR[]
InstallR[First @ RInstallationFind["IncludeCachedOnly" -> True]]

REvaluate["R.home()"]

UninstallR[]

New feature—Installing R packages from within Wolfram Language

Status

Experimental

Exists in 14.1+

Documented in 14.1

Basic Workflow

Initialization

UninstallR[]
InstallR["RHomeLocation" -> "/Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources"]

REvaluate["R.home()"]

Installing

RInstallPackage["RSQLite"]

Uninstalling

RInstallPackage["RSQLite", "InstallationMethod" -> "Remove"]

Check:

REvaluate["installed.packages()[,1]"]

Installing for specific R version

UninstallR[]
RInstallPackage["RSQLite", "/Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources"]

More Advanced Topics

Reinstall

RInstallPackage["RSQLite", "Reinstall" -> True]

Errors

RInstallPackage["Nonexistent"]

The following attempt to install the rJava package may fail, since it may default to build from the C/Java sources and the build process requires extra steps for this particular package, which we have not completed:

RInstallPackage["rJava"]

Installing from downloaded source/binaries

Download a package from the repository:

binary = First @ URLDownload["https://cran.r-project.org/bin/macosx/big-sur-x86_64/contrib/4.4/RSQLite_2.3.7.tgz"]

Install from the downloaded binary:

RInstallPackage["RSQLite", "LocalSourcePath" -> binary, "Reinstall" -> True]

Returning to the rJava package: initial installation attempt failed. However, we can grab the binary distribution for this package and install it:

binary = First @ URLDownload["https://cran.r-project.org/bin/macosx/big-sur-x86_64/contrib/4.4/rJava_1.0-11.tgz"]

RInstallPackage["rJava", "LocalSourcePath" -> binary]

New feature—R object references

Status

Experimental

Exists in 14.1+

To be documented in 14.2

Motivation: non-importable R objects

Example: Linear Model

InstallR[]

lm=REvaluate["
{
	x <- 1:5
	y <- 3 * x + 2 
	linm <- lm(y~x)
}"];

lm//Short[#,15]&

REvaluate["linm$terms"]

Example: Image of Mount Rushmore

Export the image in PGM format:

mtrush  = Export[FileNameJoin[{$TemporaryDirectory, "mtrush.pgm"}], ]

Set the path to this file in R:

RSet["mtrushPath", mtrush]

Install the Pixmap package:

RInstallPackage["pixmap"]

Load the package:

library('pixmap')

Read the data:

mtrush1 <- read.pnm(mtrushPath)

The result is wrapped in RCode, since it is not directly importable. It actually is an S4 object:

typeof(mtrush1)

However, this doesn’t mean that it is useless:

Image[#,ColorSpace->"GrayScale"]&@REvaluate["mtrush1@grey"]

We can e.g. modify the image in R (example from Norman Matloff’s book):

{
mtrush2 <- mtrush1
mtrush2@grey[84:163, 135:177] <- 1
res <- mtrush2@grey
};

Image[#,ColorSpace->"GrayScale"]&@REvaluate["res"]

# adds random noise to img, at the range rows,cols of img; img and the
# return value are both objects of class pixmap; the parameter q
# controls the weight of the noise, with the result being 1-q times the
# original image plus q times the random noise
blurpart <- function(img,rows,cols,q) {
	lrows <- length(rows)
	lcols <- length(cols)
	newimg <- img
	randomnoise <- matrix(nrow=lrows, ncol=lcols,runif(lrows*lcols))
	newimg@grey[rows,cols] <- (1-q) * img@grey[rows,cols] + q * randomnoise
	return(newimg)
};

mtrush3 <- blurpart(mtrush1,84:163,135:177,0.65);

Image[#,ColorSpace->"GrayScale"]&@REvaluate["mtrush3@grey"]

Notes

History

Long-planned feature of RLink

Could live for some time without them, because:

Most R objects are immutable, just like in Wolfram Language.

R does not expose “pointers” or comparison by memory location.

Why we need them

Avoid unnecessary data transfers

Import arbitrary R objects in a bidirectional manner, including those not normally supported/importable by RLink

Support for new ExternalObject[] in ExternalEvaluate

What currently works

Getting into Wolfram Language and sending back to R arbitrary R objects (including primitive types)

Using references in RFunction[] and RSet[]

What doesn’t work yet

Make sure ALL objects are correctly imported, even in expression mode, by replacing non-importable portions with references

Automatic reference management/GC

Multi-session support (needs to be added to RLink)

Initialization

UninstallR[]
InstallR["RHomeLocation" -> "/Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources"]

REvaluate["R.home()"]

Core Usage

Creating/returning references

Two basic ways to get an R reference into Wolfram Language:

ref = RReturnAsObjectReference @ REvaluate["R.Version()"]
ref2 = REvaluate["R.Version()", "ReturnType" ->  "Reference"]

REvaluate “dereferences” the reference by default:

REvaluate[ref]

Any R object can be returned as a reference, including primitive types:

ref4 = REvaluate["1", "ReturnType" -> "Reference"]

It is possible to return a function call’s result as a reference:

ref6 = RFunction["sum"][1,2,3, "ReturnType" -> "Reference"]
ref7 = RReturnAsObjectReference @ RFunction["sum"][1,2,3]

Dereferencing

Dereference with "ReturnType" -> "Expression" (this is the default on references):

REvaluate[ref, "ReturnType" -> "Expression"]
REvaluate[ref]

REvaluate[ref6, "ReturnType" -> "Expression"]
REvaluate[ref7, "ReturnType" -> "Expression"]

Use in RFunction and RSet

One can pass references to R functions as arguments:

RFunction["sum"][1,2,3]
RFunction["sum"][ref4,2,3]

One can also assign them to R variables:

RSet["myRVar", ref4]
REvaluate["myRVar"]

Return functions as references

One can return R functions as references:

sumref = REvaluate["sum", "ReturnType" -> "Reference"]

Pass them as arguments to other functions:

RFunction["apply"][{{1,2},{3, 4}, {5, 6}}, 1, sumref]

Or assign them to R variables and then use in RFunction:

RSet["mysum", sumref];
RFunction["mysum"][1,2,3]

Resource management

One has to manually delete no-longer-used references for now, but we plan to have an automatic resource management soon:

RObjectReferenceQ @ ref7
RObjectReferenceDelete @ ref7
RObjectReferenceQ @ ref7

Invalid references

Calling RLink functions on invalid references produces an error:

REvaluate[ref7]
RFunction["sum"][1,2, ref7]
RSet["var", ref7]

Larger example: iGraph

Install the igraph package:

RInstallPackage["igraph"]

(* REvaluate["install.packages('igraph', repos='http://cran.us.r-project.org', dependencies=TRUE)"] *)

Load the package:

REvaluate["library(igraph)"]

Define some functions:

fullgraph=RFunction["graph.full"]
diameter=RFunction["diameter"]

Certain objects (including igraph’s graph) cannot really be imported into RLink, although it may appear that they can:

g=fullgraph[5]

Trying to use them directly results in an error:

diameter[g]

We can, however, return a graph as a reference:

g1=fullgraph[5, "ReturnType" -> "Reference"]

It works now:

diameter @ g1

Larger example: SQLite Revisited

Preparation

Make sure the package has been installed:

RInstallPackage["RSQLite"]

This is our database file:

file =  FindFile["ExampleData/ecommerce-database.sqlite"]

Loading the RSQLite package:

REvaluate["library(RSQLite)"]

What we did before

This is what we’ve done before (as a reminder):

rSQLExecute= RFunction["function(file, query){
	conn <- dbConnect(RSQLite::SQLite(), file)
	res <- dbGetQuery(conn, query)
	dbDisconnect(conn)
	res
}"]

Execute the query:

qres = rSQLExecute[
FindFile["ExampleData/ecommerce-database.sqlite"], 
"SELECT * FROM offices"
]

Dataset @ qres

Better method using R references

The problem with the previous approach is that we need to open and close a connection for every query. We can do better.

These functions will create a connection/disconnect:

connect = RFunction["function(file){dbConnect(RSQLite::SQLite(), file)}"]
disconnect = RFunction["dbDisconnect"]

Create a connection (returning as a reference is crucial here):

conn = connect[file,"ReturnType" -> "Reference"]

Running some queries:

RFunction["dbGetQuery"][conn, "SELECT officeCode FROM offices"]//Dataset

This counts the number of employees working for each office:

RFunction["dbGetQuery"][
conn, 
"SELECT officeCode, COUNT(*) as employeeCount 
   FROM employees
   GROUP BY officeCode"
]//Dataset

Once we are done, close the connection and delete the reference:

disconnect @ conn
RObjectReferenceDelete @ conn

Making things automatic/robust with Enclose, Confirm and WithCleanup:

Enclose @ WithCleanup[
file = Confirm @ FindFile["ExampleData/ecommerce-database.sqlite"];
ConfirmMatch[REvaluate["library(RSQLite)"], {"RSQLite",__}];
conn = Confirm @ connect[file,"ReturnType" -> "Reference"]
,
RFunction["dbGetQuery"][conn, "SELECT officeCode FROM offices"]
,
ConfirmAssert @ First[disconnect[conn], False];
Confirm @ RObjectReferenceDelete @ conn
]

Dataset @ %

New feature—R virtual environments

Status

Highly experimental

Not yet fully integrated with RLink and ExternalEvaluate

Will likely be included in 14.2

To be documented in the future (after 14.2)

Creation

Create a virtual environment

venv = RVirtualEnvironmentCreate["renv432"]

Check that it exists

RVirtualEnvironmentExistsQ["renv432"]

Stop the currently running R session, if any:

UninstallR[]

Start new R session with the new environment

InstallR @ venv

REvaluate["R.home()"]

?RLink`Environments`*

Initialization for examples

Dataset`$DatasetTargetRowCount = 10

schema=RelationalDatabase[FindFile["ExampleData/ecommerce-database.sqlite"]]
es = EntityStore[schema]
EntityRegister[es]

ClearAll[keyWrap];
keyWrap[key_Integer]:=key;
keyWrap[key_]:=Key[key];

ClearAll[pushUp]
(*Operator form*)
pushUp[key_String]:=pushUp[{key}];
pushUp[{keyPath__}]:=With[{keys=Sequence@@Map[keyWrap,{keyPath}]},GroupBy[Function[Part[#,keys]] -> KeyDrop[{keys}]]];
(*Actual form*)
pushUp[assocs:{__Association},keys__]:=pushUp[keys][assocs];

Example: using R - based statistical summaries for data, descriptive statistics

Data

This data comes from our sample SQLite database of scaled car models

nameProp = "productName";
properties = {"quantityInStock", "buyPrice", "MSRP"}
(carsInfo = EntityValue["products", {nameProp, Sequence @@ properties}])//Short

carsDS = EntityValue[
"products",
 {nameProp, Sequence @@ properties}, 
"Dataset"][pushUp["productName"] @* Values][All, First]

This data comes from R built-in cars characteristics data set

REvaluate["mtcars"]

This second data set is bundled with R

mtcars =Dataset @ REvaluate["mtcars"]

This is how data frame looks in WL

RDataFrame @ mtcars

pastecs library, function stat.desc()

RVirtualEnvironmentEnsurePackages["renv432", "pastecs"]

ClearAll[RPastecsDescribe]
RPastecsDescribe[ds_Dataset] :=   With[{df = RDataFrame @ ds}, 
Condition[
RWithPackages["pastecs"][
REvaluate["library(pastecs)"];
Dataset @ RFunction["stat.desc"][df]
]
,
RDataFrameQ[df]
]
]

RPastecsDescribe @ mtcars

RPastecsDescribe @ carsDS

library psych, function describe()

RVirtualEnvironmentEnsurePackages["renv432", "psych"]

ClearAll[RPsychDescribe]
RPsychDescribe[ds_Dataset] := With[{df = RDataFrame @ ds}, 
Condition[
 RWithPackages["psych"][
REvaluate["library(psych)"];
Dataset @ RFunction["describe"][df]
]
,
RDataFrameQ[df]
]
]

RPsychDescribe[mtcars]//AbsoluteTiming

Example: correlations / covariance tables

Notes

This example uses standard R, no external libraries

Helper functions

ClearAll[rTableToDataset]
rTableToDataset[expr_] := ReplaceAll[
	expr,
	RObject[data_, RAttributes[___, "dimnames" :> dnames_, ___]] :>
		Dataset @ Transpose @ AssociationThread[
			Last @ dnames,
			Map[Association]@Transpose @ Map[Thread]@Thread[First @ dnames -> data]
		]
]

ClearAll[datasetToRTable]
datasetToRTable[ds_Dataset] := 
With[{data = Normal @ ds},
	With[{
		keys = DeleteDuplicates @ Flatten @ Map[Keys] @ Values[data], 
		rowNames = Keys @ data
	},
	RObject[
		Lookup[Values @ data,keys],
		RAttributes["dimnames":> {rowNames, keys}]
	]
] /; MatchQ[data,<|(_String-><|(_String->_)...|>)...|>]]

Data

This is some data gathered about various states in the US, which comes bundled with R

states77 = rTableToDataset @ REvaluate["state.x77"]

Examples

This generates covariance or correlation function, with a specific method used

ClearAll[createCorrFunction]
Options[createCorrFunction] = {
"Method" -> "pearson" (* Methods: "pearson", "spearman", "kendall" *)
}
createCorrFunction[fun:"cor"|"cov", opts:OptionsPattern[]] := Composition[
rTableToDataset,
RFunction @ TemplateApply[
StringTemplate["function(data){return(`fn`(data, method=`method`))}"],
<|"fn" -> fun, "method" -> ToString[OptionValue["Method"], InputForm]|>
],
datasetToRTable
]

Generate pearson correlation function

cor = createCorrFunction["cor"]

Use it on the data

cor @ Take[states77, 6]

Generate and use correlation with a different method:

createCorrFunction["cor", "Method" -> "spearman"] @ Take[states77, 6]

Same for covariance function

cov = createCorrFunction["cov"]

cov @ Take[states77, 6]

Example: working with financial data

Notes

Only the simplest plotting examples work

Many quantmod - related plotting examples may result in R / RLink crash, reasons to be investigated

Helper code

mathematicaRPlotWrapper= RFunction["function(filename, plotfun){
	temp <- plotfun()
	png(filename=filename)
	print(temp) 
	dev.off()}"
];

Clear[getRPlot];
getRPlot[plotFun_RFunction]:=
	With[{tempfile = FileNameJoin[{$TemporaryDirectory,"temp.jpg"}]},
		If[FileExistsQ[tempfile], DeleteFile[tempfile]];
		mathematicaRPlotWrapper[tempfile,plotFun];
		If[!FileExistsQ[tempfile],Return[$Failed]];
		Import[tempfile]
	];

ClearAll[toDates]
toDates[dates: {___DateObject}] :=
RObject[Map[N @* UnixTime, dates], RAttributes["tclass":>{"Date"},"tzone":>{"UTC"}]]

ClearAll[toRFinancialTimeSeries]
toRFinancialTimeSeries[ts_TemporalData, stockName_String] := 
	With[{dates = toDates[ts["Dates"]]},
		RObject[
			Transpose[{Normal[ts["Values"]][[All, 1]]}],
			RAttributes["index":> dates, "class":>{"xts","zoo"},"dimnames":>{Null,{stockName}}]
		]
	]

Example

RVirtualEnvironmentEnsurePackages["renv432", "quantmod"]

ClearAll[plotMarketData]
plotMarketData[dt_TemporalData, stockName_String] := RWithPackages["pastecs"][
	REvaluate["library(quantmod)"];
	RSet["marketData", toRFinancialTimeSeries[dt, stockName]];
	getRPlot[RFunction["function(){return(plot(marketData))}"]]
]

Getting some financial data

brkbTS = FinancialData["BRK-B","Open",{{2000,1,1},{2010,4,1}}]

DateListPlot @ brkbTS

Plotting it in R and getting back the image

plotMarketData[brkbTS, "BRK-B"]

Roadmap / next steps

Support for multiple R sessions in RLink

Improvements to R virtual environments

Fully integrate R virtual environments with RLink

Try to speed up the creation process

Better integration with R installations and package management

Improved integration with ExternalEvaluate

ExternalObject

ExternalOperation

Evaluators search and registration

Supporting more R data types out of the box

Better support for R data frames / Dataset / Tabular interop

Built-in support for R plots / images

Other commonly used R data types

Better error handling for RLink

This will allow to build more robust infrastructure on top of RLink

Thank you!

Additional material

Overview of RLink—Sending Data to R and Getting Data from R

Init

InstallR[]

How to Send Data to R: RSet

We use the RSet function for that. You have to provide a name of the variable in R workspace, where the data will be stored. For example:

RSet["myVar",10]

RSet["myList",Range[10]]

How to Get the Data from R

There is no separate command to get the values of variables from R—this is a part of the more general REvaluate functionality. You can test the assignment with the help of REvaluate:

REvaluate["myVar"]

REvaluate["myList"]

REvaluate["myList <-  c(1,2,3)"]

Part (and Other) Assignments

You can also use RSet to assign to parts of R objects, such as vectors or lists. For example:

RSet["mt",{{1,2,3},{4,5,6},{7,8,9}}];
RSet["mt[1,]",{0,0,0}];
REvaluate["mt"]

Generally, the string (variable name in RSet) can be any R expression that can be legally assigned a value in R.

Overview of RLink—The Core RLink-Type System

Simplified R Object Model

The following scheme illustrates the simplified object model of R, in the way it is used by RLink.

|RCode
RObject 	--> |RCoreObject + RAttributes
				|REnvironment

				|-- NULL							
RCoreObject --> |-- RVEctor
				|-- RList
				|-- RFunction

RFunction	-->	|-- builtin
				|-- closure				
				
RVector		--> [(RNativeType|NA)..]

NA			-->	Missing element, can be at any position in a vector

				|--	integer
				|--	double
RNativeType --> |--	complex
				|--	logical: TRUE|FALSE		
				|--	character (string)
				
RList		-->	{RObject..}

RAttributes	--> RList

As you can see, within this model, any R object can be represented as an R vector, R list, R function, or R NULL object, plus any R object can have attributes, which themselves are stored in an R list. R vectors can only contain the elements of the same native type, of which we support five types: integer, double, complex, logical and character.

There are also objects of types REnvironment and RCode, which represent R environments and generic R objects that do not have a special support in RLink, respectively. These two types are different from the rest in that they are used for representation (on the Mathematica side) only, and one generally cannot correctly reconstruct R objects from objects of those types.

Long and Short Forms of Data Representation in RLink

Here is a simple vector of integers:

vec = {1,2,3,4,5}

Here is the RLink internal form of it:

ToRForm[vec]//InputForm

Same for the matrix (note the column-major order in which the matrix data is stored):

ToRForm[{{1,2,3},{4,5,6}}]//InputForm

And for a list:

Range/@Range[4]

liform = ToRForm[Range/@Range[4]]

Every Mathematica expression that can be sent to R is first internally converted to this (usually longer) internal form.

You can use FromRDataType to convert the longer form back to the short one:

FromRForm[liform]

Note that, most of the time, you won't need the long form of R objects. It is, however, useful in some circumstances; in particular, sometimes you may want to check the interpretation of your short-form input by RLink.

Overview of RLink—What Data Can Be Sent to R

Notes

You can send numerical (integer, real of complex) arrays, strings and lists of strings, arrays of Boolean values, the value Null and arbitrarily nested lists containing these elements (and lists of them). Generally, any Wolfram Language expression on which the function ToRDataType does not produce $Failed, can be sent.

Simple Objects (Expressions)

Send a list of integers

RSet["v",Range[10]];
REvaluate["v"]

A matrix

RSet["w",{{1,2},{3,4}}];
REvaluate["w"]

A list of strings

RSet["words",StringSplit["This is a test",Whitespace]];
REvaluate["words"]

A list of Boolean values

RSet["bvec",{True,True,False,True}];
REvaluate["bvec"]

Sending Null

RSet["nl",Null];
REvaluate["nl"]//InputForm

A combination of types in a list [currently broken, will be fixed in 14.2]

RSet["comblist",{{1,2,3},{"a","b"},{{True,False,True},{{1,2},{3,4}}}}];
REvaluate["comblist"]

R Objects with Attributes

Many R objects have one or more attributes. In case we need to send such an object to R, it has to be wrapped into an RObject head, and its attributes are placed as delayed rules, inside an RAttributes container:

RSet["robj",RObject[{1,2,3},RAttributes["myAttr":>{"a","b"}]]];
REvaluate["robj"]

Overview of RLink—Executing R Code and Getting Data from R

Executing a Single Line of R Code

Use REvaluate for that. Here, for example, we will use it to generate a random vector of integers:

REvaluate["as.integer (runif(20, 0, 10))"]

In this case, the result was transferred back to Mathematica but not saved anywhere in R workspace. If we wish to also save the result, we can assign it to some variable in the R workspace:

REvaluate["rvec <- as.integer(runif(20, 0, 10))"]

We can test now that the values were saved in an R variable:

REvaluate["rvec"]

Here we do a little more involved computation: compute the number frequencies with table and return the result as an R data frame:

freqs = REvaluate["as.data.frame(table(rvec))"]

TableForm@freqs

This will work in the future versions of RLink:

Suppressing the Output/Data Transfer from R

Sometimes, you may wish to also suppress the output on the R side—that is, suppress the data transfer from R to Mathematica. To do this, you have to put a semicolon at the end of your code. For example:

REvaluate["rvecsq <- rvec^2;"]

We can now check that the assignment took place:

REvaluate["rvecsq"]

Multiple Lines of Code

You can execute multi-line chunks of R code with REvaluate, but in this case, you have to enclose the code in curly braces. For example:

REvaluate[
"{
a <- 1
b <- 2
c <- 3
c(a,b,c)
}"]

You can also suppress the output with the semicolon here:

REvaluate["{
a <- 1
b <- 2
};"]

Overview of RLink—Using R Functions

General Points

R functions are references

Two types of references: type I and type II

Type I references do not depend on a specific RLink session.

Type II references are obtained by executing REvaluate and do depend on specific RLink session.

RFunction[r code][args]

Is used to wrap existing or newly defined anonymous R functions

Optional arguments are not currently supported.

Using Built-in R Functions

Use RFunction

Sum a list of numbers:

RFunction["sum"][Range[10]]

Compute values of sine on a number of points:

RFunction["sin"][Range[0, 2Pi, 0.1]]
%//ListPlot

Retrieve the information on all installed R packages:

RFunction["installed.packages"][] // Short[#,5]&

Could also use REvaluate, but this is not recommended

sumFunc = REvaluate["sum"]
sumFunc[Range[10]]

Defining Your Own Functions

Defining your functions on the R side with REvaluate

Here is an example:

REvaluate["myFun <- function(vec) vec^2"]

The result is an opaque Mathematica reference to the R function we constructed:

REvaluate["myFun(c(1,2,3))"]

A somewhat larger example: a possible implementation of the Split function in R, for vectors:

split = REvaluate["vsplit <- function(vec,test){
result <-list();
current <- list(vec[1]);
for(i in 2:length(vec)){
	if(test(vec[i-1],vec[i])){
		current <- c(current,vec[i])
	}else{
		result <- c(result,list(unlist(current)))
		current <- list(vec[i])
	}
}
if(length(current) != 0){
	result <- c(result,list(unlist(current)))
}
return(result)
}"]

One way to use it on data from Mathematica is to first send that data to R and store it in some variable in R workspace. For example:

runs ={2,1,2,1,3,3,0,1,1,1,0,3,1,1,3};
RSet["runs",runs];
REvaluate["vsplit(runs,function(fst,sec) fst == sec)"]

This is, however, inconvenient, and the R function’s references can be called directly on Mathematica arguments:

split[runs,RFunction["function(fst,sec) fst == sec"]]

This also shows how we can use R functions’ references for the higher-order functions, since here we just passed a function’s reference for a comparison function as a second argument.

Defining and using R functions on the Wolfram Language side with RFunction

Using the previously defined functions.

Square the vector:

REvaluate["myFun"][Range[10]]

Or:

RFunction["myFun"][Range[10]]

The latter form is better in general, since there we create a reference that is both permanent and cached, whereas REvaluate creates a reference that has a finite lifetime of a single RLink session.

Split a list (vector)—note that here we had to define a closure to embed the testing function:

RFunction["function(x) vsplit(x,function(fst,sec) fst == sec)"][{2,1,2,1,3,3,0,1,1,1,0,3,1,1,3}]

You can, of course, also use built-in R functions. Here we compute a cumulative sum (analog of Accumulate):

RFunction["cumsum"][Range[10]]

Or a total of the list:

RFunction["sum"][Range[10]]

Transpose a matrix:

RFunction["t"][{Range[5],Range[6,10]}]