Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draw argument #245

Merged
merged 8 commits into from
Nov 12, 2024
Merged

draw argument #245

merged 8 commits into from
Nov 12, 2024

Conversation

grantmcdermott
Copy link
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)
)

@vincentarelbundock
Copy link
Collaborator

vincentarelbundock commented Nov 8, 2024

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.

@grantmcdermott
Copy link
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

@vincentarelbundock
Copy link
Collaborator

vincentarelbundock commented Nov 8, 2024

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.

@grantmcdermott
Copy link
Owner Author

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.

Yeah, exactly. I think that this could open up a bunch of cool features and flexibility. Two quick additional examples to underscore the point:

  1. Passing draw = legend to 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)
)

  1. Passing plt back 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)?

@vincentarelbundock
Copy link
Collaborator

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)?

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.

@zeileis
Copy link
Collaborator

zeileis commented Nov 8, 2024

The penguins example is cool, very nice!

@grantmcdermott
Copy link
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.

@grantmcdermott grantmcdermott merged commit 6df7187 into main Nov 12, 2024
3 checks passed
@grantmcdermott grantmcdermott deleted the draw branch November 12, 2024 06:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Adding ablines to faceted plots
3 participants