Skip to content

Keeping 2 identical plots #248

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

Keeping 2 identical plots #248

wants to merge 2 commits into from

Conversation

cderv
Copy link
Collaborator

@cderv cderv commented Jun 17, 2025

Important

This PR is here to share finding based on regression with 0.24.0 but does not solve the overall issue
with idential plots.

This fixes a regression. old behavior was keeping identical plots. closes #243

evaluate::evaluate(function() {
    hist(cars$speed)
    hist(cars$speed)
})

This is a special case to handle, as evaluate still needs to identify plots that are really different and avoid changes of par() or other small changes that are not visually different.

Same as with ggplot

  library(ggplot2)
  df <- data.frame(x = 1, y = 1)
  ev <- evaluate::evaluate(function() {
    ggplot(df) + geom_point(aes(x, y))
    ggplot(df) + geom_point(aes(x, y))
  })
  ev
#> <evaluation>
#> Source code: 
#>   ggplot(df) + geom_point(aes(x, y))
#> Plot [3]:
#>   <base> palette2()
#>   <grid> requireNamespace("ggplot2", quietly = TRUE)
#>   <grid> drawGTree(x)
#> Source code: 
#>   ggplot(df) + geom_point(aes(x, y))
#> Plot [3]:
#>   <base> palette2()
#>   <grid> requireNamespace("ggplot2", quietly = TRUE)
#>   <grid> drawGTree(x)

The changes in this PR are based on comparison with old working behavior at e1ece68 and v0.24.0

Especially:

New behavior is filtering out plots with identical display list

evaluate/R/graphics.R

Lines 14 to 17 in cadec2f

looks_different <- function(old_dl, new_dl) {
if (identical(old_dl, new_dl)) {
return(FALSE)
}

and this is the case with those two identicals

hist(cars$speed)
hist(cars$speed)

Previously this checks was done only when new display list was higher length that old due to if (n2 <= n1) return(FALSE)

evaluate/R/graphics.R

Lines 29 to 45 in a3a85ce

is_par_change <- function(p1, p2) {
calls1 <- plot_calls(p1)
calls2 <- plot_calls(p2)
n1 <- length(calls1)
n2 <- length(calls2)
if (n2 <= n1) return(FALSE)
i1 <- seq_len(n1)
if (!identical(calls1, calls2[i1])) return(FALSE)
# also check if the content of the display list is still the same (note we
# need p1[[1]][] as well because [] turns a dotted pair list into a list)
if (!identical(p1[[1]][i1], p2[[1]][i1])) return(FALSE)
last <- calls2[(n1 + 1):n2]
all(last %in% empty_calls)
}

Lastly, the previous behavior only checked for nonvisual changes when the old and new plots were not identical. Identical recorded plots were considered duplicates to filter out, but were identical on both their display list AND their raw representation

https://github.com/cderv/evaluate/blob/a3a85ce83b9841b2924ad66d16a551cca1368533/R/graphics.R#L18-L21

And the think is: Two calls to hist(cars$speed) does lead to identical display list, but not identical raw.

hist(cars$speed)
last_plot <- recordPlot()

hist(cars$speed)
plot <- recordPlot()
> identical(last_plot, plot)
[1] FALSE
> waldo::compare(last_plot, plot)
`old[[2]][1:8]`: "00" "00" "00" "00" "00" "00" "00" "00"
`new[[2]][1:8]`: "01" "00" "00" "00" "01" "00" "00" "00"

`old[[2]][444:451]`: "00" "00" "00" "f0" "3f" "00" "00" "00"
`new[[2]][444:451]`: "00" "00" "00" "39" "40" "00" "00" "00"

`old[[2]][484:491]`: "00" "00" "00" "f0" "3f" "00" "00" "00"
`new[[2]][484:491]`: "00" "00" "00" "2e" "40" "00" "00" "00"

`old[[2]][492:498]`: "00" "00" "00" "14" "40" "72" "73"
`new[[2]][492:498]`: "00" "00" "00" "08" "40" "72" "73"

`old[[2]][35740:35747]`: "00" "00" "00" "00" "00" "00" "00" "00"
`new[[2]][35740:35747]`: "00" "00" "00" "f0" "bf" "00" "00" "00"

`old[[2]][35748:35771]`: "00" "00" "00" "f0" "3f" "00" "00" "00" "00" "00" and 14 more...
`new[[2]][35748:35771]`: "00" "00" "00" "3a" "40" "c3" "f5" "28" "5c" "8f" ...           

`old[[2]][35802:35811]`: "00" "00" "00" "9d" "ff" "ff" "ff" "b7" "1e" "85"
`new[[2]][35802:35811]`: "00" "00" "00" "00" "00" "00" "00" "b7" "1e" "85"

`old[[2]][35950:35987]`: "00" "7e" "c0" "00" "00" "00" "00" "00" "00" "00" and 28 more...
`new[[2]][35950:35987]`: "00" "7e" "c0" "0b" "a0" "de" "c7" "5b" "9a" "c3" ...    

I believe the old behavior was based on this. It does seem odd...

And looking further, this only works with two plots not 3.

This is with v0.24.0 before changes

> packageVersion("evaluate")
[1] ‘0.24.0> evaluate(function() {
+   plot(cars$speed)
+   plot(cars$speed)
+ }) |> classes()
+ evaluate(function() {
+   plot(cars$speed)
+   plot(cars$speed)
+   plot(cars$speed)
+ }) |> classes()
[1] "source"       "recordedplot" "source"       "recordedplot"
[1] "source"       "recordedplot" "source"       "recordedplot" "source"    

So, definitely a deeper problem.

> packageVersion("evaluate")
[1] ‘0.24.0> plot(cars$speed)
> plot1 <- recordPlot()
> plot(cars$speed)
> plot2 <- recordPlot()
> plot(cars$speed)
> plot3 <- recordPlot()

> identical(plot1, plot2)
+ identical(plot2, plot3)
[1] FALSE
[1] TRUE

This raises the question of whether and how evaluate should handle identical plots in the same evaluation. 🤔

cderv added 2 commits June 17, 2025 11:46
This fixes a regression. old behavior was keeping identical plots
```r
evaluate::evaluate(function() {
    hist(cars$speed)
    hist(cars$speed)
})
```

This is a special case to handle, as evaluate still need to identify plots that are really different and avoid change of `par()` or other small changes not visually different.
@cderv
Copy link
Collaborator Author

cderv commented Jun 17, 2025

I missed #247 so I am looking at it in comparaison

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

Successfully merging this pull request may close these issues.

new evaluate does remove identical plot more aggressively
1 participant