I have found a bug in the gganimate
package that breaks one of my graphs (https://github.com/thomasp85/gganimate/issues/502). I know how to fix it if I step through the relevant function in debug mode (https://github.com/thomasp85/gganimate/issues/502#issuecomment-2395009593). I need to run this line of code:
data <- lapply(data, function(el){
el[order(el$x, decreasing = TRUE),]
})
just after line 76 of the gganimate:::ggplot_build.gganim
function (https://github.com/thomasp85/gganimate/blob/b46d1ff9bfee89c33ca8d6c4fd3fa1074c649027/R/plot-build.R#L76). Of course, using debug
only works if I'm running the code interactively. What I'd like to do is patch this function temporarily to get my graph to come out correctly when I'm running the code non-interactively (e.g., with knitr
).
I'd like to get a working graph while I wait for a bug fix in the package.
I was thinking something like this:
bdy = deparse(body(gganimate:::ggplot_build.gganim))
c(
bdy[1:41],
"data <- lapply(data, function(el){
el[order(el$x, decreasing = TRUE),]
})",
bdy[42:length(bdy)]
) -> bdy
f <- function(plot){}
body(f) <- as.expression(parse(text=paste0(bdy, collapse="\n")))
rlang::env_unlock(env = asNamespace('gganimate'))
rlang::env_binding_unlock(env = asNamespace('gganimate'))
assignInNamespace("ggplot_build.gganim", f, "gganimate")
rlang::env_binding_lock(env = asNamespace('gganimate'))
rlang::env_lock(asNamespace('gganimate'))
(from cobbling together answers https://stackoverflow.com/a/38732761/1129889 and https://stackoverflow.com/a/8502385/1129889) but it seems not to work, because it then doesn't see the gganimate
package functions:
library(ggplot2)
library(gganimate)
library(ragg)
library(gifski)
dplyr::tibble(
x = seq(0,.5,length.out=256),
y = 1-pnorm(qnorm(x)/5)
) -> df
res = 300
w = 7
h = 4
# If this is run in knitr, w and h are in inches, otherwise
# they are in pixels
anim_w = ifelse(interactive(),w*res,w)
anim_h = ifelse(interactive(),h*res,h)
ggplot(df,aes(x=x,y=y)) +
geom_line() +
geom_point() +
coord_cartesian(
xlim=c(0,.5),ylim=c(.5,1),expand = FALSE
) -> gg
anim = gg + transition_reveal(x, range = c(.5,0))
## Try to hack the function in place
bdy = deparse(body(gganimate:::ggplot_build.gganim))
c(
bdy[1:41],
"data <- lapply(data, function(el){
el[order(el$x, decreasing = TRUE),]
})",
bdy[42:length(bdy)]
) -> bdy
f <- function(plot){}
body(f) <- as.expression(parse(text=paste0(bdy, collapse="\n")))
rlang::env_unlock(env = asNamespace('gganimate'))
rlang::env_binding_unlock(env = asNamespace('gganimate'))
assignInNamespace("ggplot_build.gganim", f, "gganimate")
rlang::env_binding_lock(env = asNamespace('gganimate'))
rlang::env_lock(asNamespace('gganimate'))
anim
produces the error:
Error in plot_clone(plot) : could not find function "plot_clone"
(I suppose because plot_clone
is an internal function to gganimate
)
Is there any way to do something like this (or to accomplish the same thing in another way)?
Note: I do not want to fork the package and insert this line then install my version of it (because I only need this temporary fix for one graph, and it will break other calls to the function).
Use trace
to insert the code, run plot and untrace
it. trace
is based on statements rather than source lines so we used 31 rather than 76.
The print
statement can be removed after you are sure it is inserting the code at the correct point. (Note that in the print
statement the second argument must be FALSE or else it will display the original source rather than the traced source.)
e <- quote(data <- lapply(data, function(el){
el[order(el$x, decreasing = TRUE),]
})
)
trace(gganimate:::ggplot_build.gganim, e, at = 31)
print(gganimate:::ggplot_build.gganim, FALSE) # check insertion point
# ... run plot ...
untrace(gganimate:::ggplot_build.gganim)