Skip to content

ggpubr

1.0.0Package record

'ggplot2' Based Publication Ready Plots

229packages dependsuggested by 89
3Mdownloads / year
64.5%test coverage
11/13flavors OK

Overview

About

The 'ggplot2' package is excellent and flexible for elegant data visualization in R. However the default generated plots require some formatting before we can send them for publication. Furthermore, to customize a 'ggplot', the syntax is opaque and this raises the level of difficulty for researchers with no advanced R programming skills. 'ggpubr' provides some easy-to-use functions for creating and customizing 'ggplot2'-based publication ready plots. This version includes modern R ecosystem compatibility updates and customizable p-value formatting presets (APA, AMA, NEJM, Lancet, GraphPad, and scientific notation) for publication workflows, plus robust sparse-subset handling in statistical annotation layers such as 'stat_compare_means()' and 'geom_pwc()', with informative per-group skip diagnostics for non-comparable subsets.

Changes in 1.0.0

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).
Show all changes in 1.0.0Hide the rest of the changes in 1.0.0
  • 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 document

The NEWS section is longer than shown here.

From NEWS.md in the release

Install

Health

CRAN checks
2NOTE11OK
Check messages
NOTERd contentsr-devel-linux-x86_64-debian-clang, r-devel-linux-x86_64-debian-gcc
Rd files without \usage:
  ‘ggpubr_args.Rd’
\arguments should not be documented without \usage.

CRAN check page ↗

Slowest check: 11.9 min · r-oldrel-windows-x86_64
Status changes
  • NOTE11 OK, 2 NOTENow
  • NOTE12 OK, 1 NOTE2026-07-11
  • OK13 OK2026-06-09
  • ERROR12 OK, 1 ERROR2026-06-07
  • OK13 OK2026-06-03
  • ERROR11 OK, 2 ERROR2026-05-25
Show 1 earlier changeHide 1 earlier change
  • OK14 OK2026-03-10
Advisories

none known

Documentation

In release 1.0.0
READMEYes · 443 wordsVignettesNoNEWSYes · 100% structured · mentions 1.0.0Citation fileYes · software entry onlyWebsiteYes
Help pages
93 (9 internal)includes a package overview page
Examples that run
94%78 run in a plain check · 5 never run
Exported functions with examples
96%
Documented parameters
92%
Return-value docs
27%
Help pages citing references
1%

Downloads

3M
CRAN downloads in the past year
Rank #228 · ~8.1K/day · ~247.8K/mo
Daily download trend is not available in this view yet.
206.9K30 days
600.8K90 days
3M1 year
Compare downloads with other packages →
Also on29.9K r2u2 autocran88.9K conda_forge2.2K c2d4u

Repository

1,283Stars
183Forks
5Open issues
1Open PRs
24Releases
1,217Commits
9Commit identities
1,217 commits · Last activity 2026-07-31 · +0.7% stars, 30d · Default branch at 1.1.0, ahead of CRAN 1.0.0 (checked 2026-10-21)
Responsiveness
How these are sampled
PRs merged (all time)
85%
Median days to close, 50 most recently updated closed issues
1,724
Median days to close, 50 most recently updated closed PRs
0

Stars over time

2025-10-17 · 1,2032026-10-22 · 1,283
PracticesBeta

10 development-tooling and community-health practices detected across 7 families in the upstream repository

Checks run against github.com/kassambara/ggpubr on 2026-10-21.

Continuous integration (2)
GitHub ActionsR CMD check workflow

The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel

Governance and community (1)
Issue templates
Docs source (2)
README.Rmdpkgdown
Reproducibility and dev environment (2)
data-raw/Makefile
Show all practicesHide 3 practices
CRAN release process (1)
cran-comments.md
Lint, format, editor (1)
RStudio project
Git structural (1)
.gitattributes
How this is detected·Detection ruleset v4 (2026-10-20)
AI coding toolsBeta

This repository shows signs of AI coding tools.

  • Claude Codefirst seen 2025-12-21
    Named in .Rbuildignore: .claude, CLAUDE.local.md, CLAUDE.md
    Named in .gitignore: .claude, CLAUDE.md
    Commits: at least 10 crediting it in the message
  • Codexfirst seen 2026-07-04
    Named in .Rbuildignore: .codex
    Named in .gitignore: .codex
Models named in its commits
  • Claude Opus 4.56 commits · Dec 2025
  • Claude Opus 4.62 commits · Feb 2026
Counts are commits, not lines of code. Commits by a tool's accounts count once across all of its accounts. Commits crediting it are the largest count among its credit lines, a minimum.How this is detected·Detection ruleset v4 (updated 2026-09-27)

Dependencies

Declared dependencies
20 external dependencies (excludes base and recommended)

Installing ggpubr adds 67 packages (33 with compiled code, 62 MB of source tarballs). Heaviest: stringi 11 MB, ggplot2 6.0 MB, doBy 4.3 MB.

Depends (2)
R >= 4.1.0ggplot2
LinkingTo (0)
none
Suggests (5)
Enhances (0)
none

Datasets

People & History

People (2)
Author, Maintainer since 0.1.0 (2016)
Contributor · added in 0.6.3
Faculty of Economics and Business, University of Debrecen, Hungary
Releases (24)

Pick two releases to compare their code metrics.

  • 1.0.0Latest
    record
    2026-07-06 · current release · diff ↗ ·
    added 5 exports
    clean_lock_files, create_p_label, format_p_value, get_p_format_style, list_p_format_styles
  • R
    R 4.6.0 released · 2026-04-24
  • 0.6.3
    record
    2026-02-24 · diff ↗
  • 0.6.2
    record
    2025-10-17 · diff ↗
  • 0.6.1
    record
    2025-06-27 · diff ↗
  • R
    R 4.5.0 released · 2025-04-11
  • R
    R 4.4.0 released · 2024-04-24
  • R
    R 4.3.0 released · 2023-04-21
  • 0.6.0
    record
    2023-02-10 · diff ↗ ·
    added 1 export
    ggadjust_pvalue
  • 0.5.0
    record
    2022-11-16 · diff ↗ ·
    added 12 exports
    as_npc, as_npcx, as_npcy, geom_pwc, get_coord, ggpubr_options, npc_to_data_coord, stat_anova_test, stat_friedman_test, stat_kruskal_test, stat_pwc, stat_welch_anova_test
  • R
    R 4.2.0 released · 2022-04-22
  • R
    R 4.1.0 released · 2021-05-18
  • 0.4.0
    record
    2020-06-27 · diff ↗ ·
    added 14 exports
    create_aes, get_breaks, median_hilow_, median_q1q3, tab_add_border, tab_add_footnote, tab_add_hline, tab_add_title, tab_add_vline, tab_cell_crossout, tab_ncol, tab_nrow, tbody_add_border, thead_add_border
  • 0.3.0
    record
    2020-05-04 · diff ↗ ·
    added 4 exports
    clean_table_theme, get_summary_stats, ggsummarystats, ggsummarytable
  • R
    R 4.0.0 released · 2020-04-24
  • 0.2.5
    record
    2020-02-13 · diff ↗
Show 19 earlier eventsHide 19 earlier events
  • 0.2.4
    record
    2019-11-14 · diff ↗
  • 0.2.3
    record
    2019-09-04 · diff ↗ ·
    added 2 exports
    stat_central_tendency, stat_overlay_normal_density
  • 0.2.2
    record
    2019-08-07 · diff ↗ ·
    added 2 exports
    geom_bracket, stat_bracket
  • 0.2.1
    record
    2019-06-23 · diff ↗ ·
    added 1 export
    geom_signif
  • R
    R 3.6.0 released · 2019-04-26
  • 0.2
    record
    2018-11-15 · diff ↗
  • 0.1.9
    record
    2018-11-11 · diff ↗
  • 0.1.8
    record
    2018-08-30 · diff ↗ ·
    added 5 exports
    %>%, group_by, mutate, stat_pvalue_manual, stat_regline_equation
  • 0.1.7
    record
    2018-06-23 · diff ↗ ·
    added 1 export
    ggdonutchart
  • R
    R 3.5.0 released · 2018-04-23
  • 0.1.6
    record
    2017-11-14 · diff ↗ ·
    added 5 exports
    ggballoonplot, ggscatterhist, table_cell_bg, table_cell_font, theme_pubclean
  • 0.1.5
    record
    2017-08-22 · diff ↗
  • 0.1.4
    record
    2017-06-28 · diff ↗ ·
    added 17 exports
    annotate_figure, as_ggplot, background_image, clean_theme, colnames_style, color_palette, fill_palette, get_legend, ggparagraph, ggtexttable, gradient_color, gradient_fill, rownames_style, tbody_style, text_grob, theme_transparent, ttheme
  • 0.1.3
    record
    2017-06-06 · diff ↗ ·
    added 30 exports
    add_summary, bgcolor, border, change_palette, compare_means, facet, font, ggadd, ggarrange, ggexport, ggpaired, grids, mean_ci, mean_range, mean_sd, mean_se_, median_iqr, median_mad, median_range, rotate, rotate_x_text, rotate_y_text, rremove, set_palette, stat_compare_means, stat_cor, stat_stars, theme_cleveland, xscale, yscale
  • R
    R 3.4.0 released · 2017-04-21
  • 0.1.2
    record
    2017-03-14 · diff ↗ ·
    added 1 export
    get_palette
  • 0.1.1
    record
    2016-12-05 · diff ↗ ·
    added 3 exports
    geom_exec, ggtext, theme_classic2
  • 0.1.0
    record
    2016-07-20
  • R
    R 3.3.0 released · 2016-05-03

Package metadata

First published
2016-07-20
Total releases
24 / 10 yrs
License
GPL (>= 2) OSI
Minimum R
≥ 4.1.0
Bundled data
779 KB / 3 files
Download size
1.8 MB

Cite

Cite this package

Run in R for the authors' entry:

citation("ggpubr")

The package provides a citation file, so citation() returns the authors' own entry.

Cite the R Observatory

For a number measured here: a download total, a coverage figure, an archival date.

APA

Balamuta, J. J. (2026). R Observatory: Metrics for ggpubr version 1.0.0 [Data set]. HJJB, LLC. Data release v2026-10-24. https://doi.org/10.5281/zenodo.21843040

From data release v2026-10-24, which the citation names so these numbers can be found later. More on citing and the projects behind them.

Report a problem with this page →

Privacy choices

These apply to this browser and are stored on this device only. Nothing about your choice is sent to us.

Read the privacy policy