I have a curious issue saving a plot generated inside a function when using the mirt package for R. Here's a reprex:
#save image from base plot
save_as_file = function(code, filename, width = 1000, height = 750) {
png(filename, width = width, height = height)
eval(substitute(code))
dev.off()
try(dev.off(), silent = T)
}
#test it
plot(1:3)
save_as_file(plot(1:3), "test.png")
#> null device
#> 1
file.remove("test.png")
#> [1] TRUE
which makes this plot:
However, when I try to use this with mirt, nothing is produced:
#mirt test
library(mirt)
#> Loading required package: stats4
#> Loading required package: lattice
set.seed(1)
mirt_data = mirt::simdata(
a = c(1:4),
d = c(0,0,1,1),
N = 1000,
itemtype = "2PL"
)
mirt_fit = mirt(
data = mirt_data,
model = 1,
itemtype = "2PL",
verbose = F
)
plot(mirt_fit, type = "trace") #works
save_as_file(plot(mirt_fit, type = "trace"), "mirt.png") #nothing happens
#> null device
#> 1
Producing this plot, but no file is saved. Why?
As you already found out, you mysteriously have to use print
. The reason is that plot(mirt_fit,...)
dispatches to mirt:::plot_mixture(mirt_fit, ...)
which uses lattice
.
lattice
functions behave differently from standard R graphics functions in that they do not immediately create a plot when executed. Instead, like conventional functions, they create an object of class "trellis"
, and the plot is created using the print method print.trellis
(ggplot has probably cribbed that). Therefore, it may be necessary to explicitly print
(dispatches to lattice:::print.trellis
in this case) the object to display the plot.
There is a slight problem with your proposed solution; if there's an error during plotting, it won't close the png
device and will clutter your device list over time.
graphics.off()
dev.list()
# NULL
save_as_file(plot(stop(), type="trace"), "mirt.png")
# Error ...
save_as_file(plot(stop(), type="trace"), "mirt.png")
# Error ...
dev.list()
# png png
# 2 3
graphics.off()
So it's better to use dev.off()
in on.exit()
. Also we can use case handling, to avoid base R plot
s to be print
ed.
save_as_file <- function(code, filename, width=1000, height=750) {
on.exit(dev.off())
png(filename, width=width, height=height)
p <- eval(substitute(code))
if (!is.null(p)) print(p)
}
save_as_file(mirt:::plot_mixture(stop(), type="trace"), "mirt.png")
dev.list() ## no device open
# NULL
save_as_file(plot(mirt_fit, type="trace"), "mirt.png")
save_as_file(plot(1:3), "mirt.png")