Open arnejohannesholmin opened 7 months ago
This raises a side question: instead of basing the new package on base graphics, basing it on grid
(as ggplot2
does) may solve that problem, and others.
Out of curiosity, what would be the benefits of that? I imagine it would involve a complete re-write of the current package, so it would be nice to get a sense of the tangible gains before considering.
Out of curiosity, what would be the benefits of that? I imagine it would involve a complete re-write of the current package, so it would be nice to get a sense of the tangible gains before considering.
Without going into detail, we (https://github.com/StoXProject) have one framework package responsible for producing output files based on results from other packages. We currently rely on ggplot objects but would like to explore lightweight alternatives.
To add to what's already been said: Unlike the grid
-based plotting packages (ggplot2
, lattice
, etc.), the tinyplot
package relies on the original graphics
system. Unfortunately, graphics
draws directly on the graphics device and does not yield a (first-class) R object for re-use/saving later.
(To follow up on Vincent's point, tinyplot
has already hitched itself to graphics
—this was a deliberate decision right at the start of the project, despite the tradeoffs—and I'm afraid we won't be switching to grid
now, or any time in the future.)
Nonetheless, here are a few additional points that may be be useful:
graphics
doesn't provide the same object-orientated approach of grid
, you can still recall/redraw earlier plots using recordPlot()
and replayPlot()
(see: docs). Quick example:library(tinyplot)
plt(Sepal.Length ~ Petal.Length | Species, iris)
p = recordPlot()
dev.off() ## optional: remove plot to show that we're really redrawing it below
replayPlot(p)
tinyplot(..., add = TRUE)
. So if it's just a matter of adding layers to an existing plot, then that should be relatively easy to do. (Same for adding "vanilla" graphics
elements like points()
, lines()
, etc.)ggplot2
, then definitely give lattice
a look. In a sense, it's even more lightweight than tinyplot
since it comes bundled with the "Recommended" group of packages in the normal base R installation. I personally think that lattice
is underrated; it's an incredibly versatile and powerful plotting package. (The downside is that it is very function heavy and you need to learn a lot of different calls for different plot types.):100:
One further point: An additional handy aspect of grid
is that you can place a grid
viewport in all sorts of positions within a graphics device (e.g., for facets but also as panels within a tree etc.). But using gridBase
you can also draw base graphics plots within such a grid
display.
I was going to open a very similar issue. In my case, I would find it useful because it would make working with {targets}
pipelines easier. But I didn’t know about recordPlot()
, so maybe this is not an issue. I’ll have to try and see how/what works.
@b-rodrigues Doesn't targets
require that you explicitly make your plot exporting/saving step a separate function? (At least, I remember this being a minor annoyance for me when I tried it with ggplot2
a while back.) In that case, couldn't you just do something like:
save_mtcars_plot = function(filename, width, height) {
png(filename, width = width, height = height)
plt(mpg ~ wt | am, mtcars)
dev.off()
}
?
But your question does prompt to wonder if we should perhaps add a file-writing argument to tinyplot()
/plt()
, which would make the appropriate calls to, say, png()
and dev,off()
internally. Let me raise it as a separate issue.
You can define a target that simply saves the ggplot into a variable, say x
, and then do tar_read(x)
to read the plot, so no need to save it to pdf for example
@grantmcdermott Thanks for the useful info about the recordPlot() and replayPlot(), which I did not know about, and the return object from lattice functions. Surely, many of our users are used to the style of ggplot2, so using lattice would either require some persuasion or some work to make plots look like ggplot2. I will stay tuned on the developments of tinyplot and explore whether recordPlot() can be used for our needs.
I make frequent use of ggplot2
objects being created inside a function that creates other output, then returns the mixed output as a big list()
. It helps to defer graphics rendering until later in a Quarto
report, for example.
Thinking about this some more, I wonder whether we couldn't just call recordPlot()
internally, right at the end of the tinyplot()
function? Some quick testing suggests that this adds minimal overhead i.t.o. of execution time. But I might be missing some side-effects.
Mock-up example on a fairly large dataset.
library(tinyplot)
myplot = function(...) {
tinyplot(...)
grDevices::recordPlot()
}
data("diamonds", package = "ggplot2")
dp = myplot(
price ~ depth | carat, diamonds,
palette = "agSunset",
pch = 16
)
We still can't get away from displaying the initial plot on the user's device (unless we did something clever with a dummy device file location at the same time...) But the "saved" plot is easily re-callable.
dev.off()
#> null device
#> 1
dp
I am looking for a tiny alternative to ggplot2 that allows saving a plot object that can be modified (like using the "+" operator of ggplot2) and plotted (like using print() on a ggplot object) later.