Michael Jahn, 2023-09-05
Panel functions and wrappers that extend the R lattice universe
This package contains panel functions and themes for the R lattice
package, a general purpose plotting package from Deepayan Sarkar.
Lattice’s functionality is comparable to the popular ggplot2
package
but has a slightly different look and feel. The functions in this
package adhere as far as possible to the lattice
conventions but might
in some cases deviate from default behavior. However, some care was
taken to replicate the original lattice behavior so that users can apply
grouping and paneling as they are used to. Feel free to copy, fork or
source functions that you find useful. Contributions welcome!
To install the package directly from github, use this function from the
devtools
package in your R session:
require(devtools)
devtools::install_github("https://github.com/m-jahn/lattice-tools")
These functions extend or simplify the panel.function
landscape for
lattice
.
Draw summary text labels in lattice plots. This panel function allows to
draw text labels such as the mean on arbitrary groups of data points.
Text labels will be drawn for groups of identical x values with optional
subsetting by grouping or paneling variables. This function complements
panel.errbars()
and panel.barplot
as it supports drawing labels
beside each other for different groups.
library(lattice)
library(latticetools)
data(mtcars)
# annotate mean values
xyplot(mpg ~ factor(cyl) | factor(vs), mtcars,
lwd = 2, pch = 19, offset = 2, digits = 1,
panel = function(x, y, ...) {
panel.errbars(x, y, ...)
panel.annotate(x, y, ...)
}
)
# works also with grouping variable. Takes the same arguments
# "beside" and "ewidth" as panel.errbars() and panel.barplot()
# to plot labels for different groups aside of each other
xyplot(mpg ~ factor(cyl) | factor(vs), mtcars,
lwd = 2, pch = 19, groups = gear, digits = 1,
offset = 3, beside = TRUE,
panel = function(x, y, ...) {
panel.errbars(x, y, ...)
panel.annotate(x, y, ...)
}
)
Panel function to draw boxes with arrow head from 2 XY coordinates and
direction argument. Each argument can be a vector of same length so that
multiple arrows can be drawn at once. Is internally used in
panel.geneplot
.
library(lattice)
xyplot(1:3 ~ 4:6, col = "#0080ff",
panel = function(x, y, ...) {
panel.arrowbox(x0 = c(4, 5), y0 = c(2.5, 1),
x1 = c(5, 6), y1 = c(3, 1.5),
direction = c(1, -1), ...)
}
)
Draw a barplot with error bars in lattice plots. This panel function
allows to draw barplots with error bars for arbitrary groups of data
points. Error bars will be drawn for groups of identical x values with
optional subsetting by grouping or paneling variables. This function is
very similar to panel.errbars
only with bars instead of points.
# mean and stdev error bars are drawn for
# common x values
xyplot(mpg ~ factor(cyl), mtcars, lwd = 2,
panel = function(x, y, ...) {
panel.barplot(x, y, ...)
}
)
# using the same variable for x and grouping will
# result in typical lattice behavior
xyplot(mpg ~ factor(cyl), mtcars,
groups = cyl, lwd = 2,
panel = function(x, y, ...) {
panel.barplot(x, y, ...)
}
)
# we can also use different variables for the x var, grouping,
# and paneling. As a visual control that error bars are drawn
# for the correct groups we overlay the single data points.
xyplot(mpg ~ factor(cyl) | factor(vs), mtcars,
groups = gear, lwd = 2, auto.key = list(columns = 3),
panel = function(x, y, ...) {
panel.barplot(x, y, beside = TRUE, draw_points = TRUE, ...)
}
)
# alternatively, means and error margins can be supplied directly.
# In this case means are supplied as unique combinations
# of y and x while error_margin is a separate vector with same length as y.
mtcars_means <- data.frame(
cyl = sort(unique(mtcars$cyl)),
mpg = with(mtcars, tapply(mpg, cyl, mean)),
stdev = with(mtcars, tapply(mpg, cyl, sd))
)
# you might have to adjust the y-scale as it is determined from the
# range of the y variable only, ignoring the extension through error bars.
xyplot(mpg ~ factor(cyl), mtcars_means,
error_margin = mtcars_means$stdev,
ylim = c(9, 36), groups = cyl,
lwd = 2, pch = 19, cex = 1.5,
panel = function(x, y, ...) {
panel.barplot(x, y, ...)
}
)
# if you supply a two column matrix as the error_margin argument,
# error bars with different lower and upper bounds can be drawn
error_mat <- matrix(ncol = 2, 1:6)
xyplot(mpg ~ factor(cyl), mtcars_means,
error_margin = error_mat, twoway = TRUE, fill = NA,
ylim = c(9, 36), groups = cyl,
lwd = 2, pch = 19, cex = 1.5,
panel = function(x, y, ...) {
panel.barplot(x, y, ...)
}
)
Panel function for beeswarm plots. This panel function works essentially like a stripplot, but instead of randomly scattering a variable produces a regular, grid-like pattern of points. Currently only the X variable is transformed. Continuously distributed data points can be optionally discretized, both for X and for Y variable.
# simple example
df <- data.frame(
Y = sample(1:10, 60, replace = TRUE),
X = factor(rep(1:3, each = 20))
)
# beeswarm plot
xyplot(Y ~ X, df, groups = X, panel = panel.beeswarm)
# but with continuous Y variable, it doesn't work as expected
df$Y <- rnorm(60)
xyplot(Y ~ X, df, groups = X, panel = panel.beeswarm)
# for this purpose we can bin the Y variable into groups
xyplot(Y ~ X, df, groups = X,
panel = function(x, y, ...) {
panel.beeswarm(x, y, bin_y = TRUE, breaks_y = 10, ...)
})
# the breaks for Y bins are computed for each panel independently.
# we can also supply fixed bins via the 'breaks_y' argument
# to obtain the same binning for each panel
xyplot(Y ~ factor(rep(1, length(Y))) | X, df, groups = X,
panel = function(x, y, ...) {
panel.beeswarm(x, y, bin_y = TRUE,
breaks_y = seq(-4, 4, length.out = 20), ...)
})
Point labels for scatterplots. Draw text labels for all points of a
scatterplot using internal functionality from the directlabels
package. Note: an alternative panel function, panel.repellabels
, is a
wrapper using ggrepel calculated label positions instead. The same
behavior can be achieved by using “ggrepel” for the positioning
argument.
In contrast to the original directlabels
package, every point is
labeled instead of groups of points. Labels are also independent from
the grouping variable, so that e.g. colors indicate one grouping
variable and labels another. By default, labels adapt the graphical
parameters of the higher level plot, including coloring according to
groups. However, many parameters can be customized.
library(grid)
library(lattice)
data("mtcars")
mtcars$car <- rownames(mtcars)
# A standard example using lattice grouping and paneling;
# We can also draw boxes around labels and change label size
xyplot(mpg ~ wt | factor(cyl), mtcars,
groups = cyl, pch = 19, labels = mtcars$car,
as.table = TRUE, layout = c(3, 1), cex = 0.6,
panel = function(x, y, ...) {
panel.xyplot(x, y, ...)
panel.directlabels(x, y, draw_box = TRUE, box_line = TRUE, ...)
}
)
# A similar plot but without grouping. This requires explicit
# use of subscripts
xyplot(mpg ~ wt | factor(cyl), mtcars,
pch = 19, labels = mtcars$car,
as.table = TRUE, layout = c(3, 1), cex = 0.6,
panel = function(x, y, subscripts, ...) {
panel.xyplot(x, y, ...)
panel.directlabels(x, y, subscripts = subscripts,
draw_box = TRUE, box_fill = "white", ...)
}
)
# An example without panels and more groups
xyplot(mpg ~ wt, mtcars,
groups = hp, pch = 19,
labels = mtcars$wt, cex = 0.6,
panel = function(x, y, ...) {
panel.xyplot(x, y, ...)
panel.directlabels(x, y, draw_box = TRUE, box_line = TRUE, ...)
}
)
Make a dumbbell chart. Similar to a lollipop chart that shows a single point supported by a horizontal or vertical line, the dumbbell chart shows the minimum and maximum of a distribution connected by a line. The direction of the dumbbell (i.e. vertical or horizontal) can be specified manually, or it is determined from the data automatically (the default).
library(lattice)
data(mtcars)
# vertical dumbbells
xyplot(mpg ~ factor(gear), mtcars,
groups = gear,
pch = 19, lwd = 2,
panel = function(x, y, ...) {
panel.dumbbell(x, y, ...)
}
)
# horizontal dumbbells
xyplot(factor(gear) ~ mpg, mtcars,
groups = gear,
pch = 19, lwd = 2,
panel = function(x, y, ...) {
panel.dumbbell(x, y, ...)
}
)
Draw error bars in lattice plots. This panel function allows to draw
symbols with error bars for arbitrary groups of data points. Error bars
will be drawn for groups of identical x values with optional subsetting
by grouping or paneling variables. This function is very similar to
panel.barplot
only with points instead of bars.
library(lattice)
data(mtcars)
# mean and stdev error bars are drawn for
# common x values
xyplot(mpg ~ factor(cyl), mtcars,
lwd = 2, pch = 19, cex = 1.5,
panel = function(x, y, ...) {
panel.errbars(x, y, ...)
}
)
# using the same variable for x and grouping will
# result in typical lattice behavior
xyplot(mpg ~ factor(cyl), mtcars,
groups = cyl, lwd = 2, pch = 19, cex = 1.5,
panel = function(x, y, ...) {
panel.errbars(x, y, ...)
}
)
# we can also use different variables for the x var, grouping,
# and paneling.
xyplot(mpg ~ factor(cyl) | factor(vs), mtcars,
groups = gear, lwd = 2, pch = 19, cex = 1.5,
panel = function(x, y, ...) {
panel.errbars(x, y, beside = TRUE, ...)
}
)
# alternatively, means and error margins can be supplied directly.
# In this case means are supplied as unique combinations
# of y and x while error_margin is a separate vector with same length as y.
mtcars_means <- data.frame(
cyl = sort(unique(mtcars$cyl)),
mpg = with(mtcars, tapply(mpg, cyl, mean)),
stdev = with(mtcars, tapply(mpg, cyl, sd))
)
# you might have to adjust the yscale as it is determined from the
# range of the y variable only, ignoring the extension through error bars.
xyplot(mpg ~ factor(cyl), mtcars_means,
error_margin = mtcars_means$stdev,
ylim = c(9, 36), groups = cyl,
lwd = 2, pch = 19, cex = 1.5,
panel = function(x, y, ...) {
panel.errbars(x, y, ...)
}
)
# if you supply a two column matrix as the error_margin argument,
# error bars with different lower and upper bounds can be drawn
error_mat <- matrix(ncol = 2, 1:6)
xyplot(mpg ~ factor(cyl), mtcars_means,
error_margin = error_mat,
ylim = c(9, 36), groups = cyl,
lwd = 2, pch = 19, cex = 1.5,
panel = function(x, y, ...) {
panel.errbars(x, y, ...)
}
)
Plot genes along a linear axis. This panel function allows to draw genes
with start and end coordinates as main input. Optional vectors for
gene_name
or gene_strand
must have same length as x
, y
. This
function supports paneling and grouping (e.g. by gene functional
category) just as regular panel functions.
library(lattice)
# table with dummdy genetic loci
genes <- data.frame(
gene_name = c("abc", "def", "ghi", "jkl"),
gene_strand = c("+", "+", "+", "-"),
gene_start = c(123, 178, 245, 310),
gene_end = c(167, 233, 297, 354)
)
# plot genes on a linear map
xyplot(gene_end ~ gene_start, genes,
groups = gene_strand,
scales = list(y = list(draw = FALSE)),
xlim = c(80, 380), ylim = c(-3,2),
xlab = "", ylab = "",
gene_strand = genes[["gene_strand"]],
gene_name = genes[["gene_name"]],
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.geneplot(x, y, arrows = TRUE, ...)
}
)
# same example with customized arrows
xyplot(gene_end ~ gene_start, genes,
groups = gene_strand,
scales = list(y = list(draw = FALSE)),
xlim = c(80, 380), ylim = c(-3,2),
xlab = "", ylab = "",
gene_strand = genes[["gene_strand"]],
gene_name = genes[["gene_name"]],
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.geneplot(x, y, arrows = TRUE, offset = 0.5,
height = 0.6, rot_labels = 0, tip = 3, col_labels = 1, ...)
}
)
Draw custom keys in lattice plots. This panel function allows to draw a key (legend) inside a lattice panel, with more customization options than the lattice default.
library(lattice)
data(mtcars)
# Two examples for a custom lattice key inside a panel.
# The first takes all arguments from the
# top-level plotting function.
# The second has custom arguments.
xyplot(mpg ~ 1/wt | factor(vs), mtcars,
groups = carb, pch = 19,
panel = function(x, y, ...) {
panel.xyplot(x, y, ...)
panel.key(...)
panel.key(labels = letters[1:5], which.panel = 2,
corner = c(0.9, 0.1), col = 1:5, pch = 1:5)
}
)
Make a lollipop chart. This panel function creates lollipop charts. A lollipo chart is simply an XY-plot where the single points are supported by a horizontal or vertical line originating from one of the axes, or a specified threshold.
library(lattice)
data(mtcars)
mtcars$car <- rownames(mtcars)
xyplot(mpg ~ factor(car, car[order(mpg)]), mtcars[1:10, ],
pch = 19, xlab = "", lwd = 2,
scales = list(x = list(rot = 35)),
panel = function(x, y, ...) {
panel.lollipop(x, y, ...)
}
)
# with grouping, and lollipops hanging from top
xyplot(mpg ~ factor(car, car[order(mpg)]), mtcars[1:10, ],
groups = gear, pch = 19, xlab = "", lwd = 2,
scales = list(x = list(rot = 35)),
panel = function(x, y, ...) {
panel.lollipop(x, y, origin = "top", ...)
}
)
Draw pie and ring charts in lattice plots. This panel function allows to
draw pie charts (or rings) while still being able to use the typical
lattice way of subsetting data. The function can be used within
xyplot()
but only one variable needs to be supplied (x
). Grouping is
supported in the sense that the x
variable is aggregated (summed up)
over each unique group.
library(grid)
library(lattice)
data("USMortality")
# A simple example using lattice paneling
xyplot( ~ Rate | Sex, USMortality,
main = "US mortality rates by sex",
scales = list(draw = FALSE), cex = 0.7,
panel = function(x, ...) {
panel.piechart(x, ...)
}
)
# A more advanced example using grouping and
# adjusting graphical parameters. The main variable
# 'x' is now summed up for each value of 'groups'
xyplot( ~ Rate | Sex, USMortality,
groups = gsub(" ", "\n", Cause),
col = heat.colors(10),
border = grey(0.3), cex = 0.7,
main = "US mortality rates by sex",
scales = list(draw = FALSE),
panel = function(x, ...) {
panel.piechart(x, diameter_sector = 0.1, ...)
}
)
Calculate and draw p-values in lattice plots. This panel function allows
to overlay p-values obtained from a statistical significance test on
plots, together with “significance” symbols such as stars as it is often
in encountered in the scientific literature. By default, the function
applies Student’s (2-sided) t.test
to each pair of a unique X variable
and a standard. By default, the standard is the first unique value of X,
but can be specified manually.
library(lattice)
data(mtcars)
# p-values are calculated between groups of x, grouping is ignored
xyplot(mpg ~ factor(cyl), mtcars, groups = cyl, pch = 19, cex = 0.7,
panel = function(x, y, ...) {
panel.stripplot(x, y, jitter.data = TRUE,
horizontal = FALSE, amount = 0.15, ...)
panel.pvalue(x, y, std = 1, symbol = TRUE,
pvalue = TRUE, offset = 6)
})
Draw quadrants and quadrant statistics in lattice plots. This panel function allows to draw custom quadrants and display additional quadrant statistics as often used in biomedical sciences. Grouping is ignored.
library(lattice)
data(mtcars)
# Default behavior for quadrants is to split x and y data
# at the respective median, and plot percentage of points
# per quandrant
xyplot(mpg ~ 1/wt | factor(vs), mtcars,
groups = carb, pch = 19, cex = 1,
panel = function(x, y, ...) {
panel.xyplot(x, y, ...)
panel.quadrants(x, y, ...)
}
)
Draw text labels for all points of a scatterplot using internal
functionality from the ggrepel
package. Note: an alternative panel
function, panel.directlabels
, carries most of the functionality of
this function, but uses directlabels
to calculate label positions
instead. The same behavior can be achieved by using panel.directlabels
with “ggrepel” for the positioning
argument.
By default, labels adapt the graphical parameters of the higher level plot, including coloring according to groups. However, many parameters can be customized.
library(grid)
library(lattice)
data("mtcars")
mtcars$car <- rownames(mtcars)
# A standard example using lattice grouping and paneling;
# We can also draw boxes around labels and change label size
xyplot(mpg ~ wt | factor(cyl), mtcars,
groups = cyl, pch = 19, labels = mtcars$car,
as.table = TRUE, layout = c(3, 1), cex = 0.6,
panel = function(x, y, ...) {
panel.xyplot(x, y, ...)
panel.repellabels(x, y, draw_box = TRUE, box_line = TRUE, ...)
}
)
# A similar plot with panels, but without grouping.
# This requires explicit use of subscripts
xyplot(mpg ~ wt | factor(cyl), mtcars,
pch = 19, labels = mtcars$car,
as.table = TRUE, layout = c(3, 1), cex = 0.6,
panel = function(x, y, subscripts, ...) {
panel.xyplot(x, y, ...)
panel.repellabels(x, y, subscripts = subscripts,
draw_box = TRUE, box_fill = "white", ...)
}
)
# An example without panels and more groups
xyplot(mpg ~ wt, mtcars,
groups = hp, pch = 19,
labels = mtcars$wt, cex = 0.6,
panel = function(x, y, ...) {
panel.xyplot(x, y, ...)
panel.repellabels(x, y, draw_box = TRUE, box_line = TRUE, ...)
}
)
Plot a grouping variable encoded by symbols. This panel function allows
to plot one additional grouping variable encoded by symbols (‘pch’)
instead of just the default grouping by color. Inspired by
panel.bubbleplot
from package tactile
.
library(lattice)
data(mtcars)
# first grouping variable for colors, second for symbols (z)
xyplot(mpg ~ factor(cyl), mtcars,
groups = gear, z = mtcars$cyl,
panel = function(x, y, z, ...) {
panel.symbols(x, y, z, ...)
}
)
Combine violin plot and scatter plot. This panel function combines
violin and scatter plot to a violin-shaped scatter plot. By default, the
underlying violin is plotted with the generic panel.violin
function.
On top of that are the points drawn from which the violin was
calculated. To achieve this, each point is jittered randomly on the X
(Y) axis according to the kernel density estimate of the Y (X) axis. All
parameters that work for panel.violin
can be used with this function
too (e.g. to control the kernel density function). In addition to that,
this function supports grouping by color and is therefore more flexible
than the canonical panel.violin
.
# use singer data from lattice
library(lattice)
data(singer)
singer$voice.part <- gsub(" [12]", "", as.character(singer$voice.part))
singer$voice.part <- factor(singer$voice.part, c("Soprano", "Alto", "Tenor", "Bass"))
# example with grouping
xyplot(height ~ voice.part, singer, groups = voice.part,
horizontal = FALSE, pch = 19,
panel = function(x, y, ...) {
panel.violinscatter(x, y, ...)
})
# same plot but horizontal orientation
xyplot(voice.part ~ height, singer, groups = voice.part,
horizontal = TRUE, pch = 19,
panel = function(x, y, ...) {
panel.violinscatter(x, y, ...)
})
# example with more and non-discrete data points
df <- data.frame(
sample = factor(rep(c("A", "B", "C"), each = 300)),
variable = c(rnorm(300, 0, 3), rnorm(300, 1, 2), rnorm(300, 3, 3))
)
xyplot(variable ~ sample, df,
horizontal = FALSE, pch = 19, cex = 0.4,
panel = function(x, y, ...) {
panel.violinscatter(x, y, ...)
})
Custom ggplot2
like theme for lattice plots. Graphical parameters are
passed down to ggplot2like()
, which passes them down to
simpleTheme()
.
library(lattice)
data(mtcars)
xyplot(mpg ~ factor(carb) | gear, mtcars,
groups = carb, auto.key = list(columns = 3),
par.settings = custom.ggplot(),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = "white")
panel.xyplot(x, y, ...)
panel.lmline(x, y, ...)
}
)
Custom grey theme for lattice plots. Some graphical parameters can be passed to override the defaults. Size parameters are relative.
library(lattice)
data(mtcars)
xyplot(mpg ~ factor(carb) | gear, mtcars,
groups = carb, auto.key = list(columns = 3),
par.settings = custom.lattice(),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.95))
panel.xyplot(x, y, ...)
panel.lmline(x, y, ...)
}
)
Colorblind-safe grey lattice theme. Colorblind-safe color scale with 7
colors was adapted from R color brewer, see
RColorBrewer::brewer.pal(8, "Dark2"))
. Color blind safe colors are
distinguishable for most common types of color blindness (deuterotopia,
deuteroanomaly), yet still look good for non color blind people. Some
graphical parameters can be passed to override the defaults. Size
parameters are relative.
library(lattice)
data(mtcars)
xyplot(mpg ~ factor(carb) | gear, mtcars,
groups = carb, auto.key = list(columns = 3),
par.settings = custom.colorblind(),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.95))
panel.xyplot(x, y, ...)
panel.lmline(x, y, ...)
}
)
Custom scatterplot matrix (SPLOM). A wrapper function around lattice ‘splom’ with different upper and lower panels. A scatterplot matrix is a tiled plot where all variables of a data frame are plotted against each other.
library(lattice)
data(mtcars)
# Draw a scatterplot matrix of all variables of a
# data frame against each other.
custom_splom(mtcars[1:5])
# We can customize the scatterplot
custom_splom(
mtcars[1:5],
col_palette = c("#9FA2FF", "#F1F1F1", "#BAAE00"),
pscales = 10,
xlab = "data points", ylab = "regression",
pch = 1, col = 1, cex = 0.6
)