Skip to content

ggpubr 1.0.0

Latest

Choose a tag to compare

@kassambara kassambara released this 06 Jul 14:59
· 59 commits to master since this release

ggpubr 1.0.0 is on CRAN: https://cran.r-project.org/package=ggpubr

Install with:

install.packages("ggpubr")

New features

New functions

  • Added format_p_value(), get_p_format_style(), and list_p_format_styles().
  • Added style presets (default, apa, nejm, lancet, ama, graphpad, scientific).

New arguments, computed variables, and input support

  • ggmaplot() gains an optional facet.by argument to split the MA plot into
    multiple panels (e.g. one per contrast or species). Top genes are selected per
    panel, while point colors use the same significance thresholds in every panel.
    Default output (no facet.by) is unchanged (#498).

  • stat_cor() and stat_regline_equation() gain a label.anchor argument.
    With label.anchor = "panel" the label is placed at the true panel-relative
    position (npc), so labels stay aligned across panels or facets whose axis
    ranges differ - e.g. facet_wrap(scales = "free_y"), geom_smooth()
    extending each panel by a different amount, or separate plots combined with
    ggarrange(). The default label.anchor = "data" keeps the previous
    data-range placement, so existing plots are unchanged (#248).

  • geom_bracket() gains an orientation argument. orientation = "vertical"
    draws a native vertical bracket (the bar spans the y axis, the tips point left
    and the label is rotated), specified with ymin/ymax/x.position — useful
    to annotate plots where the comparison runs along the y axis, e.g.
    Kaplan-Meier curves (it is designed for a continuous x axis). The default
    orientation = "horizontal" is unchanged, and it cannot be combined with
    coord.flip = TRUE (#456).

  • ggpaired() gains an opt-in jitter argument that spreads the paired points
    horizontally to reduce overlap. Each subject (id) gets a single offset, so
    its two points move together and the connecting line stays intact, and only
    the horizontal positions change (the values are never moved). Default
    jitter = 0 leaves the plot unchanged (#407).

  • ggboxplot(), ggviolin() and ggstripchart() gain an opt-in show.n
    argument. When show.n = TRUE, the number of observations ("n = <count>")
    is displayed at the top of each group. When the groups are dodged (a
    color/fill grouping with a dodging position), one count is shown per
    group; otherwise a single count is shown per x-axis tick. Counts respect
    select/remove and are computed per facet. Default is show.n = FALSE, so
    existing plots are unchanged (#598, #627).

  • ggarrange() gains a spacing argument to increase the gap between the
    arranged plots (a long-requested option). It adds a uniform margin, in
    text-line units, around each plot. Default is 0 (no extra space; each plot
    keeps its own margins, so existing arrangements are unchanged) (#151).

  • compare_means() gains an id argument for paired comparisons. Without it, a
    paired test (paired = TRUE) pairs observations by row order, so the p-value is
    wrong when the data are not sorted so that the compared groups align by subject.
    Passing id (the name of a subject-identifier column) pairs the observations by
    id instead — row-order independent, using only the complete pairs (via
    rstatix, so the result matches a direct rstatix::t_test()/wilcox_test()
    call). It works for a two-group, a pairwise (more than two groups) and a
    ref.group comparison with t.test/wilcox.test. Default (id = NULL) is
    unchanged (#560).

  • stat_compare_means() also gains the id argument, so a paired test can be
    aligned by subject id directly on a plot (e.g. ggpaired(...) + stat_compare_means(paired = TRUE, id = "subject")), displaying the correct
    p-value even when the data are not sorted by subject. Plots without id are
    unchanged (#560).

  • geom_pwc() and stat_pwc() gain a p.adjust.n argument giving the number of
    comparisons to use for the p-value adjustment (passed as n to
    stats::p.adjust()). Default is NULL (adjust by the number of computed
    p-values, unchanged behavior); set it when the displayed comparisons are a
    subset of a larger family so the adjustment reflects that larger size. It must
    be at least the number of p-values being adjusted, and applies to the
    panel-level adjustment (p.adjust.by = "panel") and the single-comparison
    case (#612).

  • annotate_figure() gains column.titles and row.titles arguments to add a
    title above each column and to the left of each row of an arranged grid
    (useful for publication panels). Each accepts a character vector (one title
    per column / per row, rendered in bold) or a list of grobs (e.g. built with
    text_grob()) for full styling control; row.titles are rotated 90 degrees.
    The titles are placed in reserved space (they do not overlap the plots) and
    are evenly spaced, so they assume equal-sized columns / rows (the ggarrange()
    default). Default is NULL (no titles; existing figures are unchanged) (#573).

  • ggbarplot() gains a numeric.x.axis argument (already available in
    ggline() and ggerrorplot()). With numeric.x.axis = TRUE the x variable
    is kept numeric instead of being coerced to a discrete factor, so bars are
    drawn at their numeric x positions (e.g. a time axis). Default is FALSE
    (unchanged behavior) (#463).

  • geom_bracket() and stat_pvalue_manual() gain a tip.length.ref argument
    controlling what tip.length is a fraction of. With the default
    tip.length.ref = "data" the tips are a fraction of the data range (unchanged
    behavior). With tip.length.ref = "axis" the tips are a fraction of the
    y-axis range (ylim/scale_y_*), which renders at the same physical fraction
    across plots and therefore gives visually constant tip lengths regardless of
    the data range - useful to keep tips consistent across facets or across
    separate plots with different scales (#362). stat_compare_means(comparisons =)
    draws its brackets via ggsignif::geom_signif() and does not support
    tip.length.ref; passing it there now emits an informative message pointing to
    stat_pvalue_manual(..., tip.length.ref = "axis") instead of being silently
    ignored (#362).

  • stat_cor() and stat_regline_equation() gain a label.y.step argument giving
    the vertical spacing (in text-line units) between the labels of successive
    groups. Default is 1.4 (unchanged behavior). Setting label.y.step = 0 stops
    the per-group vertical shift so labels align across facet panels when a factor
    is mapped to an aesthetic that also defines the facets (the long-standing
    "labels climbing stairs across facets" behavior; #248). This control was
    inspired by the vstep argument in the ggpmisc package (by Pedro J. Aphalo),
    from which ggpubr's correlation/regression label-positioning logic was
    originally adapted; this is acknowledged via @seealso in ?stat_cor.

  • ggviolin() gains a drop argument (passed to ggplot2::geom_violin()),
    default TRUE (unchanged behavior). Previously the argument was silently
    dropped, so grouped violins where a sub-sample has too few points to draw a
    density (which geom_violin() removes, including from the dodge position)
    could not be kept aligned with added boxplots / dot plots. Setting
    drop = FALSE together with position = position_dodge(0.8, preserve = "single")
    now reserves the empty dodge lane so all geoms stay aligned (#381).

  • ggarrange() gained the ability to choose which plot(s) supply the shared legend:
    common.legend now also accepts one or several plot indices. common.legend = 2
    uses the second plot's legend (handy when the first plot's legend is not
    representative, e.g. a group is missing in the first plot), and
    common.legend = c(1, 2) keeps and combines the legends of the listed plots into a
    single shared block (side by side for legend = "top"/"bottom", stacked for
    "left"/"right") - useful when the arranged plots genuinely need different
    legends. The documentation of common.legend was also clarified: common.legend = TRUE keeps the first plot's legend (it does not merge or validate legends), so plots
    should share a consistent scale for a single shared legend to be correct. Logical
    TRUE/FALSE behave exactly as before (#347).

  • ggdonutchart() gained a label.repel argument (default FALSE). When TRUE, slice
    labels are placed with ggrepel::geom_text_repel() and connected to their slice with
    leader lines, so the labels of many small slices no longer overlap (or get dropped). The
    default output is unchanged (#655).

  • ggboxplot() now accepts a position argument (e.g.
    position = position_dodge(0.9)), like ggviolin()/ggdotplot() already do.
    Previously passing position errored (formal argument "position" matched by multiple actual arguments) because the dodge was hardcoded; the default is
    unchanged (position_dodge(0.8)), so grouped box plots look the same unless
    you set it (#615).

  • ggsummarytable() gains an angle argument to rotate the summary-table text;
    default (angle = 0) is unchanged (#595).

  • ggmaplot() gains a line.color argument to set the threshold line color;
    default ("black") is unchanged (#322).

  • ggarrange() gains a byrow argument (default TRUE) to fill the plot grid
    by column (byrow = FALSE) instead of by row; forwarded to
    cowplot::plot_grid() (#225).

  • stat_regline_equation() gains coef.digits and rr.digits arguments to
    control the number of significant digits shown for the regression-equation
    coefficients and R2; defaults (2) reproduce the previous output (#312).

  • Added formatting parameters to statistical helpers:

    • p.format.style
    • p.digits
    • p.leading.zero
    • p.min.threshold
    • p.decimal.mark
  • Added p.format.signif support and related label handling paths.

  • stat_pvalue_manual() gains a p.digits argument (default 3) that formats
    numeric p-value label columns for display (e.g. label = "p" or
    label = "p.adj"), using the same format_p_value() engine as
    stat_anova_test(). This restores clean labels (e.g. 0.0156 instead of
    0.015625) with rstatix >= 1.0.0, which now returns full-precision pairwise
    p-values. Character labels (significance symbols, pre-formatted strings, glue
    expressions) are unaffected. Set p.digits = NULL to print the raw value.

  • stat_pvalue_manual() additionally gains p.format.style, p.leading.zero,
    p.min.threshold and p.decimal.mark, matching the p-value formatting
    arguments already available in stat_compare_means(), geom_pwc() and
    stat_anova_test(). These are opt-in and default so that the rendered labels
    are unchanged; for example p.min.threshold = 0.001 displays very small
    p-values as < 0.001, and p.format.style = "nejm" applies a journal style.

  • stat_cor() gains two label-formatting arguments:

    • r.leading.zero — set to FALSE to drop the leading zero of the
      correlation coefficient (e.g. .73 instead of 0.73), completing
      APA-style reporting together with p.leading.zero (#540). The dropped
      leading zero is preserved through plotmath rendering (the value is quoted
      so it is not silently re-normalized back to 0.73 in the default
      expression output).
    • p.coef.name — symbol for the p-value label; use "P" for an uppercase
      p-value (#541).
  • stat_cor() exposes two new computed variables, rmse and rmse.label, for
    the root mean square deviation (RMSE/RMSD) between x and y — useful for
    reporting agreement between paired measurements on the same scale (e.g.
    predicted vs. reference values). Display it with
    aes(label = after_stat(rmse.label)), or combine it with the correlation
    coefficient using paste(). The default label is unchanged (#458).

  • stat_cor() can now display the confidence interval of the correlation
    coefficient, via the new computed variables conf.int.low, conf.int.high
    and conf.int.label (e.g. "95% CI [0.21, 0.75]") and a conf.level argument
    (default 0.95). Show it with aes(label = after_stat(conf.int.label)), or
    combine it with the coefficient using paste(). The confidence interval is
    available for method = "pearson" only; it is NA for Spearman/Kendall. The
    default label is unchanged (#418).

Main changes

Compatibility

  • Added compatibility updates for modern ggplot2, dplyr, and tidyr.
  • Updated legacy size usage to linewidth where required by recent ggplot2.
  • Completed the size -> linewidth migration for the remaining line layers that
    still passed the deprecated size argument: the mean/median reference line added
    by gghistogram(add = ...) and ggdensity(add = ...), and the connector segments
    of ggdotchart(add = "segments"). These no longer emit ggplot2's "size aesthetic
    for lines was deprecated" or "Ignoring empty aesthetic: size" warnings, and the
    requested line width is now applied via linewidth.
  • ggmaplot() no longer emits an "Ignoring empty aesthetic: size" warning on its
    default call; the point layer sets size only when the user supplies a value.
  • Replaced deprecated tidyverse APIs in affected helper functions.
  • Added package startup lock-file checks and clean_lock_files() helper.
  • Relaxed minimum ggrepel dependency to >= 0.9.2 to keep Ubuntu oldrel
    (R 4.4.x) CI dependency resolution working.
  • geom_pwc() now detects an absent ref.group in a grouped subset via the
    rstatix_missing_ref_group condition class raised by recent rstatix
    (walking the parent chain with rlang::cnd_inherits()), in addition to
    matching the error message. This keeps the "skip ref-less subsets" behaviour
    working after rstatix made that error message clearer (rstatix #153); older
    rstatix versions are still handled via the message fallback.
  • The minimum required rstatix version is now >= 1.0.0 (the version that
    introduced the id argument used by compare_means(id = ), and the
    full-precision pairwise p-values relied on by the p-value formatting).

Validation and messaging

  • Hardened sparse-group handling in stat_compare_means() and geom_pwc().
  • Non-comparable grouped subsets (insufficient levels/observations) are now skipped
    while preserving valid inferential comparisons in the same layer.
  • Added per-group skip diagnostics in geom_pwc() so users can see which grouped
    subsets were skipped and why (e.g., missing ref.group or insufficient levels).
  • Added regression tests for mixed comparable/non-comparable subsets and fully sparse
    subsets.
  • Passing ggtheme = NULL to the plotting functions (e.g. ggscatter(),
    ggboxplot(), ggline(), gghistogram(), ...) now skips applying a ggpubr
    theme, so the plot keeps ggplot2's default theme or the theme set globally with
    theme_set(). Previously an explicit ggtheme = NULL was treated like an unset
    argument and theme_pubr() was still applied. Calls that omit ggtheme or pass
    a specific theme are unchanged (#561).
  • compare_means() now adjusts p-values within each group.by level (and
    response) rather than pooling all groups together, so a grouped adjustment
    matches filtering to one group and adjusting there. This changes p.adj values
    for calls that use group.by; ungrouped calls are unchanged (#200).
  • stat_compare_means(comparisons = ) now emits a one-time message (and the docs
    note) clarifying that the displayed pairwise p-values are not adjusted for
    multiple comparisons, pointing to geom_pwc() / stat_pvalue_manual() +
    compare_means(p.adjust.method = ) for corrected p-values (#293).

Documentation

  • Documented a recipe for labelling groups with significance letters (compact letter display):
    compute the pairwise comparisons and derive the letters with rstatix::add_cld(), then place
    them with geom_text() (see the "Significance letters" section in ?compare_means). Also covers
    the per-control letters case (a = differs from control 1, b = differs from control 2) (#464, #434).
  • Documented that a summarized ggbarplot() (e.g. add = "mean_se") must be faceted with
    the facet.by= argument, not by appending + facet_wrap()/+ facet_grid(): the summaries
    are pre-computed over facet.by, so a manually added facet pools the bars (and, for stacked
    bars, the error bars) across all panels (#739).
  • Clarified documentation to match existing behavior: the ggtheme default per
    function, the p-value threshold/precision defaults, the stat_compare_means()
    label separator (test method name, not correlation coefficient), and the
    package Description's list of p-value formatting styles. Thanks to @erdeyl (#749).
  • Expanded test coverage for formatting helpers and compatibility paths.
  • Regenerated manuals and package documentation after source sync.
  • Closes #663 (stat_compare_means() fatal warning/error in sparse grouped subsets with modern tidyr).
  • Closes #334 (stat_compare_means(label = "p.format") rounding and formatting control).
  • Related: #540, #626.

Credits

  • Contributor: Laszlo Erdey (Faculty of Economics and Business, University of Debrecen, Hungary).
  • Thanks to @gumeo (#418), @byzheng (#608) and @JulesROBIN15 (#625) whose pull
    requests proposed features shipped in this release: the stat_cor()
    confidence interval, the stat_cor() RMSE label, and a manual number of
    comparisons for p-value adjustment in geom_pwc()/stat_pwc().

Minor changes

  • Fixed ColorBrewer sequential palette parsing in .get_brewer_pal() so
    "YlOrBr" is recognized correctly.
  • Updated ggexport() to respect verbose = FALSE by suppressing filename
    print() output in multi-file raster/vector exports.
  • Hardened format_p_value() by validating non-NULL p.min.threshold as a
    single positive finite number.
  • Updated create_p_label() to preserve NA values in p.format (returning
    NA_character_ instead of stringifying to "p = NA").
  • theme_pubr() now draws the axis tick marks in black with linewidth 0.5,
    matching the axis lines; previously the ticks inherited a lighter grey/thinner
    style, visibly inconsistent when zoomed (#668).
  • theme_pubr() now sets strip.clip = "off" so the facet strip background
    border renders at its full linewidth. With the ggplot2 (>= 3.5.0) default
    (strip.clip = "on") the border was clipped to the strip area, cutting the
    outer half of the stroke so it looked thinner and misaligned with the panel
    when zoomed (follow-up to #668). Note: a facet label wider than its panel now
    overflows the strip instead of being truncated; restore clipping with
    + theme(strip.clip = "on") if needed.
  • xticks.by/yticks.by now anchor the axis breaks to round multiples of the
    step (e.g. 0, 100, 200) instead of the slightly-negative expanded axis minimum,
    which produced odd labels such as -20, 80, 180 on bar plots with
    ylim = c(0, 400) (#313).
  • Import the %||% operator from rlang; it is used in geom_bracket() and
    geom_pwc() but base R only provides it since R 4.4, so it could be unresolved
    on the R (>= 4.1) versions the package supports (#665).

Bug fixes

  • stat_welch_anova_test(label = "as_detailed_italic") and
    stat_welch_anova_test(label = "as_detailed_expression") now display the numeric
    F statistic instead of FALSE. Thanks to @erdeyl (#749).
  • ggbarplot() now supports mapping alpha to a discrete grouping variable together
    with a summary (e.g. alpha = clarity, add = "mean_ci", position = position_dodge()).
    Previously this errored at draw time ("alpha * 255": non-numeric argument to binary operator). The mean/CI is now computed per subgroup, the bars are faded per the alpha
    variable, and the error bars are dodged to stay aligned with their bars. Bar plots
    without an alpha grouping variable are unchanged (#404).
  • ggpar() no longer errors on ggsurvplot objects (or any plot whose theme uses
    ggtext::element_markdown(), e.g. survminer's risk-table strata labels). Such
    markdown label elements are now left intact instead of triggering an "Only elements
    of the same class can be merged" error; output for all other plots is unchanged (#382).
  • geom_pwc() with an explicit list of comparisons no longer drops a whole facet/panel
    when a single requested pair cannot be tested (e.g. a group that is entirely NA or has
    fewer than two observations). The untestable comparison is skipped (with a message) and
    the remaining valid comparisons are still drawn (#542).
  • compare_means() and stat_compare_means() now forward extra test options
    (e.g. alternative = "greater") to the paired tests aligned by subject id,
    which were previously ignored on that path. Grouped and faceted paired-id
    comparisons also skip a subset that cannot be tested (fewer than two groups or
    no complete pairs) instead of failing the whole result/layer, while still
    reporting genuinely ambiguous data (e.g. duplicated ids). Thanks to
    @erdeyl (#732).
  • ggbarplot() now places the error bars of a stacked bar chart correctly when the
    data mix positive and negative values (e.g. above/below-ground measurements). The
    stacked error bars are cumulated per sign, matching position_stack(), so a negative
    segment's error bar is drawn on its own side instead of being displaced to the other
    side (previously the misplacement also flipped with the factor-level order). Stacked
    charts with single-sign data are unchanged (#426).
  • ggviolin() now keeps grouped violins aligned with their added box/dot layers by
    default when a sub-group is too sparse for geom_violin() to compute a density
    (a single data point). Previously the sparse sub-group was dropped from the dodge,
    so the remaining violin re-centered and no longer lined up. When drop/position
    are left at their defaults and such a one-point grouped cell is present, the empty
    dodge lane is now reserved automatically. Balanced, ungrouped, faceted, and
    legitimately-unbalanced plots are unchanged, and an explicit drop/position
    still takes precedence (#381).
  • geom_bracket() (and stat_pvalue_manual()) now draw visible tips for a single
    bracket placed over a stat-computed y such as geom_bar()/geom_histogram().
    Previously the bracket tips collapsed into a flat line because the y-axis range
    was trained only on the bracket's single y.position; the tips are now sized
    against the fully-trained panel range at draw time, so they render correctly even
    with coord_cartesian(ylim = ...). Brackets with an already non-zero y range are
    unchanged (#631).
  • ggline() now dodges the jittered points together with the line and error
    bars. Previously, in a grouped line plot with add = "jitter" and
    position = position_dodge(), only the line and summary (e.g. mean_se) were
    dodged while the jittered points stayed centered, so they no longer sat under
    their group. The jitter now dodges by the same width. Plots without a
    position_dodge() (the default position = "identity") are unchanged (#436).
  • ggbarplot() now lines the error bars up with the bars when
    position = position_dodge2() is used. position_dodge2() places elements
    according to their own width, so the thin error bars did not sit on the centre
    of the wider bars they belong to (most visible with preserve = "single" when
    one x group has fewer bars than another). The error bars are now drawn at the
    actual bar positions. Other positions (identity, position_dodge(),
    position_stack()) are unchanged (#363).
  • geom_bracket() and stat_pvalue_manual() now place brackets correctly on a
    transformed y axis (e.g. scale_y_log10()). y.position is given in data units
    but was previously used directly in the scale's transformed space, so brackets
    landed far off (e.g. at 10^30) and squashed the plot. The bracket y.position
    is now run through the scale's transformation; on an untransformed (identity)
    scale this is a no-op, so existing plots are unchanged (#342).
  • The core plotting functions (ggscatter(), ggboxplot(), ggviolin(),
    ggline(), ggbarplot(), gghistogram(), ggdensity(), ggdotplot(),
    ggstripchart(), ggerrorplot(), ggecdf(), ggdotchart(), ggpaired(),
    ggqqplot()) now accept the British spelling colour as an alias for
    color. Previously colour was silently ignored (#317).
  • stat_regline_equation() now displays the correct equation for orthogonal
    polynomial fits such as formula = y ~ poly(x, 2). Previously the orthogonal
    basis coefficients were printed as if they were raw polynomial coefficients,
    giving a wrong equation that did not match the fitted curve. For a simple
    poly(x, k) term with an intercept, the equation is now computed from an
    equivalent raw = TRUE fit (identical curve, R², AIC, BIC). Linear,
    raw = TRUE, and I(x^2) formulas are unaffected; no-intercept models and
    transformed poly arguments (e.g. poly(log(x), 2)) keep their previous
    behavior (#653).
  • ggscatterhist() now aligns the marginal plots with the main scatter plot's
    axes. The margins were built from the raw data, so anything that changed the
    scatter limits (ellipse = TRUE, position jitter, explicit xlim/ylim)
    left the marginal histograms/densities misaligned with the scatter. Each
    margin now inherits the scatter's axis range (#220, #420).
  • geom_pwc() now places comparison brackets over the correct groups when an
    entire x-axis group has only NA values. Such a group is dropped before the
    statistical test runs; previously the surviving groups were renumbered from 1,
    which shifted the remaining brackets left. The bracket positions are now
    anchored to the discrete x scale so they stay aligned with the plotted groups
    (#575).
  • ggballoonplot() now honors user-supplied xlab/ylab instead of always
    blanking the axis titles; axis titles are still blank by default (#639).
  • ggpar(legend.title = ) now also titles size and alpha legends, not just
    colour/fill/linetype/shape (#412).
  • facet()/panel.labs: a NAMED panel.labs vector is now matched to the data
    levels by name, fixing mislabeled panels when the order differed; unnamed
    labels still map positionally (#643).
  • stat_compare_means() no longer fails with a "*.npc coord ..." error when a
    grouped x value is 0 (or negative); .group_coord() now guards against a
    non-positive group index and falls back to the first label coordinate (#594).
  • ggpar()/plot functions now apply xticks.by and yticks.by together; an
    internal else if previously dropped xticks.by whenever yticks.by was
    also supplied (#333).
  • stat_compare_means() now forwards the family argument to the comparison
    bracket labels; it was previously ignored whenever comparisons was set
    (#592, #624).
  • ggline() now supports two grouping variables at once (e.g. color and
    linetype, or an explicit group plus color); these are combined into a
    single interaction group so the right points are connected. Previously this
    errored with "the condition has length > 1" (#616, #375).
  • A NAMED palette vector no longer emits a spurious "No shared levels found
    between names(values) ..." warning; the manual color/fill scale is now only
    applied to an aesthetic actually mapped in the plot. Unnamed palettes are
    unchanged, and a named palette whose names genuinely don't match still warns
    (#642).
  • ggviolin() no longer crashes with scales = "free" (or "free_x"/"free_y").
    The facet argument scales was being partial-matched to the violin scale
    parameter; it is now read with exact matching, so scales controls faceting
    and the violin scale keeps its default (#398).
  • xlim/ylim are now honored with orientation = "horizontal" (and
    rotate = TRUE). The limits are passed to coord_flip() instead of a separate
    coord_cartesian() that was silently replaced, which also removes the
    "Coordinate system already present" warning (#646).
  • ggpar() (and the plot functions) now honor legend.direction for all legend
    positions; it was previously ignored for legend = "top"/"bottom" (#652).
  • compare_means() no longer errors ("Can't extract columns that don't exist")
    when a formula variable name contains a space (e.g. len ~ `spa ced` ); the
    backticks that R adds to non-syntactic term names are now stripped before
    matching data columns (#385).
  • table_cell_font() and table_cell_bg() can now style an individual header
    cell (row = 1), not only body cells; they previously matched only the
    core-* grobs and silently did nothing for the header (#535).
  • ggmaplot() now draws the non-significant ("NS") points behind the significant
    ones, so the up/down-regulated hits are no longer hidden under the grey NS
    cloud; the legend order (Up, Down, NS) is unchanged (#365).
  • ggboxplot() now forwards coef to geom_boxplot(), so coef = 0 can be used
    to omit the whiskers; it was previously dropped by geom_exec() (#517).
  • tab_add_title() / tab_add_footnote(): the just argument now actually
    positions the text — "center"/"right" anchor the title/footnote across the
    table width instead of around a fixed point — and the text is no longer clipped
    when it is wider than the table (#302).
  • annotate_figure(): the figure label (fig.lab) now uses a length-independent
    horizontal justification, so labels of different lengths keep the same anchor
    and captions align across figures; previously a longer label was shifted away
    from the corner (#185).