Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

could not find function: Plot saved under old version of ggplot2 cannot be rendered with new version #5865

Closed
tommmmi opened this issue Apr 26, 2024 · 5 comments

Comments

@tommmmi
Copy link

tommmmi commented Apr 26, 2024

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
Copy link
Collaborator

teunbrand commented Apr 26, 2024

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:

  • Use something like {renv} to help versioning for projects.
  • Save preprocessed data to disk and regenerate the plot in your scripts.
  • Save the rendered plot to disk using ggsave(), which is independent of the ggplot2 or grid versions.
  • Save the object resulting from ggplotGrob(plot) to disk. This is still subject to changes in {grid} and extensions that declare special makeContent methods for their graphical objects, but no longer relies on ggplot2's internals.

@teunbrand teunbrand closed this as not planned Won't fix, can't repro, duplicate, stale Apr 26, 2024
@tommmmi
Copy link
Author

tommmmi commented Apr 26, 2024

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.

@LukasWallrich
Copy link

Just for discoverability - the same issue (obviously) arises when the Rmd cache=TRUE option is used.

@lindsayplatt
Copy link

Commenting here in case others have had the could not find function "scales_add_defaults" error pop up when building a targets pipeline and stumble across this issue.

I bumped into this error because I am using ggplot2 objects as the output returned to create a tar_target() object, which means they are ultimately saved to disk under the targets paradigm. The target containing my ggplot2 object was not rebuilt after I updated ggplot2 since none of the dependencies triggered a rebuild. I had to manually trigger a rebuild using tar_invalidate() and then I was able to carry on.

@teunbrand
Copy link
Collaborator

I'll change the title for better discoverability

@teunbrand teunbrand changed the title Version 3.5.0 or 3.5.1 cannot render a plot made with 3.4.4 could not find function: Plot saved under old version of ggplot2 cannot be rendered with new version Nov 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants