R Usage

Experimental polyglot support.

R is now partially supported in the runner.

We recommend using rpy2 (link to documentation) which embed a R runtime inside Python so it can run in the same process. Spawning a new process is still possible, but not recommended, as it will considerably slow down your code for big loops.

Submit

The R runtime will only be installed if a requirements.r.txt file is present in the root directory of your submitted files.

As with Python packages, R packages are subject to a whitelist and can be requested using the same button. In the dialog, chose "R" in the "Programming Language" select.

Notebooks

Importing a package

For notebook users, the packages are automatically extracted from the importr("<name>") calls, which is provided by rpy2.

Python Notebook Cell
# Import the `importr` function
from rpy2.robjects.packages import importr

# Import the "base" R package
base = importr("base")

The following format must be followed:

  • The import must be declared at the root level.

  • The result must be assigned to a variable; the variable's name will not matter.

  • The function name must be importr, and it must be imported as shown in the example above.

  • The first argument must be a string constant, variables or other will be ignored.

  • The other arguments are ignored; this allows for custom import mapping if necessary.

The line will not be commented, read more about line commenting here.

Installing a package

If the library is not available in your environment, you can install it using either apt-get (the recommended method) or the install.packages() from the utils package.

Python Notebook Cell
# Use `apt-get` as a regular command
!apt-get install r-cran-lme4
Python Notebook Cell
# Use the `utils` package, but it must be imported first
from rpy2.robjects.packages import importr
utils = importr("utils")

# Call `install.packages(...)`, this will be commented on submit
utils.install_packages("lme4")

Declaring a R function

Any R code can be run using the robjects.r(code) function.

The following code can be used to define a function written entirely in R:

Python Notebook Cell
from rpy2.robjects import r

# Declare the function in R and have it return
f = r("""
	f <- function(r, verbose=FALSE) {
		if (verbose) {
			cat("I am calling f().\n")
		}

		2 * pi * r
	}

	# Don't forget to return it
	f
""")

# Call the function
circumference = f(3)

Files

Manage requirements

For CLI or files users, the packages must be provided in a requirements.r.txt file. The format is the same as a regular requirements.txt file, except all specs, extras, and comments will be ignored (for now).

Technically, notebook users can also do it by using embedded files.

Executing a file

A R script can be run using robject.r.source(path), and the variable will be accessible via the robject.r[key] accessor.

Local File: `hello.R`
cat("Hello world from a R file")

x <- 42
Local File: `main.py`
# Import the R instance
from rpy2.robjects import r

# Load and execute the R file
r.source('hello.R')
#> "Hello world from a R file"

# Get a value from the scope
print(r["x"])
#> "42"

Running

When a Run is started, before using pip to install your requirements, R and your requested packages are first installed using apt-get install r-cran-<name>. This has the benefit of installing them faster, but it greatly limits the number of choices to only the most downloaded ones (for now).

Recommendations

Redirect outputs

R's output going through rpy2 callback and can be a bit hard to read, a solution is to redirect the outputs:

Notebook Cell
# @crunch/keep:on

from rpy2 import rinterface

rpy2.rinterface_lib.callbacks.consolewrite_print = lambda x: sys.stdout.write(x)
rpy2.rinterface_lib.callbacks.consolewrite_warnerror = lambda x: sys.stderr.write(x)
rpy2.rinterface_lib.callbacks.consoleflush = lambda: sys.stdout.flush(); sys.stderr.flush()

The comment at the very top is important because it ensures that your code is not commented out.

Pandas Conversion

rpy2 supports DataFrame conversion, but you must use the with syntax to enable/disable conversion at the desired location.

An easier way is to use the following helpers:

Notebook Cell
import pandas as pd
import rpy2.robjects as ro
from rpy2.robjects import pandas2ri

def convert_df_pandas_to_r(pandas_df: pd.DataFrame) -> "FloatMatrix":
    """
    Convert a pandas's DataFrame to an equivalent R's FloatMatrix.
    """

    with (ro.default_converter + pandas2ri.converter).context():
        return ro.conversion.get_conversion().py2rpy(pandas_df)

def convert_df_r_to_pandas(r_df: "FloatMatrix") -> pd.DataFrame:
    """
    Convert a R's FloatMatrix to an equivalent pandas's DataFrame.
    """

    with (ro.default_converter + pandas2ri.converter).context():
        return ro.conversion.get_conversion().rpy2py(r_df)

Last updated