rstudio / renv

renv: Project environments for R.

Home Page:https://rstudio.github.io/renv/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to deal with different versions of R?

grepinsight opened this issue · comments

Thank you for writing renv! I am excited to use it to make my code more future-proof and reproducible.

renv solves package management; my question is what is the best practice for handling different R versions?

For example, on my local computer, I have 3.5.2, but on a remote computer, I have 3.2.3, and I get the following message when trying to restore a project built from the local machine.

Project requested R version '3.5.2' but '3.2.3' is currently being used

In python, there are pyenv or conda which allows one to use multiple python versions in an isolated environment.
Is there an equivalent system that allows multiple versions of R?
If not, what's the current recommendation?

Currently, there isn't any explicit provisioning for different R versions in renv.

Note that currently, different R versions will indeed get their own separate R libraries by default, but they will all ultimately write to the same renv.lock lockfile on snapshot.

Ultimately though, it's just a warning -- renv will continue to function as normal, but it's possible that the packages recorded in your lockfile will not work with an older version of R.

Can you elaborate a bit more on why you want to support different versions of R -- e.g. why are you using R 3.2.3 on your remote machine, as opposed to something newer?

Thank you for your answer @kevinushey

Can you elaborate a bit more on why you want to support different versions of R -- e.g. why are you using R 3.2.3 on your remote machine, as opposed to something newer?

This is just mainly due to my laziness. Installing R for mac is super easy so I tend to try a newer version of R (granted, I am behind now that there is 3.6.2), whereas the remote machine is Linux and I usually install R via source often because I don't have root access.

Even if I have root access, installing a particular version of R on, say, Ubuntu is not trivial. I wish there is an easier mechanism for installing different versions of R and switching back and forth among those different versions.

My ultimate use case is to 1) enable fast/efficient development and 2) deploy the R code in a production setting.

Would you say rocker + renv would be the best solution for such use cases?

Would you say rocker + renv would be the best solution for such use cases?

I think that's the cleanest way forward, and it also protects you from issues that might arise if, say, system libraries were updated in a way that was incompatible with one or more R packages you use in your project. (Rare, but it does happen!)

The renv Docker vignette may be useful: https://rstudio.github.io/renv/articles/docker.html

Another alternative if you want something lighter weight than Rocker (which assumes the whole compute stack will be on Docker) is to use:

https://github.com/rstudio/r-builds

These R binaries:

  1. Install really fast on Ubuntu (from a .deb file) so you don't have to compile from source
  2. Are designed to support multiple versions of R being installed side-by-side, so you can switch seamlessly between R versions

Another alternative if you want something lighter weight than Rocker (which assumes the whole >compute stack will be on Docker) is to use:
https://github.com/rstudio/r-builds

@slopp Oh wow! thank you so much. This solves one of the major pain points I've had for years...

Now for a colleague to reproduce my analysis, I can just give R_VERSION and a renv cache tarball (and a bit of instruction) for him/her to quickly replicate my R environment!! this is super.

The renv Docker vignette may be useful: https://rstudio.github.io/renv/articles/docker.html

@kevinushey thank you for the recommendation. I think docker is a way to go if more rigor/reproducibility is required.

Related #253

Closing this since the issue raised can be solved by @slopp and @kevinushey 's recommendations. Thank you all!

Hi @kevinushey and @grepinsight, please re-open this issue.

I can give you a perfect example of why you might want to support multiple versions of R. About a year ago I upgraded R from 3.4.4 to 3.5 (finally). And immediately, my projects started breaking. After digging in, I discovered that one of my package was no longer supported in the new version of R (and almost all of my research depended on it!).

I think a tool like pyenv along with renv would be a fantastic way to manage this sort of situation.

And as it turns out, there is another Renv project on github that does exactly what pyenv does... but for R! This project not been updated in 8 years. So there might be an opportunity to fork the code (or take the project over entirely) and merge its capabilities with renv.

There's a lot that goes into a feature like this, though:

  1. Does renv need to get into the business of managing R installations to make this seamless?
  2. How do you tell renv where a user's set of R installations live?
  3. How does renv store the version of R to use? Do we also store the path to R? What about issues with portability (for collaborating with renv)?
  4. How does a user "activate" a particular version of R? How does this integrate with various front-ends (RStudio, ESS, and so on)?

Getting this right requires a lot of work and hence is why we haven't taken it on yet.

I find pyenv + pipenv an extremely useful combination when working with Python projects.
I guess renv would be the R alternative of pipenv, however, I could not find any alternative to pyenv for managing different versions of R.
So far, the best alternative I found was installing several pre-compiled R versions, but, this requires being root user.

So, I forked the pyenv project and did the necessary tweaks to allow it to work with R versions. I didn't have idea that someone tried this same approach some years ago, as @viking did 😂.
This work leads to the renv-installer project, to install it, simply clone the repo, and it will allow installing in one command, a selection of 33 different R versions. Switching between R versions results extremely easy.

A simple example, in Ubuntu 20.04 shell would be:

# Install it
$ git clone https://github.com/jcrodriguez1989/renv-installer.git ~/.renv
$ echo 'export RENV_ROOT="$HOME/.renv"' >> ~/.bashrc
$ echo 'export PATH="$RENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo -e 'if command -v renv 1>/dev/null 2>&1; then\n  eval "$(renv init -)"\nfi' >> ~/.bashrc
$ exec "$SHELL"

# Install user-locally R 3.0.0
$ renv install 3.0.0
# Set it to use it when R is called in this folder
$ renv local 3.0.0
$ R

I guess that renv-installer would be great in combination with renv, so I developed the bash renv sync command. When renv sync is executed in a folder that contains an renv.lock file, it will check if the required R version is installed by renv-installer, if so, it will set it as the current R to run in this folder, install renv package, and do renv::restore().

Please let me know if you have any doubts, comments, or suggestions about renv-installer.

Hi Kevin/Juan - thank you for your replies. I went ahead and installed Renv and it turns out that it works fine and has no issue working hand-in-hand with renv for package management. My setup is as follows:

First, install Renv (I am on a mac)

# Install Renv to home folder
cd ~/
git clone git://github.com/viking/Renv.git .Renv

# Add ~/.Renv/bin to $PATH
echo 'export PATH="$HOME/.Renv/bin:$PATH"' >> ~/.bash_profile

# Add Renv init to your shell to enable shims and autocompletion
echo 'eval "$(Renv init -)"' >> ~/.bash_profile

# Restart shell
exec $SHELL

Install R 3.6.3_1 into Renv using homebrew (mac only)

# Install r-latest
brew install r
brew unlink r

# Install r-3.4.4 from homebrew
# brew install https://raw.githubusercontent.com/jeroen/autobrew-core/94655a45637f90a408770c6374066fa5a76fb5ac/Formula/r.rb

brew unlink r

# Soft link R versions into Renv
ln -s /usr/local/Cellar/r/3.6.3_1 ~/.Renv/versions/3.6.3_1

# Rebuild shim binaries (run this from Terminal)
Renv rehash

Repeat the above procedure for each version of R that you want to install. Then set your global (or local) version of R and install renv for that version:

# Set the global R version
Renv global 3.6.3_1
Renv version

# Install renv
if (!requireNamespace("remotes")) {
  install.packages("remotes")
}

Select the local mirror to install from. Then continue:

remotes::install_github("rstudio/renv")

Tell renv where to find your packages cache:

touch ~/.Renviron
mkdir ~/Code/R_Packages
echo 'RENV_PATHS_CACHE = ~/Code/R_Packages/renv' >> ~/.Renviron

Tell R where to find its system packages. You only need to do this once and the %v token will automatically set the right folder for each version of R

echo "R_LIBS_USER='~/Code/R_Packages/system/%v'" >> ~/.Renviron

Now load up R and create your first project:

setwd("/path/to/project")
renv::init()

And if you want to auto-load your project whenever you are in the project's working directory, run the following shell command:

echo 'renv::activate("/path/to/project")' >> /path/to/project/.Rprofile

So all the pieces are there. The only thing I would change is to wrap the above shell commands into a script that could auto-install different versions of R into Renv and I would do the same way pyenv does, by adding the command:

Renv install 3.6.3_1 # or whichever version you want

For anyone else looking at this issue, especially on Mac, you might want to give https://asdf-vm.com/ a go. I've been using it to manage several separate R versions side-by-side, and you can switch from one to the other within a single terminal (as compared to something like https://github.com/hrbrmstr/RSwitch which switches the whole system across)

I want to bring up another use-case for not directly setting the "Version" attribute in the renv.lock file.

We use different R minor versions at work (some people are using R 4.0.2 from MRAN for the Intel MKL library, some people prefer to stay at the most up to date 4.0.3). Now, even if people use different minor versions of R, they will update the renv.lock file constantly when they use renv to install packages. This breaks any workflow with git since people keep overwriting their lock files.

I think the point that different R versions use different libraries and therefore need other renv libraries, is perfectly fine. But this is not the case for minor versions - and it would help us a lot if there was at least an option to set "Version": "4.0.x".

Thanks -- I've added the r.version project setting in 7267c20, to allow projects to set a specific version of R to be encoded in the lockfile (regardless of the one currently in use).

Incredible. That was fast. Thank you so much!

Since this discussion led me here ... here's my attempted hack at a macOS centric solution on the CLI. Involves creating a .Rversion file, kinda like using nvm and creating a .nvmrc file, and adding the x.y version to it, e.g. echo "4.0" > .Rversion. Ran via R like normal & rstudio in Bash/Zsh. Rscript function can be made for Rscript using the R function, by replacing with /usr/local/bin/Rscript.

Another Edit:

Just go here if you're interested. No switching of the system's global version of R for R & Rscript calls. Just the good ol' R_HOME variable.

Edit: made some edits since yesterday, compatible with both Zsh & Bash now.

For R:

function R() {
  rpath=/Library/Frameworks/R.framework/Versions
  old=$(readlink "$rpath"/Current); 
  if [ -f .Rversion ]; then
    new=$(cat .Rversion)
    if [[ $(/bin/ls "$rpath" | grep "$new") == "" ]]; then 
      echo "R version" "$new" "not installed" && return 1; else
      ln -sfh "$new" "$rpath"/Current; fi; fi
  if [ -n "$ZSH_VERSION" ]; then setopt local_options no_monitor; fi
  if [ -n "$BASH_VERSION" ]; then set +m; fi; 
  ({ sleep 1; ln -sfh "$old" "$rpath"/Current; if [ -n "$BASH_VERSION" ]; then set -m; fi } &)
  /usr/local/bin/R "$@" 
}

For RStudio:

function rstudio() {
  rpath=/Library/Frameworks/R.framework/Versions
  old=$(readlink "$rpath"/Current); 
  if [ -f .Rversion ]; then 
    new=$(cat .Rversion)
    if [[ $(/bin/ls "$rpath" | grep "$new") == "" ]]; then 
      echo "R version" "$new" "not installed" && return 1; else
      ln -sfh "$new" "$rpath"/Current; fi; fi
  if [ -n "$ZSH_VERSION" ]; then unsetopt local_options nomatch; fi
  if [ -f *.Rproj ]; then 
    open -na Rstudio *.Rproj; else 
    ("/Applications/RStudio.app/Contents/MacOS/RStudio" &)
  fi; ({ sleep 5; ln -sfh "$old" "$rpath"/Current;} &)
}