Plotting with unigd

This guide walks through the basic features of unigd and compares them with the plot rendering methods in base R.

Plot rendering in base R

Rendering a plot in base R is done by (1) starting a graphics device, (2) calling some plot functions and (3) closing the device:

temp <- airquality$Temp

png(file="my_plot.png", width=600, height=400) # (1) Start the 'png' device
hist(temp, col="darkblue")                     # (2) Plot a histogram
dev.off()                                      # (3) Close the device

This has some unfortunate constraints:

unigd solves these issues with a different graphics device architecture.

Plot rendering with unigd

The same render using unigd:

library(unigd)
temp <- airquality$Temp

ugd()                                                # (1) Start the 'ugd' device
hist(temp, col="darkblue")                           # (2) Plot a histogram
ugd_save(file="my_plot.png", width=600, height=400)  # Render 600x400 PNG
dev.off()                                            # (3) Close the device

Rendering is an explicit step after plotting. This means you can render the same plot to multiple formats and dimensions:

# ...
hist(temp, col="darkblue")
ugd_save(file="my_plot.png", width=600, height=400) # 600x400 PNG
ugd_save(file="my_plot.pdf", width=300, height=300) # 300x300 PDF
# ...

Starting and closing a device can be cumbersome, especially if the plotting code errors and leaves the device open. For this reason unigd provides ugd_*_inline functions that handle device lifecycle automatically:

library(unigd)
temp <- airquality$Temp

ugd_save_inline({
  hist(temp, col="darkblue")
}, file="my_plot.png", width=600, height=400)

You can get the full list of included renderers with ugd_renderers().

In-memory render access

For applications like report generation, web services, or interactive tools, you may want to access the rendered data directly instead of writing to a file. Use ugd_render() instead of ugd_save():

temp <- airquality$Temp

ugd()
hist(temp, col="darkblue")
my_svg <- ugd_render(as="svg")
dev.off()

cat(my_svg)

There is also an inline variant:

temp <- airquality$Temp

my_svg <- ugd_render_inline({
  hist(temp, col="darkblue")
}, as="svg")

cat(my_svg)

More features

Zoom

All rendering functions offer a zoom parameter that scales the size of objects inside a plot independently of the plot dimensions. For example, zoom=2 doubles all objects to 200%, zoom=0.5 halves them to 50%.

my_svg_1_0 <- ugd_render_inline({
  hist(temp, col="darkblue", main = "Zoom 1.0")
}, as="png-base64", width=300, height=300, zoom=1.0)

my_svg_1_5 <- ugd_render_inline({
  hist(temp, col="darkblue", main = "Zoom 1.5")
}, as="png-base64", width=300, height=300, zoom=1.5)

my_svg_0_5 <- ugd_render_inline({
  hist(temp, col="darkblue", main = "Zoom 0.5")
}, as="png-base64", width=300, height=300, zoom=0.5)

alts <- c("Histogram at zoom 1.0", "Histogram at zoom 1.5", "Histogram at zoom 0.5")
knitr::raw_html(paste0(sprintf("<img src=\"%s\" alt=\"%s\" />",
  c(my_svg_1_0, my_svg_1_5, my_svg_0_5), alts)))
Histogram at zoom 1.0 Histogram at zoom 1.5 Histogram at zoom 0.5

Paging (by index)

The page parameter selects which plot from the history to render. By default it is 0, which uses the most recently created plot. Values ≥ 1 select by index (oldest first), values ≤ 0 select newest-first:

ugd()
for (i in 1:10) {
  plot(1, main=paste0("Plot #", i))
}

ugd_save(file="plot.png", page = 3)  # Plot #3
ugd_save(file="plot.png")            # Plot #10 (latest)
ugd_save(file="plot.png", page = -1) # Plot #9

dev.off()

Plots can also be removed from history:

ugd_remove()          # Remove last
ugd_remove(page = -1) # Remove second-to-last
ugd_clear()           # Remove all

Plot IDs

Plot indices shift when plots are added or removed. If you need to refer to a specific plot later, grab its ID with ugd_id():

ugd()

plot(rnorm(50))           # A
id_a <- ugd_id()          # Get last ID (A)

hist(rnorm(50))           # B
plot(sin((1:100)/3))      # C
id_b <- ugd_id(-1)        # Second-to-last (B)

hist(runif(100))          # D
ugd_remove(3)             # Remove C

ugd_save(file="plot_a.png", page = id_a)
ugd_save(file="plot_b.png", page = id_b)

dev.off()

In practice this is usually simpler: just call ugd_id() after each plot to capture its ID.

Special renderers

unigd ships with a few special renderers beyond image formats:

Performance considerations

For most applications, readability should be prioritized over performance. Unless graphics rendering is bottlenecking your R script, you can safely skip this section.

The key thing to understand is when unigd needs to ask the R graphics engine to redraw a plot. Rendering happens after drawing, and the last drawn dimensions are cached. Two rules follow from this:

Grouping renders by dimension avoids unnecessary redraws:

# Two redraws (dimensions keep changing):
ugd_save(file="my_plot.png", width=600, height=400)
ugd_save(file="my_plot.pdf", width=300, height=300) # redraw
ugd_save(file="my_plot.svg",  width=600, height=400) # redraw

# One redraw (same dimensions grouped together):
ugd_save(file="my_plot.png", width=600, height=400)
ugd_save(file="my_plot.svg",  width=600, height=400)
ugd_save(file="my_plot.pdf", width=300, height=300) # redraw

You can also avoid the initial redraw by setting the target dimensions at device creation time:

ugd(width=300, height=300)
# ...
ugd_save(file="my_plot.png", width=300, height=300) # no redraw needed

If you omit the dimensions when calling rendering functions, the last known dimensions are used and no redraw occurs:

ugd_save(file="my_plot.png")

All ugd_*_inline functions also avoid unnecessary redraws.

Note that zoom interacts with dimensions: the cached width is width / zoom.