Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Package: emmeans
Type: Package
Title: Estimated Marginal Means, aka Least-Squares Means
Version: 2.0.0
Date: 2025-10-24
Version: 2.0.1
Date: 2025-12-10
Authors@R: c(person("Russell V.", "Lenth", role = c("aut", "cph"),
email = "russell-lenth@uiowa.edu"),
person("Julia", "Piaskowski", role = c("cre", "aut"),
Expand Down
15 changes: 15 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@
title: "NEWS for the emmeans package"
---

## emmeans 2.0.1

* Fix to `.parse_nest()` to handle nested factors with spaces in their name (#562)
* `col` was not handled properly in `emmip()` (#565)
* In `emmip()`, changed default for `facetlab` to `"label_both"`. This just
seems like a better choice.
* Added a `facetlab` argument to `plot.emmGrid()` -- badly needed when we have
several `by` groups
* Added the `pval.digits` option to specify the desired P-value precision (#567).
It defaults to 4, which was the only precision available in past versions.
* Note that the above change corrects a slight bug in past versions:
a P value smaller than 0.0001 that rounded *up* to 0.0001 was printed as `0.0001`
rather than `<.0001`.


## emmeans 2.0.0
We have a new major version number, with a new graphics look and a new maintainer,
Julia Piaskowski (however, Russ is still very much involved).
Expand Down
7 changes: 6 additions & 1 deletion R/emmGrid-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,10 @@ update.emmGrid = function(object, ..., silent = FALSE) {
#' displayed is just enough to reasonably distinguish estimates from the ends
#' of their confidence intervals; but always at least 3 digits. If
#' \code{FALSE}, the system value \code{getOption("digits")} is used.}
#' \item{\code{pval.digits}}{An integer indicating the precision with which
#' p-values are printed. Integers between \code{2} and \code{6} are acceptable; the default is
#' \code{4}. Floating point numbers are truncated. If a number outside the accepted range is
#' used, the nearest acceptable integer will be used instead. Non-numeric arguments are ignored.}
#' \item{\code{back.bias.adj}}{A logical value controlling whether we
#' try to adjust bias when back-transforming. If \code{FALSE}, we use naive
#' back transformation. If \code{TRUE} \emph{and \code{sigma} is available and valid}, a
Expand Down Expand Up @@ -767,7 +771,8 @@ emm_defaults = list (
estble.tol = 1e-8, # tolerance for estimability checks
simplify.names = TRUE, # simplify names like data$x to just "x"
back.bias.adj = FALSE, # Try to bias-adjust back-transformations?
opt.digits = TRUE, # optimize displayed digits?
opt.digits = TRUE, # optimize displayed digits for estimates and CI's?
pval.digits = 4L, # how many digits to list for p-values
enable.submodel = TRUE, # enable saving extra info for submodel
rg.limit = 10000, # limit on number of rows in a reference grid
lmer.df = "kenward-roger", # Use Kenward-Roger for df
Expand Down
89 changes: 42 additions & 47 deletions R/emmip.R
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ emmip = function(object, formula, ...) {
#' # Create a black-and-white version of above with different linetypes
#' # (Let the linetypes and symbols default to the palette)
#' emmip(noise.lm, type ~ side * size, CIs = TRUE, col = "black",
#' linearg = list(), dotarg = list(size = 2), CIarg = list(alpha = 1)) +
#' linearg = list(), dotarg = list(size = 4), CIarg = list(alpha = 1)) +
#' ggplot2::theme_bw()
#'
#' # One interaction plot using combinations of type and side as the trace factor
Expand Down Expand Up @@ -305,11 +305,11 @@ emmip.default = function(object, formula, type, CIs = FALSE, PIs = FALSE,
#' axis, and traces (the different curves), respectively. The \code{emmip}
#' function generates these automatically and provides therm via the \code{labs}
#' attribute, but the user may override these if desired.
#' @param facetlab Labeller for facets (when by variables are in play).
#' Use \code{"label_value"} to show just the factor levels, or \code{"label_both"}
#' to show both the factor names and factor levels. The default of
#' \code{"label_context"} decides which based on how many \code{by} factors there are.
#' See the documentation for \code{ggplot2::label_context}.
#' @param facetlab Labeller for facets (when by variables are in play). Use
#' \code{"label_value"} to show just the factor levels, or \code{"label_both"}
#' to show both the factor names and factor levels; \code{"label_context"}
#' decides which based on how many \code{by} factors there are. See the
#' documentation for \code{ggplot2::labellers}.
#' @param scale If not missing, an object of class \code{scales::trans} specifying
#' a (usually) nonlinear scaling for the vertical axis. For example,
#' \code{scales = scales::log_trans()} specifies a logarithmic scale. For
Expand Down Expand Up @@ -361,18 +361,20 @@ emmip.default = function(object, formula, type, CIs = FALSE, PIs = FALSE,
#' @importFrom rlang .data
emmip_ggplot = function(emms, style = "factor", dodge = .2,
xlab = labs$xlab, ylab = labs$ylab, tlab = labs$tlab,
facetlab = "label_context",
facetlab = "label_both",
scale,
dotarg = list(shape = "circle"),
linearg = list(linetype = "solid"),
CIarg = list(alpha = .40),
PIarg = list(alpha = .25),
col = NULL,
...) {

thm = theme_emm() # Depends on gg.theme option

labs = attr(emms, "labs")
vars = attr(emms, "vars")
ngrps = ifelse(is.null(emms$tvar), 1, length(unique(emms$tvar)))

shape.pal = linetype.pal = NULL # we use these to store shape and linetype specs in dotarg and linearg

Expand All @@ -386,42 +388,35 @@ emmip_ggplot = function(emms, style = "factor", dodge = .2,
dotarg$position = pos
if(is.null(dotarg$size))
dotarg$size = 3
if(!is.null(dotarg$shape) && length(dotarg$shape) > 1) { # treat as a shape palette
shape.pal = dotarg$shape
dotarg$shape = NULL
}

linearg$mapping = ggplot2::aes(group = .data$tvar)
linearg$position = pos
if(is.null(linearg$linewidth))
linearg$linewidth = 0.8
if(!is.null(linearg$linetype) && length(linearg$linetype) > 1) { # treat as a linetype palette
linetype.pal = linearg$linetype
linearg$linetype = NULL
}

if (length(vars$tvars) > 0) {
labarg = list(x = xlab, y = ylab, color = tlab)
grobj = ggplot2::ggplot(emms, ggplot2::aes(x = .data$xvar, y = .data$yvar, color = .data$tvar))

if(!is.null(dotarg$shape) && length(dotarg$shape) > 1) {
grobj = grobj + ggplot2::aes(shape = .data$tvar)
labarg$shape = tlab
if (ngrps > 1) {
if(!is.null(dotarg$shape) && length(dotarg$shape) > 1) { # treat as a shape palette
shape.pal = dotarg$shape
dotarg$shape = NULL
}
if(!is.null(linearg$linetype) && length(linearg$linetype) > 1) {
grobj = grobj + ggplot2::aes(linetype = .data$tvar)
labarg$linetype = tlab

linearg$mapping = ggplot2::aes(x = as.numeric(.data$xvar), linetype = .data[[tlab]])
dotarg$mapping = ggplot2::aes(shape = .data[[tlab]])

linearg$position = pos
if(is.null(linearg$linewidth))
linearg$linewidth = 0.8
if(!is.null(linearg$linetype) && length(linearg$linetype) > 1) { # treat as a linetype palette
linetype.pal = linearg$linetype
linearg$linetype = NULL
}

if (style == "factor")
emms[[tlab]] = emms$tvar # make the trace column have same name as its label

grobj = ggplot2::ggplot(emms, ggplot2::aes(x = .data$xvar, y = .data$yvar, group = .data[[tlab]],
color = .data[[tlab]]))

if (style == "factor") {
grobj = grobj + do.call(ggplot2::geom_point, dotarg) +
do.call(ggplot2::geom_line, linearg) +
do.call(ggplot2::labs, labarg) +
ggplot2::scale_x_discrete(expand = ggplot2::expansion(mult = 0.1))
else
grobj = grobj +
do.call(ggplot2::geom_line, linearg) +
do.call(ggplot2::labs, labarg)
}
grobj = grobj +
do.call(ggplot2::geom_line, linearg)

# handle any custom shapes or linetypes
if(!is.null(shape.pal))
Expand All @@ -430,23 +425,20 @@ emmip_ggplot = function(emms, style = "factor", dodge = .2,
grobj = grobj + ggplot2::scale_linetype_manual(values = linetype.pal)
}
else { # just one trace per plot
if(is.null(linearg$color) && is.null(linearg$colour))
linearg$color = .emm_palette[1]
if(is.null(dotarg$color) && is.null(dotarg$colour))
dotarg$color = .emm_palette[1]
if(is.null(CIarg$color) && is.null(CIarg$colour))
CIarg$color = .emm_palette[1]
if(is.null(PIarg$color) && is.null(PIarg$colour))
PIarg$color = .emm_palette[1]
if(is.null(col))
col = .emm_palette[1]
linearg$color = dotarg$color = CIarg$color = PIarg$color = col
col = NULL
grobj = ggplot2::ggplot(emms, ggplot2::aes(x = .data$xvar, y = .data$yvar))
if (style == "factor")
grobj = grobj + do.call(ggplot2::geom_point, dotarg) +
ggplot2::scale_x_discrete(expand = ggplot2::expansion(mult = 0.1))
linearg$mapping = ggplot2::aes(x = as.numeric(.data$xvar))
grobj = grobj +
do.call(ggplot2::geom_line, linearg) +
ggplot2::labs(x = xlab, y = ylab)
do.call(ggplot2::geom_line, linearg)

}
grobj = grobj + ggplot2::labs(x = xlab, y = ylab)
if (PIs) {
PIarg$mapping = ggplot2::aes(ymin = .data$LPL, ymax = .data$UPL)
PIarg$position = pos
Expand Down Expand Up @@ -478,6 +470,9 @@ emmip_ggplot = function(emms, style = "factor", dodge = .2,
if (style == "factor")
grobj = grobj + do.call(ggplot2::geom_point, dotarg)

if(!is.null(col))
grobj = grobj + ggplot2::scale_color_manual(values = rep(col, ngrps))


grobj + thm
}
Expand Down
3 changes: 2 additions & 1 deletion R/nested.R
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,9 @@ force_regular = function(object) {
spec = trimws(unlist(strsplit(spec, ",")))
for (s in spec) {
parts = strsplit(s, "[ ]+%in%[ ]+")[[1]]
trm = .all.vars(stats::reformulate(parts[1]))
grp = .all.vars(stats::reformulate(parts[2]))
result[[parts[[1]]]] = grp
result[[trm]] = grp
}
if(length(result) == 0)
result = NULL
Expand Down
22 changes: 15 additions & 7 deletions R/plot.emm.R
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ plot.emmGrid = function(x, y, type, CIs = TRUE, PIs = FALSE, comparisons = FALSE
#' plotted horizontally or vertically
#' @param xlab Character label for horizontal axis
#' @param ylab Character label for vertical axis
#' @param facetlab Character or function method used to label facets in a
#' multi-panel plot (with the \code{ggplot} engine).
#' Default is \code{"label_both"}, meaning that both factor names and levels are shown,
#' You can use \code{"label_value"} to save space, or \code{"label_context"}
#' to decide automatically (often wrongly). See the help page for
#' \code{ggplot2::labellers}
#' @param layout Numeric value passed to \code{\link[lattice:xyplot]{dotplot}}
#' when \code{engine == "lattice"}.
#' @param type Character value specifying the type of prediction desired
Expand Down Expand Up @@ -215,32 +221,34 @@ plot.emmGrid = function(x, y, type, CIs = TRUE, PIs = FALSE, comparisons = FALSE
#' plot(warp.emm, by = NULL, comparisons = TRUE, adjust = "none",
#' horizontal = FALSE, colors = "blue")
#'
#' ### Using a transformed scale
#' ### Using a transformed scale (also demonstrating 'facetlab' argument)
#' pigs.lm <- lm(log(conc + 2) ~ source * factor(percent), data = pigs)
#' pigs.emm <- emmeans(pigs.lm, ~ percent | source)
#' plot(pigs.emm, type = "scale", breaks = seq(20, 100, by = 10))
#' plot(pigs.emm, type = "scale", breaks = seq(20, 100, by = 10),
#' facetlab = "label_value")
#'
#' # Based on a summary.
#' # To get a transformed axis, must specify 'scale'; but it does not necessarily
#' # have to be the same as the actual response transformation
#' pigs.ci <- confint(pigs.emm, type = "response")
#' plot(pigs.ci, scale = scales::log10_trans())
#' plot(pigs.ci, scale = scales::log10_trans(),
#' facetlab = \(x) ggplot2::label_both(x, sep = " = "))
plot.summary_emm = function(x, y, horizontal = TRUE, CIs = TRUE,
xlab, ylab, layout, scale = NULL,
xlab, ylab, facetlab = "label_both", layout, scale = NULL,
colors, intervals,
plotit = TRUE, ...) {
if(!missing(intervals))
CIs = intervals
if(attr(x, "type") != "response") # disable scale when no response transformation
scale = NULL
.plot.srg (x, y, horizontal, xlab, ylab, layout, scale = scale,
.plot.srg (x, y, horizontal, xlab, ylab, facetlab = facetlab, layout, scale = scale,
CIs = CIs, colors = colors, plotit = plotit, ...)
}

# Workhorse for plot.summary_emm
#' @importFrom grDevices col2rgb hcl rgb rgb2hsv
.plot.srg = function(x, y,
horizontal = TRUE, xlab, ylab, layout, colors,
horizontal = TRUE, xlab, ylab, facetlab = "label_both", layout, colors,
engine = get_emm_option("graphics.engine"),
CIs = TRUE, PIs = FALSE, extra = NULL,
plotit = TRUE, backtran = FALSE, link, scale = NULL, ...) {
Expand Down Expand Up @@ -582,7 +590,7 @@ plot.summary_emm = function(x, y, horizontal = TRUE, CIs = TRUE,
}
if (length(byv) > 0)
grobj = grobj + ggplot2::facet_grid(as.formula(paste(paste(byv, collapse = "+"), " ~ .")),
labeller = "label_both")
labeller = facetlab)
grobj = grobj + ggplot2::geom_point(color = dot.col, size = 4, shape = 18)

if(!is.null(scale)) {
Expand Down
13 changes: 11 additions & 2 deletions R/summary.R
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,13 @@ print.summary_emm = function(x, ..., digits=NULL, quote=FALSE, right=TRUE, expor
if(!is.null(attr(x, "digits")))
digits = attr(x, "digits")

pval.digits = suppressWarnings(as.integer(get_emm_option("pval.digits")))[1]
if(is.na(pval.digits) || (pval.digits < 2) || (pval.digits > 6)) {
pval.digits = ifelse(is.na(pval.digits), emm_defaults$pval.digits, pval.digits)
pval.digits = max(2, min(6, pval.digits))
emm_options(pval.digits = pval.digits)
}

test.stat.names = c("t.ratio", "z.ratio", "F.ratio", "T.square") # format these w 3 dec places
x.save = x
if(export) x.save = list()
Expand All @@ -1368,8 +1375,10 @@ print.summary_emm = function(x, ..., digits=NULL, quote=FALSE, right=TRUE, expor
if(!is.null(x[[nm]]))
x[[nm]] = format(round(x[[nm]], 3), nsmall = 3, sci = FALSE)
if (!is.null(x$p.value)) {
fp = x$p.value = format(round(x$p.value, 4), nsmall = 4, sci = FALSE)
x$p.value[fp=="0.0000"] = "<.0001"
pval_min = 10^-(pval.digits)
pval_min_display = format(pval_min, nsmall = pval.digits, sci = FALSE)
x$p.value = ifelse(x$p.value < pval_min, paste0("<", pval_min_display),
format(round(x$p.value, pval.digits), nsmall = pval.digits, sci = FALSE))
}

est = x[[estn]]
Expand Down
15 changes: 15 additions & 0 deletions maintainer-notes/cheatsheet.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

# to load all functions into an R session
devtools::load_all()

# to rebuild documentation:
roxygen2::roxygenise()

# to run all unit tests
testthat::test_dir("tests/testthat")

# to build the entire package
devtools::build()

# required check
devtools::check()
5 changes: 5 additions & 0 deletions maintainer-notes/devel-notes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## emmeans devel branch

currently at 2.0.1


7 changes: 7 additions & 0 deletions maintainer-notes/emm-prep-details.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,15 @@ Then check to make sure there are no errors. I always do this in a DOS window (o
```
R CMD check --as-cran emmeans-xxx.tar.gz
```

Obviously, you need to correct any errors that are found.

*Note:* This creates a directory `emmeans.Rcheck/` at the same level as `emmeans/`.
Sometimes I get errors if this directory already exists, because my system sometimes thinks
I have an open file in there somewhere. So I manually delete that directory before checking.



### Doing a thorough check of related packages
Installs all reverse dependencies and checks functionality of updated package.

Expand Down Expand Up @@ -98,6 +101,10 @@ If there is a problem, contact the package developer. These issues need to be re

### build a TarBall

In an R session:
```
devtools::build()
```

### Check against R-Devel
Go to the [Win-Builder site](https://win-builder.r-project.org/upload.aspx)
Expand Down
Empty file removed maintainer-notes/md
Empty file.
6 changes: 5 additions & 1 deletion man/emm_options.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading