diff --git a/NEWS.md b/NEWS.md index 2da16260..7a83e79e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -55,11 +55,20 @@ where the formatting is also better._ - Better integration with the Positron IDE graphics pane. Thanks to @thomasp85 for the report and helpful suggestions. (#377, #394 @grantmcdermott) - The one remaining Positron issue at present is calling `plt_add()` on a - faceted plot, but this appears to be an upstream limitation/bug (see - https://github.com/posit-dev/positron/issues/7316) + faceted plot, but this appears to be an upstream limitation/bug + [positron#7316](https://github.com/posit-dev/positron/issues/7316). - Fixed a bug that resulted in y-axis labels being coerced to numeric for - `"p"`-alike plot types (including `"jitter"`) if `y` is a factor or character - (#387 @grantmcdermott). + `"p"`-alike plot types (including `"jitter"`) if `y` is a factor or character. + (#387 @grantmcdermott) + +### Website: + +- Improved column spacing of Arguments in the References section of the website. + (#328 thanks to @etiennebacher's upstream `altdoc` fix) +- Added a new "Ticks & tips" vignette for non-standard workarounds. + (#381 @vincentarelbundock) +- Improved website theme and navigation layout, especially on mobile. + (#395 @zeileis) ### Internals: @@ -69,12 +78,6 @@ where the formatting is also better._ logic was mostly an artifact of development inertia and this new nesting logic should simplify the creation of certain plot types. (#331 @grantmcdermott) - -### Misc: - -- Improved column spacing of Arguments in the References section of the website. - (#328 thanks to @etiennebacher's upstream `altdoc` fix) - ## 0.3.0 ### New features diff --git a/R/draw_legend.R b/R/draw_legend.R index 861850e4..ce960e6b 100644 --- a/R/draw_legend.R +++ b/R/draw_legend.R @@ -40,7 +40,6 @@ #' @importFrom utils modifyList #' #' @examples -#' #' oldmar = par("mar") #' #' draw_legend( diff --git a/R/tinyplot_add.R b/R/tinyplot_add.R index 847d16e3..fc9bea60 100644 --- a/R/tinyplot_add.R +++ b/R/tinyplot_add.R @@ -22,8 +22,6 @@ #' call. #' #' @examples -#' library(tinyplot) -#' #' tinyplot(Sepal.Width ~ Sepal.Length | Species, #' facet = ~Species, #' data = iris) diff --git a/R/tinytheme.R b/R/tinytheme.R index e118d10a..03579afd 100644 --- a/R/tinytheme.R +++ b/R/tinytheme.R @@ -54,7 +54,6 @@ #' @seealso [`tpar`] which does the heavy lifting under the hood. #' #' @examples -#' #' # Reusable plot function #' p = function() tinyplot( #' lat ~ long | depth, data = quakes, diff --git a/R/type_text.R b/R/type_text.R index 1b5d3b60..21f65d70 100644 --- a/R/type_text.R +++ b/R/type_text.R @@ -7,7 +7,6 @@ #' @param font Font to be used, following [graphics::par()] #' @inheritParams graphics::text #' @examples -#' #' tinyplot(mpg ~ hp | factor(cyl), #' data = mtcars, #' type = type_text( diff --git a/altdoc/quarto_website.yml b/altdoc/quarto_website.yml index cd0a28a3..e64b874b 100644 --- a/altdoc/quarto_website.yml +++ b/altdoc/quarto_website.yml @@ -12,14 +12,14 @@ website: left: - text: Introduction file: vignettes/introduction.qmd - - text: Plot types + - text: Types file: vignettes/types.qmd - text: Themes file: vignettes/themes.qmd - text: Gallery file: vignettes/gallery.qmd - - text: Tips & tricks - file: tips_and_tricks.qmd + - text: Tips + file: vignettes/tips.qmd - text: Reference file: man/tinyplot.qmd - text: News diff --git a/man/draw_legend.Rd b/man/draw_legend.Rd index f4a4d64a..be516c50 100644 --- a/man/draw_legend.Rd +++ b/man/draw_legend.Rd @@ -76,7 +76,6 @@ Function used to calculate the placement of (including outside the plotting area) and drawing of legend. } \examples{ - oldmar = par("mar") draw_legend( diff --git a/man/tinyplot_add.Rd b/man/tinyplot_add.Rd index 916e7159..8a0f457b 100644 --- a/man/tinyplot_add.Rd +++ b/man/tinyplot_add.Rd @@ -39,8 +39,6 @@ arguments (\code{x}, \code{y}, etc.) are attached to your global R environment.) } \examples{ -library(tinyplot) - tinyplot(Sepal.Width ~ Sepal.Length | Species, facet = ~Species, data = iris) diff --git a/man/tinytheme.Rd b/man/tinytheme.Rd index a82b9f63..2411c946 100644 --- a/man/tinytheme.Rd +++ b/man/tinytheme.Rd @@ -66,7 +66,6 @@ or main titles that contain "\\n"). } } \examples{ - # Reusable plot function p = function() tinyplot( lat ~ long | depth, data = quakes, diff --git a/man/type_text.Rd b/man/type_text.Rd index 7dc41d25..44dde707 100644 --- a/man/type_text.Rd +++ b/man/type_text.Rd @@ -45,7 +45,6 @@ Type function for adding text annotations to a plot. This function allows you to draw text at specified (x,y) coordinates. } \examples{ - tinyplot(mpg ~ hp | factor(cyl), data = mtcars, type = type_text( diff --git a/vignettes/introduction.qmd b/vignettes/introduction.qmd index b8bfcda3..cf346b3c 100644 --- a/vignettes/introduction.qmd +++ b/vignettes/introduction.qmd @@ -20,11 +20,11 @@ knitr::opts_chunk$set( ) ``` -The goal of this intro tutorial is to give you a sense of the main features and -syntax of **tinyplot**, a lightweight extension of the base R graphics system. -We don't try to cover everything, but you should come away with a good -understanding of how the package works and how it can integrate with your own -projects. +**tinyplot** is a lightweight extension of the base R graphics system, designed +to simplify and enhance data visualization. This tutorial provides a gentle +introduction to the package's core features and syntax. We won’t cover +everything, but you should come away with a solid understanding of how +**tinyplot** works and how it can integrate with your own projects. We start this tutorial by loading the package and a slightly modified version of the `airquality` dataset that comes bundled with base R. @@ -54,7 +54,7 @@ par(mfrow = c(1, 1)) # reset layout ``` Similarly, we can plot elements from a data frame using either the atomic or -formula methods. Here's a simple example using the `aq` dataset that we created +formula methods. Here is a simple example using the `aq` dataset that we created earlier. ```{r tinyplot_simple} @@ -178,7 +178,7 @@ as convenience strings.^[See the accompanying help pages of those two functions for more details on the available palettes, or read Zeileis & Murrell (2023, The R Journal, [doi:10.32614/RJ-2023-071](https://doi.org/10.32614/RJ-2023-071)).] Note that case-insensitive, partial matching for these convenience -string is allowed. For example: +strings is allowed. For example: ```{r palette_tableau} tinyplot( @@ -194,7 +194,7 @@ example, if you have installed the **ggsci** package ([link](https://nanx.me/ggsci/index.html)) then you could use `palette = ggsci::pal_npg()` to generate a palette consistent with those used by the Nature Publishing Group.] You can also use the `alpha` argument to adjust -the (alpha) transparency of your colours: +the (alpha) transparency of your colours. ```{r} tinyplot( @@ -220,11 +220,11 @@ palette("tableau") In all of the preceding plots, you will have noticed that we get an automatic legend. The legend position and look can be customized with the `legend` argument. At a minimum, you can pass the familiar legend position keywords as a -convenience string ("topright", "bottom", "left", etc.). Moreover, a key feature +convenience string (`"topright"`, `"bottom"`, etc.). Moreover, a key feature of `tinyplot` is that we can easily and elegantly place the legend _outside_ the plot area by adding a trailing "!" to these keywords. (As you may have realised, -the default legend position is "right!".) Let's demonstrate by moving the legend -to the left of the plot: +the default legend position is `"right!"`.) Let's demonstrate by moving the +legend to the left of the plot. ```{r legend_bottom} tinyplot( @@ -235,12 +235,12 @@ tinyplot( ``` Beyond the convenience of these positional keywords, the `legend` argument also -permits additional customization by passing an appropriate function (or, a list -of arguments that will be passed on to the standard `legend()` function -internally.) So you can change or turn off the legend title, remove the bounding -box, switch the direction of the legend text to horizontal, etc. Here's a -grouped density plot example, where we also add some shading by specifying that -the background colour should vary by groups too. +permits additional customization in the form of a list of arguments, which will +be passed on to the standard `legend()` function internally. So you can change +or turn off the legend title, remove the bounding box, switch the direction of +the legend text to horizontal, etc. Here is a grouped density plot example, +where we also add some shading by specifying that the background colour should +vary by groups too. ```{r density_topright} tinyplot( @@ -263,10 +263,8 @@ tinyplot(Temp ~ Wind | Ozone, data = aq, pch = 19) Gradient legends (and plots) can be customized in an identical manner to discrete legends by adjusting the keyword positioning, palette choice, alpha -transparency etc. Here is a quick adaptation of the previous plot to -demonstrate. Note that here we pass a special convenience argument to -`bg`/`fill`; if it detects a numeric in the range of `[0,1]`, then it -automatically inherits the grouped colour mappings but with added transparency. +transparency, etc. Here is a quick adaptation of the previous plot to +demonstrate. ```{r legend_gradient2} tinyplot( @@ -278,20 +276,23 @@ tinyplot( ) ``` +As an aside, note that we passed a numeric convenience argument to `fill` +(alias `bg`) above. Specifically, when `fill` is given as a numeric in the range +of `[0,1]` then it automatically inherits the grouped colour mappings, but with +corresponding alpha transparency. + ## More plot types -We have already seen several plot types above such as `"p"` (points), `"l"` -(lines), and `"density"`. In general, **tinyplot** supports all of the primitive -plot types/elements available in base R, as well as a number of additional plot -types that can be a bit tedious to code up manually. You can see the full list -of supported plot types by checking the -[`?tinyplot`](https://grantmcdermott.com/tinyplot/man/tinyplot.html#arguments) -documentation, or by taking a look at the dedicated [Plot Types](types.qmd) -vignette on this website. +We’ve already seen several plot types, such as `"p"` (points), `"l"` (lines), +and `"density"`. In general, **tinyplot** supports all the primitive plot types +and elements available in base R, along with a number of additional types that +can be tedious to code manually. You can find the full list of in the dedicated +[Plot Types](types.qmd) vignette. For the moment, we’ll content ourselves with +a few illustrative examples. -For example, **tinyplot** supports interval plots via the `"pointrange"`, -`"errorbar"`, `"ribbon"`, and related type arguments. A canonical use-case is -coefficient plots: +One such example is the family of "interval" plots provided by the +`"pointrange"`,`"errorbar"`, `"ribbon"`, and related types. A canonical use-case +is coefficient plots. ```{r pointrange, warning = FALSE} mod = lm(Temp ~ 0 + Month / Day, data = aq) @@ -319,10 +320,10 @@ with( ) ``` -**tinyplot** also supports special types to fit models and display their -predictions, along with confidence intervals. Here is a somewhat silly example -where we fit a linear model to predict temperature by day of month.^[The grouped -setting here makes this visualization equivalent to +Note that **tinyplot** also supports special types to fit models and display +their predictions, along with confidence intervals. Here is a somewhat silly +example where we fit a linear model to predict temperature by day of month.^[The +grouped setting here makes this visualization equivalent to `predict(lm(Temp ~ 0 + Month / Day, data = aq), interval = "confidence")`.] ```{r ribbon_pred} @@ -337,16 +338,17 @@ tinyplot( The default behaviour of these model types can be adjusted by passing appropriate arguments to the equivalent _functional_ version of the type in question. These functional types all follow a `type_` syntax, so that -`"lm"` is paired with `type_lm()`, etc. Below we illustrate an adapted -generalised linear model, where we passing the binomial -[family](https://search.r-project.org/R/refmans/stats/html/family.html) argument -and thus fit a logistic regression. +`"lm"` is paired with `type_lm()`, etc. Below we illustrate with an adapted +generalised linear model, where we fit a logistic regression passing explicitly +passing the binomial +[family](https://search.r-project.org/R/refmans/stats/html/family.html) +argument. ```{r ribbon_pred_glm} tinyplot( - I(Temp > 80) ~ Wind, aq, + I(Temp > 75) ~ Wind, aq, type = type_glm(family = binomial), - main = "Logit model: Temps above 80 °F" + main = "Logit model: Temps above 75 °F" ) ``` @@ -416,17 +418,17 @@ function, or as part of a dedicated `tinytheme()`. We will revisit this idea in the [Themes](#themes) section below. Finally, the `facet` argument also accepts a _two-sided_ formula for arranging -facets in a fixed grid layout. Here's a simple (if contrived) example. +facets in a fixed grid layout. Here is a simple (if contrived) example. ```{r facet_grid} tinyplot( - Temp ~ Day, data = aq, - facet = windy ~ hot, - # the rest of these arguments are optional... - facet.args = list(col = "white", bg = "black"), - pch = 16, col = "dodgerblue", - grid = TRUE, frame = FALSE, ylim = c(50, 100), - main = "Daily temperatures vs. wind" + Temp ~ Day, data = aq, + facet = windy ~ hot, + # the rest of these arguments are optional... + facet.args = list(col = "white", bg = "black"), + pch = 16, col = "dodgerblue", + grid = TRUE, frame = FALSE, ylim = c(50, 100), + main = "Temps versus wind" ) ``` @@ -442,11 +444,10 @@ rather than opening a new window. However, while this argument is useful, it can become verbose since it requires users to make very similar successive calls, with many shared arguments. -For this reason, the **tinyplot** package provides a special -`tinyplot_add()` convenience function for adding layers to -an existing tinyplot. The idea is that users need simply pass the _specific_ -arguments that they want to add or modify relative to the base layer, and all -arguments will be inherited from the original. +For this reason, **tinyplot** provides a special `tinyplot_add()` convenience +function for adding layers to an existing tinyplot. The idea is that users need +simply pass the _specific_ arguments that they want to add or modify relative to +the base layer; all others arguments will be inherited from the original call. An example may help to demonstrate. Here we first draw some faceted points with group colouring and various other aesthetic tweaks. Next, we add regression @@ -510,7 +511,7 @@ drawing/annotating function. You can combine multiple drawing functions by wrapping them with curly brackets`{}`, and even pass `tinyplot()` back towards itself. -Here's a slightly more complicated "spaghetti" plot example, where we pass +Here is a slightly more complicated "spaghetti" plot example, where we pass multiple functions through `draw = {...}`, including drawing all of the lines in the background (of each facet) via a secondary `tinyplot()` call. @@ -587,12 +588,15 @@ graphics parameters via `tpar()` and then using `file` to save a plot. ## Conclusion -In summary, consider the **tinyplot** package if you are looking for base R `plot` -functionality with added convenience features. You can use (nearly) the exact -same syntax and all of your theme elements should carry over too. It has no -dependencies other than base R itself and this should make it an attractive -option for package developers, as well as situations where dependency management -is expensive (e.g., production pipelines, continuous integration or an R -application running in a browser via -[WebAssembly](https://docs.r-wasm.org/webr/latest/)). - +The goal of this tutorial has been to give you a clear sense of how **tinyplot** +works and what it offers. The take-home pitch is simple: you get to use the same +syntax as base R `plot()`, but with the added benefit of _many_ additional plot +types and user-friendly features. Finally, it's worth noting that **tinyplot** +has no dependencies other than base R itself. We hope that this makes it an +attractive and lightweight option for package developers (and regular R users!) +who want to create convenient, sophisticated plots with minimal overhead. + +Believe it or not, there's still plenty of **tinyplot** functionality that we +didn't cover here. If you’d like to keep exploring, we recommend continuing with +the [Plot types](types.qmd) and [Themes](themes.qmd) vignettes. Happy +tinyplotting! diff --git a/vignettes/themes.qmd b/vignettes/themes.qmd index f9df4bda..0abe7bfa 100644 --- a/vignettes/themes.qmd +++ b/vignettes/themes.qmd @@ -23,7 +23,7 @@ knitr::opts_chunk$set( The base R aesthetic tends to divide option. Some people like the default minimalist look of base R plots and/or are happy to customize the (many) graphical parameters that are available to them. Others find base plots ugly -and don't want to spend time endlessly tweaking different parameters. Similarly, +and don't want to spend time endlessly tweaking different parameters. Moreover, the inherent "canvas" approach to drawing base R graphics, with fixed placement for plot elements, means that plots don't adjust dynamically and this can lead to awkward whitespace artifacts unless the user explicitly accounts for them. @@ -63,6 +63,7 @@ tinyplot( I(Sepal.Width*1e5) ~ Sepal.Length | Species, facet = "by", data = iris, + yaxl = ",", # use comma format for the y-axis labels main = "Title of the plot", sub = "The left-margin adjusts to accomodate the long y-axis labels" ) diff --git a/vignettes/tips.qmd b/vignettes/tips.qmd new file mode 100644 index 00000000..d1989e3d --- /dev/null +++ b/vignettes/tips.qmd @@ -0,0 +1,72 @@ +--- +title: "Tips & Tricks" +format: + html: + css: custom.css +tbl-colwidths: [20,20,50,10] +--- + +This page collects miscellaneous tips and tricks for **tinyplot**. By +definition, these are workarounds---i.e., techniques that fall outside of +standard use cases or features that aren't (yet) natively supported by the +package. Please feel free to suggest or add more tips via our [GitHub repo](https://github.com/grantmcdermott/tinyplot/issues). + +## Legend + +### Opacity (`alpha`) + +By default, the legend inherits the opacity of the plotted elements. In some +cases, this may be undesirable. For example, in the following plot, the legend +is too light: + +```{r} +library(tinyplot) +x = rnorm(1e6) +y = x + rnorm(1e6) +z = sample(c("a", "b"), 1e6, replace = TRUE) +dat = data.frame(x, y, z) +tinyplot(y ~ x | z, data = dat, alpha = .1, pch = 19) +``` + +One solution is to draw our plot in two steps. First, we draw an empty plot with +the desired (zero transparency) legend. Second, we add the transparent points on +top of the existing canvas. + +```{r} +tinyplot(y ~ x | z, data = dat, pch = 19, empty = TRUE) +tinyplot_add(empty = FALSE, alpha = .1) +``` + +## Labels + +### Axis label rotation + +When category labels are long or overlapping, users may want to rotate them for +readability. One option is fully perpendicular (90°) axis labels, which +`tinyplot` supports via themes, e.g. `tinytheme("clean", las = 2)`. If a user +wants finer control over the degree of rotation---say, 45°---this requires a bit +more manual effort since we do not support custom rotation out of the box. The +workaround involves three steps: + +1. Suppress the default x-axis with `xaxt = "n"`. +2. Use `text()` to manually add rotated labels. +3. Optionally, clear the y-axis label by setting `ylab = ""`. + +```{r} +library(tinyplot) +tinyplot(~cyl, data = mtcars, type = "barplot", xaxt = "n", ylab = "") +text(1:3, 0, + labels = c("Four cylinders", "Six cylinders", "Eight cylinders"), + srt = 45, # rotate text 45 degrees + adj = c(1.1, 1.5), # adjust text alignment + xpd = TRUE) # allow drawing outside plot region +``` + +Note that `adj` and `xpd` settings may require trial and error to position +labels correctly. Also, removing the x-axis label by setting `ylab = ""` is +unintuitive but currently necessary when using formulas like `~ cyl`. + +P.S. Another option for long axis labels is to wrap them at a designated +character length. See the final example in the +[`tinylabel`](https://grantmcdermott.com/tinyplot/man/tinylabel.html) +documentation for an example. diff --git a/vignettes/tips_and_tricks.qmd b/vignettes/tips_and_tricks.qmd deleted file mode 100644 index 5ea64ab2..00000000 --- a/vignettes/tips_and_tricks.qmd +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: "Tips & Tricks " -format: - html: - css: custom.css -tbl-colwidths: [20,20,50,10] ---- - -This page collects miscellaneous tips and tricks for **tinyplot**. -Please feel free to suggest or add more tips via our [GitHub repo](https://github.com/grantmcdermott/tinyplot/issues). - -# Legend - -## Opacity (`alpha`) - -By default, the legend inherits the opacity of the plotted elements. In some cases, this may be undesirable. For example, in the following plot, the legend is too light: - -```{r} -library(tinyplot) -x = rnorm(1e6) -y = x + rnorm(1e6) -z = sample(c("a", "b"), 1e6, replace = TRUE) -dat = data.frame(x, y, z) -tinyplot(y ~ x | z, data = dat, alpha = .1, pch = 19) -``` - -One solution is to draw our plot in two steps. First, we draw an empty plot with the desired (zero transparency) legend. Second, we add the transparent points on top of the existing canvas. - -```{r} -tinyplot(y ~ x | z, data = dat, pch = 19, empty = TRUE) -tinyplot_add(empty = FALSE, alpha = .1) -``` - -# Labels - -## Axis label rotation - -When category labels are long or overlapping, users may want to rotate them for readability. One option is fully perpendicular (90°) axis labels, which `tinyplot` supports via themes, e.g. `tinytheme("clean", las = 2)`. If a user wants finer control over the degree of rotation---say, 45°---this requires a bit more manual effort since we do not support custom rotation out of the box. The workaround involves three steps: - -1. Suppress the default x-axis with `xaxt = "n"`. -2. Use `text()` to manually add rotated labels. -3. Optionally, clear the y-axis label by setting `ylab = ""`. - -```{r} -library(tinyplot) -tinyplot(~cyl, data = mtcars, type = "barplot", xaxt = "n", ylab = "") -text(1:3, 0, - labels = c("Four cylinders", "Six cylinders", "Eight cylinders"), - srt = 45, # rotate text 45 degrees - adj = c(1.1, 1.5), # adjust text alignment - xpd = TRUE) # allow drawing outside plot region -``` - -Note that `adj` and `xpd` settings may require trial and error to position labels correctly. Also, removing the x-axis label by setting `ylab = ""` is unintuitive but currently necessary when using formulas like `~ cyl`. - -P.S. Another option for long axis labels is to wrap them at a designated character length. See the final example in the [`tinylabel`](https://grantmcdermott.com/tinyplot/man/tinylabel.html) documentation for an example. diff --git a/vignettes/types.qmd b/vignettes/types.qmd index ef14ea68..4ce53173 100644 --- a/vignettes/types.qmd +++ b/vignettes/types.qmd @@ -6,12 +6,10 @@ format: tbl-colwidths: [20,20,50,10] --- -**tinyplot** is a lightweight extension of R's base plotting system, designed to -simplify and enhance data visualization. One of its key features is the `type` -argument, which allows you to specify different types of plots easily. This -tutorial will guide you through the various plot types available in -**tinyplot**, demonstrating how to use the `type` argument to create a wide -range of visualizations. +A key feature of **tinyplot** is the `type` argument, which allows you to +specify different kinds of plots. This tutorial will guide you through the +various plot types available in **tinyplot**, demonstrating how the `type` +argument can be used to to create a wide range of visualizations. We will consider three categories of plot types: @@ -173,7 +171,7 @@ need to define three simple functions: `data_typename()`, `draw_typename()`, and In this section, we explain the role of each of these functions and present a minimalist example of a custom type. Interested readers may refer to -[the **tinyplot** source code](https://github.com/grantmcdermott/tinyplot) +the **tinyplot** [source code](https://github.com/grantmcdermott/tinyplot) to see many more examples, since each **tinyplot** type is itself implemented as a custom type.