rfor-loopggplot2ggsaveragg

Different title in ggsave PNG when looping over plots


Saving a ggplots as PNG within a loop gives me a wrong plot title for one of the plots.

See reprex below, output corresponds to what I see in RStudio plot pane.

But then, my saved PNG via ggsave() has wrong "Sr" title twice where one should be "Fe". Other two plot titles are correct.

Is this a bug or am I doing something wrong?

library(tidyverse)
#> Warning: package 'tibble' was built under R version 4.5.1
#> Warning: package 'purrr' was built under R version 4.5.1

df <-
  tribble(~var, ~x, ~y,
          "Sr", 1, 3,
          "Fe", 1, 2,
          "Al", 1, 4)
df
#> # A tibble: 3 × 3
#>   var       x     y
#>   <chr> <dbl> <dbl>
#> 1 Sr        1     3
#> 2 Fe        1     2
#> 3 Al        1     4

for (i in unique(df$var)){
  
  print(i)
  
  p <-df |> 
    filter(var == i) |> 
    ggplot(aes(x, y)) +
    geom_point() +
    ggtitle(i)
  
  plot(p)
  
  ggsave(paste0("plot_", i, ".png"))
}
#> [1] "Sr"

#> Saving 7 x 5 in image
#> [1] "Fe"

#> Saving 7 x 5 in image
#> [1] "Al"

#> Saving 7 x 5 in image

Created on 2025-08-18 with reprex v2.1.1

My saved "plot_Fe.png" from working directory: plot showing the saved plot_Fe.png file

Note, y = 2 is correct for Fe, but the title should not be Sr.

sessionInfo()

R version 4.5.0 (2025-04-11 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=English_United Kingdom.utf8  LC_CTYPE=English_United Kingdom.utf8    LC_MONETARY=English_United Kingdom.utf8
[4] LC_NUMERIC=C                            LC_TIME=English_United Kingdom.utf8    

time zone: Europe/Berlin
tzcode source: internal

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

other attached packages:
 [1] lubridate_1.9.4 forcats_1.0.0   stringr_1.5.1   dplyr_1.1.4     purrr_1.1.0     readr_2.1.5     tidyr_1.3.1    
 [8] tibble_3.3.0    ggplot2_3.5.2   tidyverse_2.0.0

loaded via a namespace (and not attached):
 [1] utf8_1.2.6         generics_0.1.4     stringi_1.8.7      hms_1.1.3          digest_0.6.37      magrittr_2.0.3    
 [7] evaluate_1.0.4     grid_4.5.0         timechange_0.3.0   RColorBrewer_1.1-3 fastmap_1.2.0      processx_3.8.6    
[13] ps_1.9.1           scales_1.4.0       textshaping_1.0.1  cli_3.6.5          rlang_1.1.6        reprex_2.1.1      
[19] withr_3.0.2        yaml_2.3.10        tools_4.5.0        tzdb_0.5.0         vctrs_0.6.5        R6_2.6.1          
[25] lifecycle_1.0.4    fs_1.6.6           ragg_1.4.0         pkgconfig_2.0.3    callr_3.7.6        clipr_0.8.0       
[31] pillar_1.11.0      gtable_0.3.6       glue_1.8.0         systemfonts_1.2.3  xfun_0.52          tidyselect_1.2.1  
[37] rstudioapi_0.17.1  knitr_1.50         farver_2.1.2       htmltools_0.5.8.1  rmarkdown_2.29     labeling_0.4.3    
[43] compiler_4.5.0    

I just ran the code on another machine with a different config (see sessionInfo() below) and there it does work correctly.

R version 4.4.1 (2024-06-14 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 10 x64 (build 19045)

Matrix products: default


locale:
[1] LC_COLLATE=English_United Kingdom.utf8  LC_CTYPE=English_United Kingdom.utf8    LC_MONETARY=English_United Kingdom.utf8
[4] LC_NUMERIC=C                            LC_TIME=English_United Kingdom.utf8    

time zone: Europe/Berlin
tzcode source: internal

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

other attached packages:
[1] lubridate_1.9.3 forcats_1.0.0   stringr_1.5.1   dplyr_1.1.4     purrr_1.0.2     readr_2.1.5     tidyr_1.3.1     tibble_3.2.1   
 [9] ggplot2_3.5.1   tidyverse_2.0.0

loaded via a namespace (and not attached):
[1] gtable_0.3.5      compiler_4.4.1    tidyselect_1.2.1  systemfonts_1.1.0 scales_1.3.0      textshaping_0.4.0 R6_2.5.1         
 [8] labeling_0.4.3    generics_0.1.3    munsell_0.5.1     pillar_1.9.0      tzdb_0.4.0        rlang_1.1.4       utf8_1.2.4       
[15] stringi_1.8.4     timechange_0.3.0  cli_3.6.3         withr_3.0.1       magrittr_2.0.3    grid_4.4.1        rstudioapi_0.16.0
[22] hms_1.1.3         lifecycle_1.0.4   vctrs_0.6.5       glue_1.7.0        farver_2.1.2      ragg_1.3.2        fansi_1.0.6      
[29] colorspace_2.1-1  tools_4.4.1       pkgconfig_2.0.3  

Solution

  • This seems to be a bug, not related to {ggplot2}, but to {ragg}. The following workaround works fine:

    library(tidyverse)
    
    df <-
      tribble(~var, ~x, ~y,
              "Sr", 1, 3,
              "Fe", 1, 2,
              "Al", 1, 4)
    
    for (i in unique(df$var)){
        
        print(i)
        
        p <- df |>
          filter(var == i) |>
          ggplot(aes(x, y)) +
          geom_point() +
          ggtitle({{as.symbol(i)}})
        
        plot(p)
        
        ggsave(paste0("plot_", i, ".png"))
        
      }
    
    #> [1] "Sr"
    #> Saving 7 x 5 in image
    #> [1] "Fe"
    #> Saving 7 x 5 in image
    #> [1] "Al"
    #> Saving 7 x 5 in image
    

    Created on 2025-08-18 with reprex v2.1.1