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.
# 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.
# Use `apt-get` as a regular command
!apt-get install r-cran-lme4
# 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:
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)
Even if the import must be at the root level, the call to robjects.r(code)
must be made within a Python function so that it is not commented out.
We recommend declaring them in a dedicated cell and using the # @crunch/keep:on
command to prevent them from being commented out. Read more about line commenting here.
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.
cat("Hello world from a R file")
x <- 42
# 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:
# @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:
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