Owner
Closes #236.
Adds a new draw argument that allows users to pass drawing functions that are simply captured and evaluated directly.
pkgload::load_all(("~/Documents/Projects/tinyplot")) #> ℹ Loading tinyplot library(marginaleffects) fit = lm(Sepal.Length ~ (Sepal.Width + Petal.Length + Petal.Width) * Species, iris) mfx = avg_slopes(fit, variables = c("Sepal.Width", "Petal.Length", "Petal.Width"), by = "Species") with( mfx, plt( x = term, y = estimate, ymin = conf.low, ymax = conf.high, type = "errorbar", pch = 19, facet = Species, facet.args = list(bg = "gray90"), frame = FALSE, draw = abline(h = 0, lty = 2, col = "hotpink") ) )
One nice feature is that you can leverage the internal facet looping counter (namely: ii) to construct programmatic drawings.
plt( mpg ~ wt, facet = ~am, data = mtcars, draw = text(5, 30, paste("Facet", ii), adj = 1, col = "red", font = 2) )
Collaborator
Do we need to expose this to users?
We could have a type_abline() that creates this function internally and passes it to the facet drawing code. That would be consistent with our current user interface.
Then, type_hline(), type_vline(), etc. are all sensible shortcuts.
Owner Author
Do we need to expose this to users?
We could have a
type_abline()that creates this function internally and passes it to the facet drawing code. That would be consistent with our current user interface.Then,
type_hline(),type_vline(), etc. are all sensible shortcuts.
Hmmm, I have a feeling you're going to warn me against argument proliferation... but I don't necessarily see this as an either-or situation. I think we could add type_abline() & co. without necessarily duplicating the role (or convenience) of draw.
For example, sometimes I just want to add text annotation to a plot and this requires a different logic to passing labels through a data frame (which I imagine is what type_text() will leverage once we support it.) Plus you can do kind of fun (dumb) things like this:
pkgload::load_all(("~/Documents/Projects/tinyplot")) #> ℹ Loading tinyplot plt( Temp ~ Day, facet = ~Month, data = airquality, draw = { abline(h = 80, lty = 2) text(5, 80, "Hot", adj = 1, col = "red", pos = 3) text(5, 80, "Cold", adj = 1, col = "blue", pos = 1) } )
Created on 2024-11-07 with reprex v2.1.1
Collaborator
Hmmm, I have a feeling you're going to warn me against argument proliferation...
lol, yeah. I have a reputation now ;-)
I will say that, in this case, I can see the motivation. What is cool about draw is that it is totally generic, so it makes sense as an argument to the top level function. And your examples suggest that this could open up applications that we haven't thought about yet, so it might be a good investment for the future.
Owner Author
What is cool about
drawis that it is totally generic, so it makes sense as an argument to the top level function. And your examples suggest that this could open up applications that we haven't thought about yet, so it might be a good investment for the future.
Yeah, exactly. I think that this could open up a bunch of cool features and flexibility. Two quick additional examples to underscore the point:
- Passing
draw = legendto create custom annotations.
tpar(pch = 19) plt( mpg ~ wt, data = mtcars, facet = ~am, legend = FALSE, #axes = "l", draw = legend("topright", legend = as.roman(ii), bg = "lightcyan", x.intersp = 0) )
- Passing
pltback to itself (very "Inception"-esque) to plot the full dataset in the background of a faceted plot. You can also do this for spagetti (line) plots to highlight individual series.
data("penguins", package = "palmerpenguins") plt( bill_length_mm ~ body_mass_g | species, data = penguins, facet = "by", legend = FALSE, axes = "l", draw = plt(bill_length_mm ~ body_mass_g , data = penguins, add = TRUE, col = "lightgray") )
I'll add a few more examples to to the docs and then we can review again.
Question: One potential downside is that the draw elements are always drawn in the background, before the main plot elements. But I can imagine times where you'd want to draw on top of the main plot elements. Should we add a companion argument to draw that does this (maybe ink)?
Collaborator
Question: One potential downside is that the
drawelements are always drawn in the background, before the main plot elements. But I can imagine times where you'd want to draw on top of the main plot elements. Should we add a companion argument todrawthat does this (maybeink)?
What happens if they use draw in our future tinyplot_add() function? To me, that would feel like the more natural way to implement this.
Owner Author
Minor update. I'm going to wait for #246 to be merged first, since I'll build on some changes/docs forthcoming that PR.