RCall: function / method conflict with R functions

Hello everyone,

I am trying to figure out the resolution order of conflicting methods, particularly as it regards to the theme function in ggplot2 from R and Plots in Julia. Following up on the question here:

I’ve tried several of the suggested options. Only the first below works and the others which have been suggested do not. I am wondering why that is as when @rlibrary ggplot2 and theme is called before loading the Plots library, or @rimport ggplot2; ggplot2.theme or R"library(ggplot2)"; R"theme" is called, theme appears to refer to the ggplot2 theme function in the REPL. However, upon application I get the error that ERROR: syntax: invalid keyword argument name "axis.text" which suggests that Plots.theme is being invoked. There is also the suggestion not to load Plots at all, but that limits what I can do with the same script.

So my questions are:

  1. Why don’t some of the other methods work? Typing theme at the REPL seems to indicate that the ggplot2 theme function is preferentially referenced.
  2. Is the first method really the only way to get this to work?

First method - only one that works

using RCall
using Plots
using DataFrames
using RDatasets

@rlibrary ggplot2
R"library(ggplot2)"

iris = dataset("datasets", "iris")

ggplot(data=iris)+
  geom_point(mapping=aes(x=:PetalLength,y=:PetalWidth))+
  R"theme(axis.text = element_text(size = 14))"

Second method - doesn’t work

using RCall
using Plots
using DataFrames
using RDatasets

@rlibrary ggplot2
R"library(ggplot2)"

iris = dataset("datasets", "iris")

ggplot(data=iris)+
  geom_point(mapping=aes(x=:PetalLength,y=:PetalWidth))+
  R"theme"(axis.text = element_text(size = 14))

Third method - doesn’t work

using RCall
using Plots
using DataFrames
using RDatasets

@rlibrary ggplot2
@rimport ggplot2

iris = dataset("datasets", "iris")

ggplot(data=iris)+
  geom_point(mapping=aes(x=:PetalLength,y=:PetalWidth))+
  ggplot2.theme(axis.text = element_text(size = 14))

Fourth method - doesn’t work

using RCall
@rlibrary ggplot2
theme
using Plots
using DataFrames
using RDatasets

iris = dataset("datasets", "iris")

ggplot(data=iris)+
  geom_point(mapping=aes(x=:PetalLength,y=:PetalWidth))+
  theme(axis.text = element_text(size = 14))

Fifth method - doesn’t work

using RCall
@rlibrary ggplot2
ggtheme = theme
using Plots
using DataFrames
using RDatasets

iris = dataset("datasets", "iris")

ggplot(data=iris)+
  geom_point(mapping=aes(x=:PetalLength,y=:PetalWidth))+
  ggtheme(axis.text = element_text(size = 14))

I don’t think that’s what the error is suggesting. axis.text is just not valid keyword syntax in Julia, the . is always parsed for property access so it can’t be incorporated into symbols via normal source code. Per RCall.jl’s manual:

Some R functions may have keyword arguments which contain dots. RCall provides a string macro to escape those keywords, e.g,

julia> @rimport base as rbase

julia> rbase.sum([1, 2, 3], var"rm.na" = true)
RObject{IntSxp}
[1] 7
1 Like

Ah, thanks. I missed that.

All those instances work then if

theme(axis.text = element_text(size = 14))

is written as

theme(var"axis.text" = element_text(size = 14)).

Not sure actually, it’s been a while since I used RCall and it appears as if v0.14.5 removed var_str and the Internal API doesn’t document it anymore despite the earlier quoted section still being there. The associated Github issue doesn’t make the reason obvious. You’ll have to verify if it still works somehow, and if it doesn’t, a documentation update is in order. Wonder if Symbol("axis.text") => ... works as a workaround; all var_str did was expand to a Symbol that gets interpolated into the surrounding Expr, and keywords can be written as direct Symbols in pairs in source code.

var_str does seem to work for a few of the cases above I tested.

Indeed it’s still in the documentation.

On the other hand, wrapping in Symbol doesn’t work. theme(Symbol("axis.text") = ...) gives

ERROR: syntax: invalid keyword argument name "Symbol("axis.text")"

And theme(Symbol("axis.text") => ...) gives

ERROR: MethodError: no method matching sexpclass(::Pair{Symbol, RObject{VecSxp}}) The function sexpclass exists, but no method is defined for this combination of argument types.

The => expression with the direct Symbol instantiation in source code must strictly be written after ;, like theme(; Symbol("axis.text") => ...). Source code symbols can undergo a variety of keyword parsing options, including optional ; for = expressions, though => is not supported.

However, I’m not confident if it would work if the previous var_str implementation was removed from the macros file and export list, maybe it doesn’t work like that anymore. What is your RCall version, by the way?

1 Like

I think @var_str is now part of Base. Try

help?> @var_str
1 Like

Totally slipped my mind this was part of Base, and for a while (v1.3+). It does the same thing:

julia> dump(@macroexpand var"axis.text" = blah)
Expr
  head: Symbol =
  args: Array{Any}((2,))
    1: Symbol axis.text
    2: Symbol blah

It should be the latest version

(@v1.11) pkg> status RCall
Status `~/.julia/environments/v1.11/Project.toml`
  [6f49c342] RCall v0.14.6

So okay, the following works:

using RCall
using Plots
using DataFrames
using RDatasets

@rlibrary ggplot2
@rimport ggplot2

iris = dataset("datasets", "iris")

ggplot(data=iris)+
  geom_point(mapping=aes(x=:PetalLength,y=:PetalWidth))+
  ggplot2.theme(; Symbol("axis.text") => element_text(size = 14))

as does the last expression replaced with

ggplot(data=iris)+
  geom_point(mapping=aes(x=:PetalLength,y=:PetalWidth))+
  ggplot2.theme(var"axis.text" = element_text(size = 14))
2 Likes

I realize there are great reasons to use ggplot directly interfacing with R, but in case folks are curious, here is how to generate this plot using Tidier/TidierPlots (powered by Makie).

using Tidier
using RDatasets

iris = dataset("datasets", "iris")

ggplot(iris) + 
    geom_point(aes(x=:PetalLength,y=:PetalWidth)) + 
    theme(xlabelsize = 14, ylabelsize = 14) +  # theme options directly from Makie
    labs(x = "Petal Length", y = "Petal Width") # adding the axis labels

which produces:

6 Likes

This is great! Didn’t realize it had come this far along. I understand it’s built on top of Makie but do you expect that it will take on R-like syntax or it will remain “Julian” in the way variables are references and the theming, etc.?

2 Likes

A bit of both.

It will continue to retain syntax that is as close to the original tidyverse as possible, but we play nicely with Julia (and enable Julian syntax) wherever it makes sense to do it.

For example, in this syntax using the aes() function, column names are referenced using symbols, which is a Julian convention.

ggplot(iris) + 
    geom_point(aes(x=:PetalLength,y=:PetalWidth)) + 
    theme(xlabelsize = 14, ylabelsize = 14) +
    labs(x = "Petal Length", y = "Petal Width")

However, the same plot can be generated using the @aes() macro, in which columns are referenced using bare column names, which is an R convention.

ggplot(iris) + 
    geom_point(@aes(x=PetalLength,y=PetalWidth)) + 
    theme(xlabelsize = 14, ylabelsize = 14) +
    labs(x = "Petal Length", y = "Petal Width")

What’s nice with using the @aes() macro is that you can give it any tidy expression (just like in R), and it will be mutated using that expression before being plotted. For example, if you wanted to add 1 to all of the petal widths before generating the plot, you can do this:

ggplot(iris) + 
    geom_point(@aes(x=PetalLength,y=PetalWidth + 1)) + 
    theme(xlabelsize = 14, ylabelsize = 14) +
    labs(x = "Petal Length", y = "Petal Width")

In terms of aesthetics, we support some of the ggplot aesthetics, but we support all Makie aesthetics (they are passed directly to Makie). And for theme, we support all Makie themes and theme options, including theme_minimal(), theme_dark(), etc. These are similarly passed through to Makie.

For example, adding theme_dark()

ggplot(iris) + 
    geom_point(@aes(x=PetalLength,y=PetalWidth)) + 
    theme(xlabelsize = 14, ylabelsize = 14) +
    theme_dark() +
    labs(x = "Petal Length", y = "Petal Width")

…generates:

Caveat: in the latest release, there are some bugs in positioning (“dodge” and “stack”) and with some of the aesthetics in certain plots (e.g., “color” vs. “fill”). That is largely because the package was rewritten to work with the latest version of Makie after it overhauled its rendering engine. These will get fixed.

3 Likes