tidyverse / ggplot2

An implementation of the Grammar of Graphics in R
https://ggplot2.tidyverse.org
Other
6.4k stars 2k forks source link

Version 3.5.0 or 3.5.1 cannot render a plot made with 3.4.4 #5865

Closed tommmmi closed 2 months ago

tommmmi commented 2 months ago

Short background: I make quite extensive reports using RMarkdown and they often include figures produced with ggplot2. For faster re-knitting, I like to save finished tables and figures to RDS files and just use readRDS() function in the knit phase.

Few days ago I had to get back to an old project and add a new table to the report but suddenly the file refused to knit and threw an error.

Here is the code to reproduce the bug:

Fresh R session with ggplot2 version 3.4.4:

library(ggplot2)

## this section is from the ggplot() function examples:
set.seed(1)

sample_df <- data.frame(
  group = factor(rep(letters[1:3], each = 10)),
  value = rnorm(30)
)

group_means_df <- setNames(
  aggregate(value ~ group, sample_df, mean),
  c("group", "group_mean")
)

ggplot(data = sample_df, mapping = aes(x = group, y = value)) +
  geom_point() +
  geom_point(
    mapping = aes(y = group_mean), data = group_means_df,
    colour = 'red', size = 3
  ) -> gg_fig

## save as RDS
saveRDS(gg_fig, file="gg_fig_3_4_4.rds")

Fresh R session with ggplot2 version 3.5.0:

library(ggplot2)

readRDS("gg_fig_3_4_4.rds") -> gg_fig_old_ver

gg_fig_old_ver

This produces 'could not find function "scales_add_defaults"' error:

Error in `geom_point()`:
! Problem while computing aesthetics.
ℹ Error occurred in the 1st layer.
Caused by error in `scales_add_defaults()`:
! could not find function "scales_add_defaults"
Run `rlang::last_trace()` to see where the error occurred.

It took me quite a while to figure out what's causing the error because nothing changed in the report except the newly added table.

I drew the figure with both versions and compared the ggplot-object names:

> names(gg_fig_3_5_0)
 [1] "data"        "layers"      "scales"      "guides"      "mapping"    
 [6] "theme"       "coordinates" "facet"       "plot_env"    "layout"     
[11] "labels"     
> names(gg_fig_3_4_4)
[1] "data"        "layers"      "scales"      "mapping"     "theme"      
[6] "coordinates" "facet"       "plot_env"    "labels"     

I think the newer version of ggplot2 cannot render a figure that has been made with version 3.4.4 because version 3.4.4 ggplot-object doesn't have guides- and layout- ggproto-objects in it?

teunbrand commented 2 months ago

Thanks for the report!

We explicitly do not recommend saving ggplot objects to disk. When saving ggplot objects to disk, internal pieces of code needed to generate the plot are saved along with it. When an old plot is loaded, the saved code may be incompatible with the code of the current ggplot version, leading to errors like the one you encountered.

While we do our best to keep the user-facing functions as stable as we reasonably can, no such promises are made about internal code. It would make ggplot2 unmaintainable if internal code has to be backward compatible with previous versions. Therefore, we recommend against saving plot objects to disk. As such, we won't fix rendering old plots with new versions.

There are several things you might try for this issue:

tommmmi commented 2 months ago

Thank you @teunbrand for the Grob tip!

I quickly tested it out and it seemed to work saving Grob to RDS from v3.4.4 and drawing it in v3.5.0.

R with ggplot2 version 3.4.4:

ggplotGrob(gg_fig) -> gg_grob

saveRDS(gg_grob, file="gg_grob_3_4_4.rds")

R with ggplot2 version 3.5.0:

readRDS("gg_grob_3_4_4.rds") -> gg_grob_old_ver

grid::grid.draw(gg_grob_old_ver)
grid_draw

Works the other way round, too. Save the Grob object from v3.5.0 and grid.draw it in v3.4.4.