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 thatplot(mirt_fit,...)dispatches tomirt:::plot_mixture(mirt_fit, ...)which useslattice.latticefunctions 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 methodprint.trellis(ggplot has probably cribbed that). Therefore, it may be necessary to explicitlyprint(dispatches tolattice:::print.trellisin 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
pngdevice and will clutter your device list over time.So it's better to use
dev.off()inon.exit(). Also we can use case handling, to avoid base Rplots to beprinted.