matplotlib inline plots with reticulate on RStudio Server

rstudioserver
reticulate

#1

Reticulate is awesome, and I love that we can run python code but get all the goodies of RMarkdown including publishing to Connect.

While reticulate (using RStudio Preview 1.2) works fine on my local machine, when I try to run it with an RStudio Server Preview 1.2 running on an AWS EC2 instance, I get an error

"TclError: couldn't connect to display ":0"

Detailed traceback: 
File "<string>", line 1, in <module> 
File "/home/rstudio/.local/lib/python3.6/site-packages/matplotlib/pyplot.py", line 2748, in plot 
return gca().plot( 
File "/home/rstudio/.local/lib/python3.6/site-packages/matplotlib/pyplot.py", line 934, in gca 
return gcf().gca(**kwargs) 
File "/home/rstudio/.local/lib/python3.6/site-packages/matplotlib/pyplot.py", line 577, in gcf 
return figure() 
File "/home/rstudio/.local/lib/python3.6/site-packages/matplotlib/pyplot.py", line 524, in figure 
**kwargs) 
File "/home/rstudio/.local/lib/python3.6/site-packages/matplotlib/backend_bases.py", line 3218, in new_figure_manager 
return cls.new_figure_manager_given_figure(num, fig) 
File "/home/rstudio/.local/lib/python3.6/site-packages/matplotlib/backends/_backend_tk.py", line 1008, in new_figure_manager_given_figure 
window = Tk.Tk(className="matplotlib") 
File "/usr/lib/python3.6/tkinter/__init__.p"

FYI -- I'm running RStudio Server on Ubuntu 18.04. I've installed tkinter using sudo apt-get install python3-tk or I get an error about "ImportError: No module named 'tkinter'" when I try to show the plot.

The display environmental variable on RStudio Server is

Sys.getenv("DISPLAY") 
[1] ":0"

while on my Mac it's

Sys.getenv("DISPLAY") 
[1] "/private/tmp/com.apple.launchd.dajCiOg7ER/org.macosforge.xquartz:0"

so I suspect there's an issue that matplotlib doesn't know where to display the plot. Any suggestions on how to solve this issue?


#2

The issue here relates to the matplotlib backend that's being selected by default. It looks like the TkAgg backend can have issues in many cases, so in the next iteration of the IDE preview we're going to enforce usage of the Agg backend.

For now, you should be able to work around the issue. First, restart R, and then run the following reticulate R code:

library(reticulate)
matplotlib <- import("matplotlib", convert = TRUE)
matplotlib$use("Agg")

and then future plotting attempts should work as expected.


#3

Thanks for looking at this Kevin! Unfortunately, I still get this error using the 'Agg' backend. Here's a screenshot of the error.

Session Info

R version 3.5.1 (2018-07-02)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.1 LTS

Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

locale:
 [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8        LC_COLLATE=C.UTF-8    
 [5] LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8    LC_PAPER=C.UTF-8       LC_NAME=C             
 [9] LC_ADDRESS=C           LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] reticulate_1.10 knitr_1.20      dplyr_0.7.6    

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.19     lattice_0.20-35  crayon_1.3.4     assertthat_0.2.0 grid_3.5.1       R6_2.3.0        
 [7] jsonlite_1.5     magrittr_1.5     pillar_1.3.0     rlang_0.2.2      bindrcpp_0.2.2   Matrix_1.2-14   
[13] tools_3.5.1      glue_1.3.0       purrr_0.2.5      compiler_3.5.1   pkgconfig_2.0.2  bindr_0.1.1     
[19] tidyselect_0.2.5 tibble_1.4.2   

This is on an AWS EC2 instance running Ubuntu 18.04 using python3, with matplotlib installed through pip3, and R and RStudio installed in the standard way.


#4

Dang, okay. I'll try standing up an AWS EC2 instance tomorrow and see if I can figure out what's going on.


#5

Thanks. Here's the relevant part of the setup script I used, starting from the Ubuntu Server 18.04 LTS (HVM), SSD Volume Type - ami-0ac019f4fcb7cb7e6 AMI.


# RStudio user and password
RSTUDIO_USERNAME=rstudio
RSTUDIO_PASSWORD=rstudio

# Create RStudio user
sudo useradd -m $RSTUDIO_USERNAME
sudo echo "$RSTUDIO_USERNAME:$RSTUDIO_PASSWORD" | sudo chpasswd
sudo adduser $RSTUDIO_USERNAME sudo

# General software
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade
sudo apt install -y htop bmon jq pigz libpqxx-dev libgsl-dev libxml2-dev libcurl4-openssl-dev libpng-dev     

# Python
sudo apt install -y python python3 python-pip python3-pip python3-tk
sudo -H pip install --upgrade pip
sudo -H pip3 install --upgrade pip

# install python packages
sudo pip3 install numpy pandas matplotlib

# AWS CLI
sudo -H pip3 install awscli --upgrade

# Java
sudo apt install -y openjdk-8-jre openjdk-8-jdk

# R
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/'
sudo apt update && sudo apt install -y r-base r-base-dev
sudo R CMD javareconf

# RStudio Server
sudo apt install -y libapparmor1 gdebi-core
wget https://s3.amazonaws.com/rstudio-ide-build/server/trusty/amd64/rstudio-server-1.2.1047-amd64.deb
sudo gdebi --non-interactive rstudio-server-1.2.1047-amd64.deb
# set rstudio to launch from default port 80
sudo sh -c "echo 'www-port=80' >> /etc/rstudio/rserver.conf"
sudo service rstudio-server restart

R Notebook below (sorry can't attach it:

#####################

---
title: "Python Code in an R Notebook"
output: html_notebook
---

Demo of running Python code in an R notebook.

Use system install python3 


```{r}
library(dplyr)
library(reticulate)

use_python("/usr/bin/python3")

py_config()

# some data
data(mtcars)
glimpse(mtcars)
write.csv(mtcars, file = "mtcars.csv", row.names = FALSE, quote = FALSE)
```

Test that Pandas works.

```{python}
import pandas
pycars = pandas.read_csv("mtcars.csv")
pycars = pycars[pycars['vs'] == 1]
print(pycars.head())
```

Matplotlib


```{python}
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
import numpy as np

x = np.linspace(0, 10, 30)
y = np.sin(x)

plt.plot(x, y, 'o', color='black')
plt.show()
```

Doesn't work.

Try in an R chunk calling Python.

```{r}
matplotlib <- import("matplotlib", convert = TRUE)
#matplotlib$use("Agg")

np <- import("numpy", as = "np")
plt <- import("matplotlib.pyplot", as = "plt")

x <- np$linspace(0, 10, 100)
plt$plot(x, x, label = "linear")
plt$legend()
plt$show()
```

Nope.

#6

I've just tested and this works as long at the plot backend has not yet been initialized:

library(reticulate)
matplotlib <- import("matplotlib")
matplotlib$use("Agg", force = TRUE)

So I think this does work as expected as long as the TkAgg backend doesn't leak in. Note that we'll have a better fix for this coming in the next preview release of RStudio so hopefully this issue should be moot by then!


#7

Thanks Kevin - that did it!

Funny thing -- the R chunk still doesn't show any output

matplotlib <- import("matplotlib")
matplotlib$use("Agg", force = TRUE)

np <- import("numpy", as = "np")
plt <- import("matplotlib.pyplot", as = "plt")

x <- np$linspace(0, 10, 100)
plt$plot(x, x, label = "linear")
plt$legend()
plt$show()

but, once I run matplotlib$use("Agg", force = TRUE) within an R chunk, then the Python matplotlib chunk does work!

And more confusing, if I try to set the matplotlib backend in the Python chunk (but not in the R reticulate chunk), it still doesn't work!?

import matplotlib
matplotlib.use('Agg')

Anyway, thanks for your assistance and I'd be happy to help and debug your next preview release if needed!