├── .github ├── .gitignore ├── workflows │ ├── test-coverage.yaml │ ├── pkgdown.yaml │ ├── lint.yml │ └── check-full.yaml └── CODE_OF_CONDUCT.md ├── revdep ├── failures.md ├── problems.md ├── .gitignore ├── cran.md └── README.md ├── R ├── ggsignif-package.R ├── utilities.R └── geom_signif.R ├── paper ├── paper.pdf ├── paper_files │ └── figure-latex │ │ └── simpe_comparison-1.pdf ├── paper.bib ├── paper.md ├── paper.Rmd ├── arxiv.sty ├── paper.tex └── apa.csl ├── tests ├── testthat │ ├── Rplots.pdf │ ├── test-parsing.R │ ├── test-geom_signific_strict.R │ ├── _snaps │ │ ├── errors.md │ │ ├── geom_signific_strict.md │ │ └── vdiffr │ │ │ ├── identical-annotations.svg │ │ │ ├── geom-works-with-identity.svg │ │ │ └── flipping-aesthetics-works.svg │ ├── test-errors.R │ ├── test-vdiffr.R │ └── test-geom_signific_basic.R └── testthat.R ├── pkgdown ├── favicon │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ └── apple-touch-icon-180x180.png └── _pkgdown.yml ├── NAMESPACE ├── man ├── figures │ ├── README-coord_flip-1.png │ ├── README-faceted_simple-1.png │ ├── README-dodge_comparison-1.png │ ├── README-simpe_comparison-1.png │ ├── README-faceted_comparison-1.png │ └── README-orientation_argument-1.png ├── ggsignif-package.Rd └── stat_signif.Rd ├── .gitattributes ├── inst ├── WORDLIST └── CITATION ├── cran-comments.md ├── ggsignif.Rproj ├── .lintr ├── .Rbuildignore ├── .gitignore ├── WIP └── experiments.R ├── Makefile ├── DESCRIPTION ├── NEWS.md ├── vignettes └── intro.Rmd ├── CITATION.cff ├── codemeta.json ├── README.Rmd ├── README.md └── LICENSE.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /revdep/failures.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | *Wow, no problems at all. :)* -------------------------------------------------------------------------------- /R/ggsignif-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | -------------------------------------------------------------------------------- /paper/paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/paper/paper.pdf -------------------------------------------------------------------------------- /tests/testthat/Rplots.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/tests/testthat/Rplots.pdf -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /revdep/.gitignore: -------------------------------------------------------------------------------- 1 | checks 2 | library 3 | checks.noindex 4 | library.noindex 5 | data.sqlite 6 | *.html 7 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(geom_signif) 4 | export(stat_signif) 5 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(ggsignif) 3 | 4 | library(ggplot2) 5 | 6 | test_check("ggsignif") 7 | -------------------------------------------------------------------------------- /man/figures/README-coord_flip-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/man/figures/README-coord_flip-1.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /man/figures/README-faceted_simple-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/man/figures/README-faceted_simple-1.png -------------------------------------------------------------------------------- /man/figures/README-dodge_comparison-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/man/figures/README-dodge_comparison-1.png -------------------------------------------------------------------------------- /man/figures/README-simpe_comparison-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/man/figures/README-simpe_comparison-1.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /man/figures/README-faceted_comparison-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/man/figures/README-faceted_comparison-1.png -------------------------------------------------------------------------------- /man/figures/README-orientation_argument-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/man/figures/README-orientation_argument-1.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /paper/paper_files/figure-latex/simpe_comparison-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/const-ae/ggsignif/HEAD/paper/paper_files/figure-latex/simpe_comparison-1.pdf -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/* linguist-documentation 2 | man/* linguist-documentation 3 | misc/* linguist-documentation 4 | pkgdown/* linguist-documentation 5 | *.html linguist-documentation 6 | -------------------------------------------------------------------------------- /revdep/cran.md: -------------------------------------------------------------------------------- 1 | ## revdepcheck results 2 | 3 | We checked 7 reverse dependencies (3 from CRAN + 4 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. 4 | 5 | * We saw 0 new problems 6 | * We failed to check 0 packages 7 | 8 | -------------------------------------------------------------------------------- /tests/testthat/test-parsing.R: -------------------------------------------------------------------------------- 1 | test_that("parsing works properly", { 2 | expr <- parse_safe(c("gamma", "", "alpha")) 3 | 4 | expect_identical(class(expr), "expression") 5 | expect_equal(expr, expression(gamma, NA, alpha)) 6 | expect_type(expr, "expression") 7 | }) 8 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | Ahlmann 2 | CRAN's 3 | Codecov 4 | Eltze 5 | NA’ 6 | ORCID 7 | README 8 | ae 9 | behaviour 10 | const 11 | coord 12 | ggplot 13 | ggplots 14 | github 15 | https 16 | io 17 | lifecycle 18 | linetype 19 | md 20 | multiway 21 | patilindrajeets 22 | plotmath 23 | signif 24 | textsize 25 | wilcox 26 | xmax 27 | xmin 28 | ’ 29 | -------------------------------------------------------------------------------- /pkgdown/_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://const-ae.github.io/ggsignif/ 2 | 3 | template: 4 | bootstrap: 5 5 | bootswatch: flatly 6 | bslib: 7 | code_font: { google: "JetBrains Mono" } 8 | 9 | authors: 10 | Constantin Ahlmann-Eltze: 11 | href: https://twitter.com/const_ae 12 | Indrajeet Patil: 13 | href: https://sites.google.com/site/indrajeetspatilmorality/ 14 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## R CMD check results 2 | 3 | 0 errors | 0 warnings | 0 note 4 | 5 | * Gets rid of `NOTE` in CRAN checks. 6 | 7 | ## revdepcheck results 8 | 9 | We checked 7 reverse dependencies (3 from CRAN + 4 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package. 10 | 11 | * We saw 0 new problems 12 | * We failed to check 0 packages 13 | -------------------------------------------------------------------------------- /R/utilities.R: -------------------------------------------------------------------------------- 1 | #' Parse takes a vector of n lines and returns m expressions. 2 | #' See https://github.com/tidyverse/ggplot2/issues/2864 for discussion. 3 | #' 4 | #' @noRd 5 | parse_safe <- function(text) { 6 | stopifnot(is.character(text)) 7 | out <- vector("expression", length(text)) 8 | for (i in seq_along(text)) { 9 | expr <- parse(text = text[[i]]) 10 | out[[i]] <- if (length(expr) == 0) NA else expr[[1]] 11 | } 12 | out 13 | } 14 | -------------------------------------------------------------------------------- /ggsignif.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: No 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | 23 | QuitChildProcessesOnExit: Yes 24 | DisableExecuteRprofile: Yes 25 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | citHeader("To cite 'ggsignif' in publications use:") 2 | 3 | citEntry( 4 | entry = "Article", 5 | title = "{ggsignif}: R Package for Displaying Significance Brackets for {'ggplot2'}", 6 | author = c(person("Ahlmann-Eltze", "Constantin"), person("Indrajeet", "Patil")), 7 | year = 2021, 8 | journal = "PsyArxiv", 9 | url = "https://psyarxiv.com/7awm6", 10 | doi = "10.31234/osf.io/7awm6", 11 | textVersion = "Ahlmann-Eltze, C., & Patil, I. (2021). ggsignif: R Package for Displaying Significance Brackets for 'ggplot2'. PsyArxiv. doi:10.31234/osf.io/7awm6" 12 | ) 13 | -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: all_linters( 2 | absolute_path_linter = NULL, 3 | assignment_linter = NULL, 4 | commented_code_linter = NULL, 5 | cyclocomp_linter(25L), 6 | if_not_else_linter(exceptions = character(0L)), 7 | implicit_integer_linter = NULL, 8 | library_call_linter = NULL, 9 | line_length_linter(120L), 10 | namespace_linter = NULL, 11 | nonportable_path_linter = NULL, 12 | object_name_linter = NULL, 13 | object_length_linter(50L), 14 | object_usage_linter = NULL, 15 | todo_comment_linter = NULL, 16 | string_boundary_linter = NULL, 17 | strings_as_factors_linter = NULL, 18 | undesirable_function_linter = NULL, 19 | undesirable_operator_linter = NULL, 20 | unnecessary_concatenation_linter(allow_single_expression = FALSE), 21 | unused_import_linter = NULL 22 | ) 23 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - uses: r-lib/actions/setup-r@v2 21 | with: 22 | use-public-rspm: true 23 | 24 | - uses: r-lib/actions/setup-r-dependencies@v2 25 | with: 26 | extra-packages: r-lib/covr 27 | needs: coverage 28 | 29 | - name: Test coverage 30 | run: covr::codecov() 31 | shell: Rscript {0} 32 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^\cache$ 2 | ^codemeta\.json$ 3 | ^Meta$ 4 | ^doc$ 5 | ^.*\.Rproj$ 6 | ^\.Rproj\.user$ 7 | ^README\.Rmd$ 8 | ^Rplots.pdf$ 9 | ^README-.*\.png$ 10 | ^CONDUCT\.md$ 11 | ^SECURITY\.md$ 12 | ^cran-comments\.md$ 13 | ^CODE_OF_CONDUCT\.md$ 14 | ^SUPPORT\.md$ 15 | ^\.github$ 16 | ^NEWS$ 17 | ^docs$ 18 | ^revdep$ 19 | publication/* 20 | ^codecov\.yml$ 21 | ^\.coveralls\.yml$ 22 | ^\.travis\.yml$ 23 | ^_pkgdown\.yml$ 24 | ^appveyor\.yml$ 25 | ^.gitlab-ci\.yml$ 26 | ^data-raw$ 27 | ^pkgdown$ 28 | ^\.httr-oauth$ 29 | ^CRAN-RELEASE$ 30 | tests\^spelling 31 | ^LICENSE\.md$ 32 | ^\.lintr$ 33 | ^\.circleci$ 34 | ^tests/manual$ 35 | ^\.covrignore$ 36 | ^\.github/ISSUE_TEMPLATE$ 37 | ^paper.*$ 38 | references.bib 39 | ^API$ 40 | ^\.pre-commit-config\.yaml$ 41 | ^\.github/workflows/R\.yaml$ 42 | ^\.github/workflows/pr-commands\.yaml$ 43 | hextools 44 | ^WIP/. 45 | ^Makefile$ 46 | ^README\.md$ 47 | ^\.Rproj$ 48 | ^CITATION\.cff$ 49 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | |field |value | 4 | |:--------|:-----------------------------------------| 5 | |version |R version 4.2.1 (2022-06-23) | 6 | |os |macOS Monterey 12.6 | 7 | |system |aarch64, darwin20 | 8 | |ui |RStudio | 9 | |language |(EN) | 10 | |collate |en_US.UTF-8 | 11 | |ctype |en_US.UTF-8 | 12 | |tz |Europe/Berlin | 13 | |date |2022-10-10 | 14 | |rstudio |2022.07.1+554 Spotted Wakerobin (desktop) | 15 | |pandoc |2.19.2 @ /usr/local/bin/ (via rmarkdown) | 16 | 17 | # Dependencies 18 | 19 | |package |old |new |Δ | 20 | |:--------|:-----|:-----|:--| 21 | |ggsignif |0.6.3 |0.6.4 |* | 22 | 23 | # Revdeps 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Meta 2 | doc 3 | # Windows image file caches 4 | Thumbs.db 5 | ehthumbs.db 6 | 7 | # Folder config file 8 | Desktop.ini 9 | # Recycle Bin used on file shares 10 | $RECYCLE.BIN/ 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | # Windows shortcuts 17 | *.lnk 18 | # ========================= 19 | # Operating System Files 20 | # OSX 21 | .DS_Store 22 | .AppleDouble 23 | .LSOverride 24 | # Thumbnails 25 | ._* 26 | # Files that might appear in the root of a volume 27 | .DocumentRevisions-V100 28 | .fseventsd 29 | .Spotlight-V100 30 | .TemporaryItems 31 | .Trashes 32 | .VolumeIcon.icns 33 | # Directories potentially created on remote AFP share 34 | .AppleDB 35 | .AppleDesktop 36 | Network Trash Folder 37 | Temporary Items 38 | .apdisk 39 | # R Studio files 40 | .Rproj.user 41 | .Rhistory 42 | .RData 43 | .Ruserdata 44 | inst/doc 45 | .httr-oauth 46 | revdep/checks 47 | revdep/library 48 | revdep/checks.noindex 49 | revdep/library.noindex 50 | revdep/data.sqlite 51 | /doc/ 52 | /Meta/ 53 | tests/testthat/Rplots.pdf 54 | -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @incollection{Wilkinson2012, 2 | title={The grammar of graphics}, 3 | author={Wilkinson, Leland}, 4 | booktitle={Handbook of computational statistics}, 5 | pages={375--414}, 6 | year={2012}, 7 | publisher={Springer} 8 | } 9 | @Book{Wickham2016, 10 | author = {Hadley Wickham}, 11 | title = {{ggplot2}: Elegant Graphics for Data Analysis}, 12 | publisher = {Springer-Verlag New York}, 13 | year = {2016}, 14 | isbn = {978-3-319-24277-4}, 15 | url = {https://ggplot2.tidyverse.org}, 16 | } 17 | 18 | @Manual{Kassambara2020, 19 | title = {{ggpubr}: '{ggplot2}' Based Publication Ready Plots}, 20 | author = {Alboukadel Kassambara}, 21 | year = {2020}, 22 | note = {R package version 0.4.0}, 23 | url = {https://CRAN.R-project.org/package=ggpubr}, 24 | } 25 | 26 | @Article{Patil2018, 27 | title = {{ggstatsplot}: '{ggplot2}' Based Plots with Statistical Details}, 28 | author = {Indrajeet Patil}, 29 | year = {2018}, 30 | journal = {CRAN}, 31 | url = {https://CRAN.R-project.org/package=ggstatsplot}, 32 | doi = {10.5281/zenodo.2074621}, 33 | } 34 | -------------------------------------------------------------------------------- /tests/testthat/test-geom_signific_strict.R: -------------------------------------------------------------------------------- 1 | test_that("the plotting works - strict test", { 2 | library(ggplot2) 3 | 4 | dat <- data.frame( 5 | Group = c("S1", "S1", "S2", "S2"), 6 | Sub = c("A", "B", "A", "B"), 7 | Value = c(3, 5, 7, 8) 8 | ) 9 | 10 | p <- ggplot(dat, aes(Group, Value)) + 11 | geom_bar(aes(fill = Sub), 12 | stat = "identity", 13 | position = "dodge", 14 | width = .5 15 | ) + 16 | geom_signif( 17 | stat = "identity", 18 | data = data.frame( 19 | m = c(0.875, 1.875), 20 | xend = c(1.125, 2.125), 21 | n = c(5.8, 8.5), 22 | annotation = c("***", "NS") 23 | ), 24 | aes( 25 | x = m, 26 | xend = xend, 27 | y = n, 28 | yend = n, 29 | annotation = annotation 30 | ) 31 | ) + 32 | geom_signif( 33 | comparisons = list(c("S1", "S2")), 34 | annotations = "***", 35 | y_position = 9.3, 36 | tip_length = 0 37 | ) + 38 | scale_fill_manual(values = c("grey80", "grey20")) 39 | 40 | pb <- ggplot_build(p) 41 | 42 | expect_snapshot(pb$data) 43 | }) 44 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/errors.md: -------------------------------------------------------------------------------- 1 | # should not work 2 | 3 | Problem while computing stat. 4 | i Error occurred in the 2nd layer. 5 | Caused by error in `setup_params()`: 6 | ! Can only handle data with groups that are plotted on the x-axis 7 | 8 | --- 9 | 10 | Problem while computing stat. 11 | i Error occurred in the 2nd layer. 12 | Caused by error in `setup_params()`: 13 | ! Can only handle data with groups that are plotted on the x-axis 14 | 15 | --- 16 | 17 | object 'airquality_trimmed' not found 18 | 19 | --- 20 | 21 | Manual mode only works if with 'annotations' is provided in mapping 22 | 23 | --- 24 | 25 | If manual mode is selected you need to provide the data and mapping parameters 26 | 27 | --- 28 | 29 | Problem while computing stat. 30 | i Error occurred in the 1st layer. 31 | Caused by error in `setup_params()`: 32 | ! Can only handle data with groups that are plotted on the x-axis 33 | 34 | --- 35 | 36 | Manual mode only works if with 'annotations' is provided in mapping 37 | 38 | --- 39 | 40 | Manual mode only works if with 'annotations' is provided in mapping 41 | 42 | -------------------------------------------------------------------------------- /WIP/experiments.R: -------------------------------------------------------------------------------- 1 | # StatLm <- ggproto("StatLm", Stat, 2 | # required_aes = c("x", "y"), 3 | # 4 | # compute_group = function(data, scales, params, n = 100, formula = y ~ x) { 5 | # rng <- range(data$x, na.rm = TRUE) 6 | # grid <- data.frame(x = seq(rng[1], rng[2], length = n)) 7 | # 8 | # mod <- lm(formula, data = data) 9 | # grid$y <- predict(mod, newdata = grid) 10 | # 11 | # grid 12 | # } 13 | # ) 14 | # 15 | # stat_lm <- function(mapping = NULL, data = NULL, geom = "line", 16 | # position = "identity", na.rm = FALSE, show.legend = NA, 17 | # inherit.aes = TRUE, n = 50, formula = y ~ x, 18 | # ...) { 19 | # layer( 20 | # stat = StatLm, data = data, mapping = mapping, geom = geom, 21 | # position = position, show.legend = show.legend, inherit.aes = inherit.aes, 22 | # params = list(n = n, formula = formula, na.rm = na.rm, ...) 23 | # ) 24 | # } 25 | # 26 | # ggplot(mpg, aes(displ, hwy)) + 27 | # geom_point() + 28 | # stat_lm(formula = y ~ poly(x, 10), n=3) + 29 | # stat_lm(formula = y ~ poly(x, 10), geom = "point", colour = "red", n = 20) 30 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | env: 18 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: r-lib/actions/setup-pandoc@v2 23 | 24 | - uses: r-lib/actions/setup-r@v2 25 | with: 26 | use-public-rspm: true 27 | 28 | - uses: r-lib/actions/setup-r-dependencies@v2 29 | with: 30 | extra-packages: r-lib/pkgdown, local::. 31 | needs: website 32 | 33 | - name: Build site 34 | run: Rscript -e 'pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)' 35 | 36 | - name: Deploy to GitHub pages 🚀 37 | if: github.event_name != 'pull_request' 38 | uses: JamesIves/github-pages-deploy-action@v4.6.0 39 | with: 40 | clean: false 41 | branch: gh-pages 42 | folder: docs 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKGNAME := $(shell sed -n "s/Package: *\([^ ]*\)/\1/p" DESCRIPTION) 2 | PKGVERS := $(shell sed -n "s/Version: *\([^ ]*\)/\1/p" DESCRIPTION) 3 | PKGSRC := $(shell basename `pwd`) 4 | 5 | all: rd check1 clean 6 | 7 | rd: 8 | Rscript -e 'library(methods);devtools::document()' 9 | 10 | readme: 11 | Rscript -e 'rmarkdown::render("README.Rmd", encoding="UTF-8")' 12 | 13 | build1: 14 | Rscript -e 'devtools::build()' 15 | 16 | build2: 17 | Rscript -e 'devtools::build(vignettes = FALSE)' 18 | 19 | install: 20 | cd ..;\ 21 | R CMD INSTALL $(PKGNAME)_$(PKGVERS).tar.gz 22 | 23 | check1: rd build1 24 | cd ..;\ 25 | Rscript -e 'rcmdcheck::rcmdcheck("$(PKGNAME)_$(PKGVERS).tar.gz")' 26 | 27 | crancheck: rd build1 28 | cd ..;\ 29 | Rscript -e 'rcmdcheck::rcmdcheck("$(PKGNAME)_$(PKGVERS).tar.gz", args="--as-cran")' 30 | 31 | debug: rd build1 install 32 | 33 | clean: 34 | cd ..;\ 35 | $(RM) -r $(PKGNAME).Rcheck/ 36 | 37 | clean2: 38 | cd ..;\ 39 | $(RM) $(PKGNAME)_$(PKGVERS).tar.gz 40 | 41 | bignore: 42 | Rscript -e 'usethis::use_build_ignore(c("Makefile", "README.md", "README.Rmd", "CONDUCT.md", ".Rproj.user", ".Rproj"))' 43 | 44 | gignore: 45 | Rscript -e 'usethis::use_git_ignore(c(".DS_Store", ".RData", ".Rhistory", ".Rproj.user"))' 46 | 47 | update: 48 | git fetch --all;\ 49 | git checkout main;\ 50 | git merge origin/main 51 | 52 | push: 53 | git push origin main 54 | -------------------------------------------------------------------------------- /man/ggsignif-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ggsignif-package.R 3 | \docType{package} 4 | \name{ggsignif-package} 5 | \alias{ggsignif} 6 | \alias{ggsignif-package} 7 | \title{ggsignif: Significance Brackets for 'ggplot2'} 8 | \description{ 9 | Enrich your 'ggplots' with group-wise comparisons. This package provides an easy way to indicate if two groups are significantly different. Commonly this is shown by a bracket on top connecting the groups of interest which itself is annotated with the level of significance (NS, *, **, ***). The package provides a single layer (geom_signif()) that takes the groups for comparison and the test (t.test(), wilcox.text() etc.) as arguments and adds the annotation to the plot. 10 | } 11 | \seealso{ 12 | Useful links: 13 | \itemize{ 14 | \item \url{https://const-ae.github.io/ggsignif/} 15 | \item \url{https://github.com/const-ae/ggsignif} 16 | \item Report bugs at \url{https://github.com/const-ae/ggsignif/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Constantin Ahlmann-Eltze \email{artjom31415@googlemail.com} (\href{https://orcid.org/0000-0002-3762-068X}{ORCID}) (@const_ae) [contributor] 22 | 23 | Authors: 24 | \itemize{ 25 | \item Indrajeet Patil \email{patilindrajeet.science@gmail.com} (\href{https://orcid.org/0000-0003-1995-6531}{ORCID}) (@patilindrajeets) [contributor] 26 | } 27 | 28 | } 29 | \keyword{internal} 30 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: lint 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - uses: r-lib/actions/setup-r@v2 20 | with: 21 | use-public-rspm: true 22 | 23 | - uses: r-lib/actions/setup-r-dependencies@v2 24 | with: 25 | extra-packages: | 26 | r-lib/lintr 27 | local::. 28 | needs: lint 29 | 30 | # TODO: Revisit to remove some of these allowances after more important lints 31 | # have been removed. 32 | - name: Lint 33 | run: | 34 | library(lintr) 35 | lint_package(linters = linters_with_defaults( 36 | commented_code_linter = NULL, 37 | cyclocomp_linter = cyclocomp_linter(25), 38 | implicit_integer_linter = NULL, 39 | line_length_linter(120), 40 | object_name_linter = NULL, 41 | object_length_linter(50), 42 | object_usage_linter = NULL, 43 | todo_comment_linter = NULL, 44 | extraction_operator_linter = NULL, 45 | defaults = linters_with_tags(tags = NULL) 46 | )) 47 | shell: Rscript {0} 48 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: ggsignif 3 | Title: Significance Brackets for 'ggplot2' 4 | Version: 0.6.4.9000 5 | Authors@R: 6 | c( 7 | person(given = "Constantin", 8 | family = "Ahlmann-Eltze", 9 | role = c("aut", "cre", "ctb"), 10 | email = "artjom31415@googlemail.com", 11 | comment = c(ORCID = "0000-0002-3762-068X", Twitter = "@const_ae")), 12 | person(given = "Indrajeet", 13 | family = "Patil", 14 | role = c("aut", "ctb"), 15 | email = "patilindrajeet.science@gmail.com", 16 | comment = c(ORCID = "0000-0003-1995-6531", Twitter = "@patilindrajeets")) 17 | ) 18 | Description: Enrich your 'ggplots' with group-wise comparisons. 19 | This package provides an easy way to indicate if two groups are 20 | significantly different. Commonly this is shown by a bracket on top 21 | connecting the groups of interest which itself is annotated with the 22 | level of significance (NS, *, **, ***). The package provides a single 23 | layer (geom_signif()) that takes the groups for comparison and the 24 | test (t.test(), wilcox.text() etc.) as arguments and adds the 25 | annotation to the plot. 26 | License: GPL-3 | file LICENSE 27 | URL: https://const-ae.github.io/ggsignif/, 28 | https://github.com/const-ae/ggsignif 29 | BugReports: https://github.com/const-ae/ggsignif/issues 30 | VignetteBuilder: 31 | knitr 32 | Encoding: UTF-8 33 | Language: en-US 34 | Depends: 35 | R (>= 3.6.0) 36 | Imports: 37 | ggplot2 (>= 3.5.0) 38 | Suggests: 39 | knitr, 40 | rmarkdown, 41 | scales, 42 | testthat, 43 | vdiffr (>= 1.0.7) 44 | Roxygen: list(markdown = TRUE, roclets = c("rd", "namespace", "collate")) 45 | RoxygenNote: 7.3.1 46 | Config/testthat/edition: 3 47 | Config/testthat/parallel: true 48 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/geom_signific_strict.md: -------------------------------------------------------------------------------- 1 | # the plotting works - strict test 2 | 3 | Code 4 | pb$data 5 | Output 6 | [[1]] 7 | fill x y PANEL group flipped_aes ymin ymax xmin xmax colour linewidth 8 | 1 grey80 0.875 3 1 1 FALSE 0 3 0.75 1.00 NA 0.5 9 | 2 grey20 1.125 5 1 3 FALSE 0 5 1.00 1.25 NA 0.5 10 | 3 grey80 1.875 7 1 2 FALSE 0 7 1.75 2.00 NA 0.5 11 | 4 grey20 2.125 8 1 4 FALSE 0 8 2.00 2.25 NA 0.5 12 | linetype alpha 13 | 1 1 NA 14 | 2 1 NA 15 | 3 1 NA 16 | 4 1 NA 17 | 18 | [[2]] 19 | x y xend yend annotation PANEL group shape colour textsize angle hjust 20 | 1 0.875 5.8 1.125 5.8 *** 1 1 19 black 3.88 0 0.5 21 | 2 1.875 8.5 2.125 8.5 NS 1 2 19 black 3.88 0 0.5 22 | vjust alpha family fontface lineheight linetype size 23 | 1 0 NA 1 1.2 1 0.5 24 | 2 0 NA 1 1.2 1 0.5 25 | 26 | [[3]] 27 | x xend y yend annotation group flipped_aes PANEL shape colour textsize 28 | 1 1 1 9.575 9.575 *** S1-S2-1 FALSE 1 19 black 3.88 29 | 2 1 2 9.575 9.575 *** S1-S2-1 FALSE 1 19 black 3.88 30 | 3 2 2 9.575 9.575 *** S1-S2-1 FALSE 1 19 black 3.88 31 | angle hjust vjust alpha family fontface lineheight linetype size 32 | 1 0 0.5 0 NA 1 1.2 1 0.5 33 | 2 0 0.5 0 NA 1 1.2 1 0.5 34 | 3 0 0.5 0 NA 1 1.2 1 0.5 35 | 36 | 37 | -------------------------------------------------------------------------------- /.github/workflows/check-full.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [main, master] 4 | pull_request: 5 | branches: [main, master] 6 | 7 | name: R-CMD-check 8 | 9 | jobs: 10 | R-CMD-check: 11 | runs-on: ${{ matrix.config.os }} 12 | 13 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | config: 19 | # CRAN doesn't provide binaries for macOS on R-devel, so skip because 20 | # it will be too slow otherwise 21 | #- {os: macOS-latest, r: 'devel'} 22 | - { os: macOS-latest, r: "release" } 23 | 24 | - { os: windows-latest, r: "devel" } 25 | - { os: windows-latest, r: "release" } 26 | - { os: windows-latest, r: "oldrel" } 27 | # use 4.1 to check with rtools40's older compiler 28 | - { os: windows-latest, r: "4.1" } 29 | 30 | #- { os: ubuntu-latest, r: "next" } 31 | - { os: ubuntu-latest, r: "devel" } 32 | - { os: ubuntu-latest, r: "release" } 33 | - { os: ubuntu-latest, r: "4.3" } 34 | - { os: ubuntu-latest, r: "4.2" } 35 | - { os: ubuntu-latest, r: "4.1" } 36 | - { os: ubuntu-latest, r: "4.0" } 37 | - { os: ubuntu-latest, r: "3.6" } 38 | 39 | env: 40 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 41 | R_KEEP_PKG_SOURCE: yes 42 | 43 | steps: 44 | - uses: actions/checkout@v4 45 | 46 | - uses: r-lib/actions/setup-pandoc@v2 47 | 48 | - uses: r-lib/actions/setup-r@v2 49 | with: 50 | r-version: ${{ matrix.config.r }} 51 | http-user-agent: ${{ matrix.config.http-user-agent }} 52 | use-public-rspm: true 53 | 54 | - uses: r-lib/actions/setup-r-dependencies@v2 55 | with: 56 | extra-packages: any::rcmdcheck 57 | needs: check 58 | 59 | - uses: r-lib/actions/check-r-package@v2 60 | with: 61 | upload-snapshots: true 62 | -------------------------------------------------------------------------------- /tests/testthat/test-errors.R: -------------------------------------------------------------------------------- 1 | test_that("should not work", { 2 | library(ggplot2) 3 | 4 | expect_snapshot_error( 5 | print(ggplot(mpg, aes(x = manufacturer, y = displ)) + 6 | geom_boxplot() + 7 | stat_signif( 8 | comparisons = list(c("audi", "ford"), c("hyundai", "nissan")), 9 | map_signif_level = TRUE, 10 | inherit.aes = FALSE 11 | )) 12 | ) 13 | 14 | expect_snapshot_error( 15 | print(ggplot(mpg, aes(x = manufacturer, y = displ)) + 16 | geom_boxplot() + 17 | geom_signif( 18 | comparisons = list(c("audi", "ford"), c("hyundai", "nissan")), 19 | map_signif_level = TRUE, 20 | inherit.aes = FALSE 21 | )) 22 | ) 23 | 24 | expect_snapshot_error( 25 | print(ggplot(airquality_trimmed, aes(x = Month, y = Ozone, fill = Temp.f)) + 26 | geom_boxplot(alpha = 0.7, width = 0.4) + 27 | geom_signif( 28 | y_position = 150, xmin = c(0.9), xmax = c(1.1, 2.1), 29 | annotations = c("***", "NS") 30 | )) 31 | ) 32 | 33 | expect_snapshot_error( 34 | print(ggplot(iris, aes(x = Species, y = Sepal.Length)) + 35 | geom_boxplot() + 36 | geom_signif( 37 | comparisons = list(c("versicolor", "virginica")), 38 | map_signif_level = TRUE, 39 | manual = TRUE 40 | )) 41 | ) 42 | 43 | expect_snapshot_error( 44 | print(ggplot() + 45 | stat_signif( 46 | manual = TRUE, 47 | inherit.aes = FALSE 48 | )) 49 | ) 50 | 51 | expect_snapshot_error( 52 | print(ggplot() + 53 | stat_signif( 54 | data = mtcars, 55 | mapping = aes("x", "y"), 56 | manual = TRUE, 57 | inherit.aes = FALSE 58 | )) 59 | ) 60 | 61 | expect_snapshot_error( 62 | print(ggplot() + 63 | geom_signif( 64 | manual = TRUE, 65 | inherit.aes = FALSE 66 | )) 67 | ) 68 | 69 | 70 | expect_snapshot_error( 71 | print(ggplot() + 72 | geom_signif( 73 | data = mtcars, 74 | mapping = aes("x", "y"), 75 | manual = TRUE, 76 | inherit.aes = FALSE 77 | )) 78 | ) 79 | }) 80 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # Version 0.6.4.9000 2 | 3 | - Updates docs and tests for `{ggplot2}` release (`3.4.0`). 4 | 5 | # Version 0.6.4 6 | 7 | - Gets rid of `NOTE` in CRAN checks. 8 | 9 | # Version 0.6.3 10 | 11 | - Introducing `orientation` argument to control the direction (either `x` or 12 | `y`) of the layer and better compatible with `coord_flip()`. (@xiangpin, 13 | #104) 14 | 15 | # Version 0.6.2 16 | 17 | - Updates visual regression tests for `vdiffr 1.0.0` release. 18 | 19 | - Removes `NOTE` about `lazyData` from CRAN's daily checks. 20 | 21 | # Version 0.6.1 22 | 23 | - @IndrajeetPatil is now a `ggsignif` author in recognition of his significant 24 | and sustained contributions. 25 | 26 | - Add `extend_line` parameter to make the brackets shorter or wider (#70). 27 | Thanks to @romanhaa for making the PR. 28 | 29 | - Adds a new package website: . 30 | 31 | - Minimum `ggplot2` version needed is bumped to `3.3.3`. 32 | 33 | - Minor internal refactoring 34 | 35 | - Adds snapshot and visual regression testing infrastructure. 36 | 37 | # Version 0.6.0 38 | 39 | - Support plotmath expression and add new parameter `parse` to function. Fixes 40 | issue #64. Thanks to @IndrajeetPatil for the idea. 41 | 42 | # Version 0.5.0 43 | 44 | - Fix typos in README.md (thanks to @SMargell) 45 | 46 | - `map_signif_level` can now take a user-supplied function to format the p-value 47 | (PR #52, thanks to @ilia-kats) 48 | 49 | # Version 0.4.0 50 | 51 | - Fix bug that stopped textsize from working 52 | 53 | - Add `manual=TRUE` mode, which allows the parameters to be given as a 54 | data.frame 55 | 56 | # Version 0.3.0 57 | 58 | - Simplify setting brackets at custom locations with xmin, xmax and y_position 59 | 60 | - Extend documentation 61 | 62 | - Bug fixes 63 | 64 | # Version 0.2.0 65 | 66 | - Fixed bug, when `alpha()` from another package was loaded (issue #2) 67 | 68 | - Added additional parameters to customize bracket linetype, textsize, size 69 | (issue #5) 70 | 71 | - Fixed bug when annotation was identical for different brackets (issue #6) 72 | 73 | - Fixed bug when multiple comparisons referenced the same block (issue #8) 74 | 75 | # Version 0.1.0 76 | 77 | - Initial release on CRAN: https://CRAN.R-project.org/package=ggsignif 78 | 79 | -------------------------------------------------------------------------------- /paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ggsignif: R Package for Displaying Significance Brackets for 'ggplot2'" 3 | tags: 4 | - R 5 | - ggplot2 6 | - ggplot2-extension 7 | authors: 8 | - name: Constantin Ahlmann-Eltze 9 | orcid: 0000-0002-3762-068X 10 | affiliation: 1 11 | - name: Indrajeet Patil 12 | orcid: 0000-0003-1995-6531 13 | affiliation: 2 14 | affiliations: 15 | - index: 1 16 | name: The European Molecular Biology Laboratory, Heidelberg, Germany 17 | - index: 2 18 | name: Center for Humans and Machines, Max Planck Institute for Human Development, Berlin, Germany 19 | date: "2021-03-26" 20 | bibliography: paper.bib 21 | --- 22 | 23 | 24 | 25 | # Summary 26 | 27 | Research hypotheses are often concerned with the difference between two groups and 28 | statistical tests provide indicators (like *p*-values or Bayes factors) about 29 | the evidence for or against such differences. The R package, `ggsignif` provides 30 | a quick way to visualize such pairwise indicators as an annotation in a plot, 31 | for example showing if a difference is statistically significant. `ggsignif` 32 | follows the principles of the grammar of graphics [@Wilkinson2012] and provides 33 | a new layer that can be added to plots made with the `ggplot2` package 34 | [@Wickham2016]. 35 | 36 | # Statement of Need 37 | 38 | During the exploratory analysis of data with discrete covariates, researchers 39 | commonly start with a one-way ANOVA to see if there are any differences in the 40 | group means. This is typically followed up with multiple comparisons to see if 41 | specific levels of the discrete covariates differ significantly. The `ggsignif` 42 | package provides a way to graphically display all or a few (depending on the 43 | context of the research hypotheses) of such comparisons. It is also used by 44 | other R package developers as the back-end for graphical display of pairwise 45 | comparisons, such as `ggpubr` [@Kassambara2020], `ggstatsplot` [@Patil2018], and 46 | more. These packages demonstrate how `ggsignif` can be extended to display 47 | results from any type of pairwise comparisons test (e.g., Bayesian *t*-test, 48 | Dunn test, etc.). 49 | 50 | # Features 51 | 52 | The following is a simple example demonstrating how a group difference can be 53 | annotated using `geom_signif` layers from `ggsignif` package. 54 | 55 | 56 | ```r 57 | set.seed(123) 58 | library(ggplot2) 59 | library(ggsignif) 60 | 61 | ggplot(mpg, aes(class, hwy)) + 62 | geom_boxplot() + 63 | geom_signif( 64 | comparisons = list(c("compact", "midsize"), c("minivan", "suv")), 65 | map_signif_level = TRUE 66 | ) + 67 | ylim(NA, 48) 68 | ``` 69 | 70 | 71 | \includegraphics[width=1\linewidth]{paper_files/figure-latex/simpe_comparison-1} 72 | 73 | For more advanced examples, the readers are encouraged to read the package 74 | website: . 75 | 76 | # Licensing and Availability 77 | 78 | `ggsignif` is licensed under the GNU General Public License (v3.0), with all 79 | source code stored at [GitHub](https://github.com/const-ae/ggsignif), and with a 80 | corresponding issue tracker for bug reporting and feature enhancements. In the 81 | spirit of honest and open science, we encourage requests/tips for fixes, feature 82 | updates, as well as general questions and concerns via direct interaction with 83 | contributors and developers, by [filing an issue](https://github.com/const-ae/ggsignif/issues). See the 84 | [*Contribution Guidelines*](https://github.com/const-ae/ggsignif/blob/master/CODE_OF_CONDUCT.md) for this package. 85 | 86 | # Acknowledgements 87 | 88 | We would like to thank the users of `ggsignif` package for reporting bugs and 89 | for providing valuable feedback. 90 | 91 | This work was supported by the EMBL International PhD Programme (C.A.E.). 92 | 93 | # References 94 | -------------------------------------------------------------------------------- /paper/paper.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ggsignif: R Package for Displaying Significance Brackets for 'ggplot2'" 3 | keywords: 4 | - R 5 | - ggplot2 6 | - ggplot2-extension 7 | authors: 8 | - name: Constantin Ahlmann-Eltze 9 | orcid: 0000-0002-3762-068X 10 | affiliation: The European Molecular Biology Laboratory, Heidelberg, Germany 11 | - name: Indrajeet Patil 12 | orcid: 0000-0003-1995-6531 13 | affiliation: Center for Humans and Machines, Max Planck Institute for Human Development, Berlin, Germany 14 | bibliography: paper.bib 15 | abstract: | 16 | Research hypotheses are often concerned with the difference between two groups and 17 | statistical tests provide indicators (like *p*-values or Bayes factors) about 18 | the evidence for or against such differences. The R package, `ggsignif` provides 19 | a quick way to visualize such pairwise indicators as an annotation in a plot, 20 | for example showing if a difference is statistically significant. `ggsignif` 21 | follows the principles of the grammar of graphics [@Wilkinson2012] and provides 22 | a new layer that can be added to plots made with the `ggplot2` package 23 | [@Wickham2016]. 24 | output: 25 | rticles::arxiv_article: 26 | keep_tex: true 27 | csl: apa.csl 28 | link-citations: yes 29 | --- 30 | 31 | ```{r echo=FALSE} 32 | knitr::opts_chunk$set( 33 | collapse = TRUE, 34 | dpi = 300, 35 | warning = FALSE, 36 | message = FALSE, 37 | out.width = "100%", 38 | comment = "#>" 39 | ) 40 | ``` 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | # Statement of Need 54 | 55 | During the exploratory analysis of data with discrete covariates, researchers 56 | commonly start with a one-way ANOVA to see if there are any differences in the 57 | group means. This is typically followed up with multiple comparisons to see if 58 | specific levels of the discrete covariates differ significantly. The `ggsignif` 59 | package provides a way to graphically display all or a few (depending on the 60 | context of the research hypotheses) of such comparisons. It is also used by 61 | other R package developers as the back-end for graphical display of pairwise 62 | comparisons, such as `ggpubr` [@Kassambara2020], `ggstatsplot` [@Patil2018], and 63 | more. These packages demonstrate how `ggsignif` can be extended to display 64 | results from any type of pairwise comparisons test (e.g., Bayesian *t*-test, 65 | Dunn test, etc.). 66 | 67 | # Features 68 | 69 | The following is a simple example demonstrating how a group difference can be 70 | annotated using `geom_signif` layers from `ggsignif` package. 71 | 72 | ```{r simpe_comparison} 73 | set.seed(123) 74 | library(ggplot2) 75 | library(ggsignif) 76 | 77 | ggplot(mpg, aes(class, hwy)) + 78 | geom_boxplot() + 79 | geom_signif( 80 | comparisons = list(c("compact", "midsize"), c("minivan", "suv")), 81 | map_signif_level = TRUE 82 | ) + 83 | ylim(NA, 48) 84 | ``` 85 | 86 | For more advanced examples, the readers are encouraged to read the package 87 | website: . 88 | 89 | # Licensing and Availability 90 | 91 | `ggsignif` is licensed under the GNU General Public License (v3.0), with all 92 | source code stored at [GitHub](https://github.com/const-ae/ggsignif), and with a 93 | corresponding issue tracker for bug reporting and feature enhancements. In the 94 | spirit of honest and open science, we encourage requests/tips for fixes, feature 95 | updates, as well as general questions and concerns via direct interaction with 96 | contributors and developers, by [filing an issue](https://github.com/const-ae/ggsignif/issues). See the 97 | [*Contribution Guidelines*](https://github.com/const-ae/ggsignif/blob/master/CODE_OF_CONDUCT.md) for this package. 98 | 99 | # Acknowledgements 100 | 101 | We would like to thank the users of `ggsignif` package for reporting bugs and 102 | for providing valuable feedback. 103 | 104 | This work was supported by the EMBL International PhD Programme (C.A.E.). 105 | 106 | # References 107 | -------------------------------------------------------------------------------- /vignettes/intro.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Introduction to 'ggsignif' package" 3 | author: "Constantin Ahlmann-Eltze and Indrajeet Patil" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Introduction to 'ggsignif' package} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, echo=FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>", 16 | message = FALSE, 17 | warning = FALSE, 18 | dpi = 300, 19 | out.width = "100%", 20 | fig.width = 6, 21 | fig.height = 5 22 | ) 23 | ``` 24 | 25 | # Statement of Need 26 | 27 | Research hypotheses often concern with differences between two or multiple 28 | groups and significance-based hypothesis testing can provide indices for 29 | evidence for such differences. Naturally, when people are visualizing such group 30 | differences, they might also wish to quickly annotate if the difference between 31 | two levels of group in a plot is significantly different 32 | ([look](https://stackoverflow.com/questions/17084566/put-stars-on-ggplot-barplots-and-boxplots-to-indicate-the-level-of-significanc) 33 | [at](https://stackoverflow.com/questions/14958159/indicating-the-statistically-significant-difference-in-bar-graph-using-r) 34 | [all](https://stackoverflow.com/questions/29263046/how-to-draw-the-boxplot-with-significant-level) 35 | [those](https://stackoverflow.com/questions/29186435/showing-significance-relationships-in-a-ggplot2-bar-graph) 36 | [questions](https://groups.google.com/forum/#!topic/ggplot2/ntVlMqTc6PI)). This 37 | package does exactly that! It provides a single layer `geom_signif` which can 38 | calculate the significance of a difference between groups and add the annotation 39 | to the plot in a single line. In doing so, it further extends the fundamental 40 | strength of `ggplot`, which allows one to quickly make advanced plots by 41 | combining layers of visualization, which can encapsulate complex statistical 42 | methods (`geom_smooth`, `geom_contour` etc.). 43 | 44 | # How to use it? 45 | 46 | ## Simple case 47 | 48 | First step: load the needed packages. 49 | 50 | ```{r} 51 | library(ggplot2) 52 | library(ggsignif) 53 | ``` 54 | 55 | Second step: plot your data. 56 | 57 | ```{r simple} 58 | ggplot(iris, aes(x = Species, y = Sepal.Length)) + 59 | geom_boxplot() + # using `ggsignif` to display comparison of interest 60 | geom_signif( 61 | comparisons = list(c("versicolor", "virginica")), 62 | map_signif_level = TRUE 63 | ) 64 | ``` 65 | 66 | That's it, it is as simple as that! 67 | 68 | Note that, if we were to statistically analyze this data, we would run a one-way 69 | ANOVA to assess if *any* of the group means differ from each other and then 70 | follow up with *post hoc* multiple comparisons to do more fine-grained 71 | comparisons between different levels of the group. The `ggsignif` package 72 | provides a way to graphically display all or a few (depending on the research 73 | hypotheses context) of such comparisons. 74 | 75 | ## Advanced options 76 | 77 | Sometimes one might need more advanced control over the display. For example, 78 | instead of a one-way ANOVA design, you can have a complex multiway-ANOVA design. 79 | In such instances, you may wish to have a much finer control over which 80 | comparisons to include for pairwise comparison displays. 81 | 82 | To specify exactly where the bracket is drawn use the `y_position`, `xmin` and 83 | `xmax` parameters combined with a custom `annotations`. This is always necessary 84 | if `geom_signif` is combined with another layer that uses `position="dodge"`, 85 | because it changes the location of the visual elements *without* updating the 86 | data. 87 | 88 | ```{r advanced} 89 | dat <- data.frame( 90 | Group = c("S1", "S1", "S2", "S2"), 91 | Sub = c("A", "B", "A", "B"), 92 | Value = c(3, 5, 7, 8) 93 | ) 94 | 95 | ggplot(dat, aes(Group, Value)) + 96 | geom_bar(aes(fill = Sub), stat = "identity", position = "dodge", width = .5) + 97 | geom_signif( 98 | y_position = c(5.3, 8.3), xmin = c(0.8, 1.8), xmax = c(1.2, 2.2), 99 | annotation = c("**", "NS"), tip_length = 0 100 | ) + 101 | geom_signif( 102 | comparisons = list(c("S1", "S2")), 103 | y_position = 9.3, tip_length = 0, vjust = 0.2 104 | ) + 105 | scale_fill_manual(values = c("grey80", "grey20")) 106 | ``` 107 | 108 | For more detailed documentation of the available parameters see the manual page 109 | for the `geom_signif` function: 110 | 111 | 112 | # Bugs, Comments or Questions? 113 | 114 | If you have any problems with the package, just file an issue at https://github.com/const-ae/ggsignif. 115 | -------------------------------------------------------------------------------- /tests/testthat/test-vdiffr.R: -------------------------------------------------------------------------------- 1 | test_that("plots are rendered correctly", { 2 | library(ggplot2) 3 | 4 | set.seed(123) 5 | vdiffr::expect_doppelganger( 6 | title = "basic plot", 7 | fig = ggplot(mpg, aes(x = manufacturer, y = displ)) + 8 | geom_boxplot() + 9 | stat_signif( 10 | comparisons = list(c("audi", "ford"), c("hyundai", "nissan")), 11 | map_signif_level = TRUE, 12 | test = "wilcox.test", test.args = list(alternative = "two.sided"), 13 | margin_top = 0.02, step_increase = 0, tip_length = 0.01 14 | ) + 15 | theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 16 | facet_wrap(~ as.factor(year), scale = "free") 17 | ) 18 | 19 | 20 | set.seed(123) 21 | vdiffr::expect_doppelganger( 22 | title = "flipping aesthetics works", 23 | fig = ggplot(mpg, aes(class, hwy)) + 24 | geom_boxplot() + 25 | geom_signif(comparisons = list( 26 | c("compact", "pickup"), 27 | c("subcompact", "suv") 28 | )) + 29 | coord_flip() 30 | ) 31 | 32 | set.seed(123) 33 | vdiffr::expect_doppelganger( 34 | title = "geom works the same way as stat", 35 | fig = ggplot(mpg, aes(x = manufacturer, y = displ)) + 36 | geom_boxplot() + 37 | geom_signif( 38 | comparisons = list(c("audi", "ford"), c("hyundai", "nissan")), 39 | annotations = c("Interesting", "Too far apart"), 40 | test = "wilcox.test", 41 | test.args = list(alternative = "two.sided"), 42 | margin_top = 0.02, 43 | step_increase = 0, 44 | tip_length = 0.01 45 | ) + 46 | theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 47 | facet_wrap(~ as.factor(year), scale = "free") 48 | ) 49 | 50 | dat <- data.frame( 51 | Group = c("S1", "S1", "S2", "S2"), 52 | Sub = c("A", "B", "A", "B"), 53 | Value = c(3, 5, 7, 8) 54 | ) 55 | 56 | set.seed(123) 57 | vdiffr::expect_doppelganger( 58 | title = "geom works with identity", 59 | fig = ggplot(dat, aes(Group, Value)) + 60 | geom_bar(aes(fill = Sub), 61 | stat = "identity", 62 | position = "dodge", 63 | width = .5 64 | ) + 65 | geom_signif( 66 | stat = "identity", 67 | data = data.frame( 68 | m = c(0.875, 1.875), 69 | xend = c(1.125, 2.125), 70 | n = c(5.8, 8.5), 71 | annotation = c("***", "NS") 72 | ), 73 | aes( 74 | x = m, 75 | xend = xend, 76 | y = n, 77 | yend = n, 78 | annotation = annotation 79 | ) 80 | ) + 81 | geom_signif( 82 | comparisons = list(c("S1", "S2")), 83 | annotations = "***", 84 | y_position = 9.3, 85 | tip_length = 0 86 | ) + 87 | scale_fill_manual(values = c("grey80", "grey20")) 88 | ) 89 | }) 90 | 91 | test_that("method which return text works - snapshot", { 92 | magnitude_test <- function(x, y, ...) { 93 | change <- mean(y) / mean(x) 94 | p <- t.test(x, y)$p.value 95 | stars <- if (p < 0.001) { 96 | "***" 97 | } else if (p < 0.01) { 98 | "**" 99 | } else if (p < 0.05) { 100 | "*" 101 | } else { 102 | "" 103 | } 104 | list(p.value = paste0(signif(change, digits = 2), stars)) 105 | } 106 | 107 | p <- ggplot(mpg, aes(x = manufacturer, y = displ)) + 108 | geom_boxplot() + 109 | stat_signif( 110 | comparisons = list(c("audi", "ford"), c("hyundai", "nissan")), 111 | test = magnitude_test, 112 | margin_top = 0.02, step_increase = 0, tip_length = 0.01 113 | ) + 114 | theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 115 | facet_wrap(~ as.factor(year), scale = "free") 116 | 117 | vdiffr::expect_doppelganger( 118 | title = "stat-signif-magnitude", 119 | fig = p 120 | ) 121 | }) 122 | 123 | 124 | test_that("identical annotations are plotted separetly - snapshot", { 125 | dat <- data.frame( 126 | Group = c("S1", "S1", "S2", "S2"), 127 | Sub = c("A", "B", "A", "B"), 128 | Value = c(3, 5, 7, 8) 129 | ) 130 | 131 | p <- ggplot(dat, aes(Group, Value)) + 132 | geom_bar(aes(fill = Sub), stat = "identity", position = "dodge", width = .5) + 133 | geom_signif( 134 | comparisons = list(c("S1", "S2")), annotations = "***", 135 | y_position = 9.3, tip_length = 0, vjust = 0.4 136 | ) + 137 | geom_signif( 138 | stat = "identity", 139 | data = data.frame( 140 | x = c(0.875, 1.875), xend = c(1.125, 2.125), 141 | y = c(5.8, 8.5), annotation = c("**", "**") 142 | ), 143 | aes( 144 | x = x, 145 | xend = xend, 146 | y = y, 147 | yend = y, 148 | annotation = annotation, 149 | group = c(1, 2) 150 | ) 151 | ) + 152 | scale_fill_manual(values = c("grey80", "grey20")) 153 | 154 | vdiffr::expect_doppelganger( 155 | title = "identical annotations", 156 | fig = p 157 | ) 158 | }) 159 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards 42 | of acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies 54 | when an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail 56 | address, posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at artjom31415@googlemail.com. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, 118 | available at . 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | . Translations are available at . 127 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------- 2 | # CITATION file created with {cffr} R package, v0.2.1 3 | # See also: https://docs.ropensci.org/cffr/ 4 | # ----------------------------------------------------------- 5 | 6 | cff-version: 1.2.0 7 | message: 'To cite package "ggsignif" in publications use:' 8 | type: software 9 | license: GPL-3.0-only 10 | title: 'ggsignif: Significance Brackets for ''ggplot2''' 11 | version: 0.6.3.9000 12 | doi: 10.31234/osf.io/7awm6 13 | abstract: Enrich your 'ggplots' with group-wise comparisons. This package provides 14 | an easy way to indicate if two groups are significantly different. Commonly this 15 | is shown by a bracket on top connecting the groups of interest which itself is annotated 16 | with the level of significance (NS, *, **, ***). The package provides a single layer 17 | (geom_signif()) that takes the groups for comparison and the test (t.test(), wilcox.text() 18 | etc.) as arguments and adds the annotation to the plot. 19 | authors: 20 | - family-names: Ahlmann-Eltze 21 | given-names: Constantin 22 | email: artjom31415@googlemail.com 23 | orcid: https://orcid.org/0000-0002-3762-068X 24 | - family-names: Patil 25 | given-names: Indrajeet 26 | email: patilindrajeet.science@gmail.com 27 | orcid: https://orcid.org/0000-0003-1995-6531 28 | preferred-citation: 29 | type: article 30 | title: 'ggsignif: R Package for Displaying Significance Brackets for ''ggplot2''' 31 | authors: 32 | - family-names: Constantin 33 | given-names: Ahlmann-Eltze 34 | - family-names: Patil 35 | given-names: Indrajeet 36 | email: patilindrajeet.science@gmail.com 37 | orcid: https://orcid.org/0000-0003-1995-6531 38 | year: '2021' 39 | journal: PsyArxiv 40 | url: https://psyarxiv.com/7awm6 41 | doi: 10.31234/osf.io/7awm6 42 | repository: https://CRAN.R-project.org/package=ggsignif 43 | repository-code: https://github.com/const-ae/ggsignif 44 | url: https://const-ae.github.io/ggsignif/ 45 | contact: 46 | - family-names: Ahlmann-Eltze 47 | given-names: Constantin 48 | email: artjom31415@googlemail.com 49 | orcid: https://orcid.org/0000-0002-3762-068X 50 | keywords: 51 | - asterisk 52 | - ggplot-extension 53 | - ggplot2 54 | - rstats 55 | - significance-stars 56 | references: 57 | - type: software 58 | title: ggplot2 59 | abstract: 'ggplot2: Create Elegant Data Visualisations Using the Grammar of Graphics' 60 | notes: Imports 61 | authors: 62 | - family-names: Wickham 63 | given-names: Hadley 64 | email: hadley@rstudio.com 65 | orcid: https://orcid.org/0000-0003-4757-117X 66 | - family-names: Chang 67 | given-names: Winston 68 | orcid: https://orcid.org/0000-0002-1576-2126 69 | - family-names: Henry 70 | given-names: Lionel 71 | - family-names: Pedersen 72 | given-names: Thomas Lin 73 | email: thomas.pedersen@rstudio.com 74 | orcid: https://orcid.org/0000-0002-5147-4711 75 | - family-names: Takahashi 76 | given-names: Kohske 77 | - family-names: Wilke 78 | given-names: Claus 79 | orcid: https://orcid.org/0000-0002-7470-9261 80 | - family-names: Woo 81 | given-names: Kara 82 | orcid: https://orcid.org/0000-0002-5125-4188 83 | - family-names: Yutani 84 | given-names: Hiroaki 85 | orcid: https://orcid.org/0000-0002-3385-7233 86 | - family-names: Dunnington 87 | given-names: Dewey 88 | orcid: https://orcid.org/0000-0002-9415-4582 89 | year: '2022' 90 | url: https://CRAN.R-project.org/package=ggplot2 91 | version: '>= 3.3.5' 92 | - type: software 93 | title: knitr 94 | abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R' 95 | notes: Suggests 96 | authors: 97 | - family-names: Xie 98 | given-names: Yihui 99 | email: xie@yihui.name 100 | orcid: https://orcid.org/0000-0003-0645-5666 101 | year: '2022' 102 | url: https://CRAN.R-project.org/package=knitr 103 | - type: software 104 | title: rmarkdown 105 | abstract: 'rmarkdown: Dynamic Documents for R' 106 | notes: Suggests 107 | authors: 108 | - family-names: Allaire 109 | given-names: JJ 110 | email: jj@rstudio.com 111 | - family-names: Xie 112 | given-names: Yihui 113 | email: xie@yihui.name 114 | orcid: https://orcid.org/0000-0003-0645-5666 115 | - family-names: McPherson 116 | given-names: Jonathan 117 | email: jonathan@rstudio.com 118 | - family-names: Luraschi 119 | given-names: Javier 120 | email: javier@rstudio.com 121 | - family-names: Ushey 122 | given-names: Kevin 123 | email: kevin@rstudio.com 124 | - family-names: Atkins 125 | given-names: Aron 126 | email: aron@rstudio.com 127 | - family-names: Wickham 128 | given-names: Hadley 129 | email: hadley@rstudio.com 130 | - family-names: Cheng 131 | given-names: Joe 132 | email: joe@rstudio.com 133 | - family-names: Chang 134 | given-names: Winston 135 | email: winston@rstudio.com 136 | - family-names: Iannone 137 | given-names: Richard 138 | email: rich@rstudio.com 139 | orcid: https://orcid.org/0000-0003-3925-190X 140 | year: '2022' 141 | - type: software 142 | title: testthat 143 | abstract: 'testthat: Unit Testing for R' 144 | notes: Suggests 145 | authors: 146 | - family-names: Wickham 147 | given-names: Hadley 148 | email: hadley@rstudio.com 149 | year: '2022' 150 | url: https://CRAN.R-project.org/package=testthat 151 | - type: software 152 | title: vdiffr 153 | abstract: 'vdiffr: Visual Regression Testing and Graphical Diffing' 154 | notes: Suggests 155 | authors: 156 | - family-names: Henry 157 | given-names: Lionel 158 | email: lionel@rstudio.com 159 | - family-names: Pedersen 160 | given-names: Thomas Lin 161 | email: thomas.pedersen@rstudio.com 162 | orcid: https://orcid.org/0000-0002-5147-4711 163 | - family-names: Luciani 164 | given-names: T Jake 165 | email: jake@apache.org 166 | - family-names: Decorde 167 | given-names: Matthieu 168 | email: matthieu.decorde@ens-lyon.fr 169 | - family-names: Lise 170 | given-names: Vaudor 171 | email: lise.vaudor@ens-lyon.fr 172 | year: '2022' 173 | url: https://CRAN.R-project.org/package=vdiffr 174 | version: '>= 1.0.2' 175 | -------------------------------------------------------------------------------- /codemeta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://doi.org/10.5063/schema/codemeta-2.0", 3 | "@type": "SoftwareSourceCode", 4 | "identifier": "ggsignif", 5 | "description": "Enrich your 'ggplots' with group-wise comparisons. This package provides an easy way to indicate if two groups are significantly different. Commonly this is shown by a bracket on top connecting the groups of interest which itself is annotated with the level of significance (NS, *, **, ***). The package provides a single layer (geom_signif()) that takes the groups for comparison and the test (t.test(), wilcox.text() etc.) as arguments and adds the annotation to the plot.", 6 | "name": "ggsignif: Significance Brackets for 'ggplot2'", 7 | "relatedLink": ["https://const-ae.github.io/ggsignif/", "https://CRAN.R-project.org/package=ggsignif"], 8 | "codeRepository": "https://github.com/const-ae/ggsignif", 9 | "license": "https://spdx.org/licenses/GPL-3.0", 10 | "version": "0.6.4", 11 | "programmingLanguage": { 12 | "@type": "ComputerLanguage", 13 | "name": "R", 14 | "url": "https://r-project.org" 15 | }, 16 | "runtimePlatform": "R version 4.1.1 (2021-08-10)", 17 | "provider": { 18 | "@id": "https://cran.r-project.org", 19 | "@type": "Organization", 20 | "name": "Comprehensive R Archive Network (CRAN)", 21 | "url": "https://cran.r-project.org" 22 | }, 23 | "author": [ 24 | { 25 | "@type": "Person", 26 | "givenName": "Constantin", 27 | "familyName": "Ahlmann-Eltze", 28 | "email": "artjom31415@googlemail.com", 29 | "@id": "https://orcid.org/0000-0002-3762-068X" 30 | }, 31 | { 32 | "@type": "Person", 33 | "givenName": "Indrajeet", 34 | "familyName": "Patil", 35 | "email": "patilindrajeet.science@gmail.com", 36 | "@id": "https://orcid.org/0000-0003-1995-6531" 37 | } 38 | ], 39 | "contributor": [ 40 | { 41 | "@type": "Person", 42 | "givenName": "Constantin", 43 | "familyName": "Ahlmann-Eltze", 44 | "email": "artjom31415@googlemail.com", 45 | "@id": "https://orcid.org/0000-0002-3762-068X" 46 | }, 47 | { 48 | "@type": "Person", 49 | "givenName": "Indrajeet", 50 | "familyName": "Patil", 51 | "email": "patilindrajeet.science@gmail.com", 52 | "@id": "https://orcid.org/0000-0003-1995-6531" 53 | } 54 | ], 55 | "maintainer": [ 56 | { 57 | "@type": "Person", 58 | "givenName": "Constantin", 59 | "familyName": "Ahlmann-Eltze", 60 | "email": "artjom31415@googlemail.com", 61 | "@id": "https://orcid.org/0000-0002-3762-068X" 62 | } 63 | ], 64 | "softwareSuggestions": [ 65 | { 66 | "@type": "SoftwareApplication", 67 | "identifier": "knitr", 68 | "name": "knitr", 69 | "provider": { 70 | "@id": "https://cran.r-project.org", 71 | "@type": "Organization", 72 | "name": "Comprehensive R Archive Network (CRAN)", 73 | "url": "https://cran.r-project.org" 74 | }, 75 | "sameAs": "https://CRAN.R-project.org/package=knitr" 76 | }, 77 | { 78 | "@type": "SoftwareApplication", 79 | "identifier": "rmarkdown", 80 | "name": "rmarkdown", 81 | "provider": { 82 | "@id": "https://cran.r-project.org", 83 | "@type": "Organization", 84 | "name": "Comprehensive R Archive Network (CRAN)", 85 | "url": "https://cran.r-project.org" 86 | }, 87 | "sameAs": "https://CRAN.R-project.org/package=rmarkdown" 88 | }, 89 | { 90 | "@type": "SoftwareApplication", 91 | "identifier": "testthat", 92 | "name": "testthat", 93 | "provider": { 94 | "@id": "https://cran.r-project.org", 95 | "@type": "Organization", 96 | "name": "Comprehensive R Archive Network (CRAN)", 97 | "url": "https://cran.r-project.org" 98 | }, 99 | "sameAs": "https://CRAN.R-project.org/package=testthat" 100 | }, 101 | { 102 | "@type": "SoftwareApplication", 103 | "identifier": "vdiffr", 104 | "name": "vdiffr", 105 | "version": ">= 1.0.2", 106 | "provider": { 107 | "@id": "https://cran.r-project.org", 108 | "@type": "Organization", 109 | "name": "Comprehensive R Archive Network (CRAN)", 110 | "url": "https://cran.r-project.org" 111 | }, 112 | "sameAs": "https://CRAN.R-project.org/package=vdiffr" 113 | } 114 | ], 115 | "softwareRequirements": { 116 | "1": { 117 | "@type": "SoftwareApplication", 118 | "identifier": "ggplot2", 119 | "name": "ggplot2", 120 | "version": ">= 3.3.5", 121 | "provider": { 122 | "@id": "https://cran.r-project.org", 123 | "@type": "Organization", 124 | "name": "Comprehensive R Archive Network (CRAN)", 125 | "url": "https://cran.r-project.org" 126 | }, 127 | "sameAs": "https://CRAN.R-project.org/package=ggplot2" 128 | }, 129 | "SystemRequirements": null 130 | }, 131 | "fileSize": "906.037KB", 132 | "citation": [ 133 | { 134 | "@type": "ScholarlyArticle", 135 | "datePublished": "2021", 136 | "author": [ 137 | { 138 | "@type": "Person", 139 | "givenName": "Ahlmann-Eltze", 140 | "familyName": "Constantin" 141 | }, 142 | { 143 | "@type": "Person", 144 | "givenName": "Indrajeet", 145 | "familyName": "Patil" 146 | } 147 | ], 148 | "name": "{ggsignif}: R Package for Displaying Significance Brackets for {'ggplot2'}", 149 | "identifier": "10.31234/osf.io/7awm6", 150 | "url": "https://psyarxiv.com/7awm6", 151 | "@id": "https://doi.org/10.31234/osf.io/7awm6", 152 | "sameAs": "https://doi.org/10.31234/osf.io/7awm6", 153 | "isPartOf": { 154 | "@type": "PublicationIssue", 155 | "datePublished": "2021", 156 | "isPartOf": { 157 | "@type": ["PublicationVolume", "Periodical"], 158 | "name": "PsyArxiv" 159 | } 160 | } 161 | } 162 | ], 163 | "releaseNotes": "https://github.com/const-ae/ggsignif/blob/master/NEWS.md", 164 | "readme": "https://github.com/const-ae/ggsignif/blob/main/README.md", 165 | "contIntegration": "https://app.codecov.io/gh/const-ae/ggsignif?branch=main", 166 | "developmentStatus": "https://lifecycle.r-lib.org/articles/stages.html", 167 | "keywords": ["rstats", "ggplot-extension", "significance-stars", "ggplot2", "asterisk"] 168 | } 169 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | # ggsignif: Significance Brackets for 'ggplot2' 6 | 7 | [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/ggsignif)](https://cran.r-project.org/package=ggsignif) 8 | [![R build status](https://github.com/const-ae/ggsignif/workflows/R-CMD-check/badge.svg)](https://github.com/const-ae/ggsignif/actions) 9 | [![Total downloads badge](https://cranlogs.r-pkg.org/badges/grand-total/ggsignif?color=blue)](https://CRAN.R-project.org/package=ggsignif) 10 | [![Codecov test coverage](https://codecov.io/gh/const-ae/ggsignif/branch/main/graph/badge.svg)](https://app.codecov.io/gh/const-ae/ggsignif?branch=main) 11 | [![lifecycle](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html) 12 | 13 | 14 | 15 | ```{r, echo = FALSE} 16 | knitr::opts_chunk$set( 17 | collapse = TRUE, 18 | comment = "#>", 19 | dpi = 300, 20 | out.width = "100%", 21 | fig.path = "man/figures/README-", 22 | message = FALSE, 23 | warning = FALSE 24 | ) 25 | ``` 26 | 27 | ## Introduction 28 | 29 | This package provides an easy way to indicate if two groups are significantly 30 | different. Commonly this is shown by a bar on top connecting the groups of 31 | interest which itself is annotated with the level of significance (NS, \*, \*\*, 32 | \*\*\*). The package provides a single layer (`geom_signif`) that takes the 33 | groups for comparison and the test (t.test, wilcox etc.) and adds the annotation 34 | to the plot. 35 | 36 | ## Citation 37 | 38 | If you wish to cite this package in a publication, you can run the following 39 | command in your R console: 40 | 41 | ```{r citation} 42 | citation("ggsignif") 43 | ``` 44 | 45 | ## Example 46 | 47 | You can first install this package from `CRAN`: 48 | 49 | ```{r eval=FALSE} 50 | install.packages("ggsignif") 51 | ``` 52 | 53 | Or get the latest development version: 54 | 55 | ```{r eval=FALSE} 56 | install.packages("remotes") 57 | remotes::install_github("const-ae/ggsignif") 58 | ``` 59 | 60 | Plot significance 61 | 62 | ```{r simpe_comparison} 63 | library(ggplot2) 64 | library(ggsignif) 65 | 66 | p1 <- ggplot(mpg, aes(class, hwy)) + 67 | geom_boxplot() + 68 | geom_signif( 69 | comparisons = list(c("compact", "midsize"), c("minivan", "suv")), 70 | map_signif_level = TRUE, textsize = 6 71 | ) + 72 | ylim(NA, 48) 73 | p1 74 | ``` 75 | 76 | Control the direction (either `x` or `y`) via `orientation` 77 | 78 | ```{r orientation_argument} 79 | p2 <- ggplot( 80 | data = mpg, 81 | mapping = aes( 82 | x = hwy, 83 | y = class 84 | ) 85 | ) + 86 | geom_boxplot( 87 | orientation = "y" 88 | ) + 89 | geom_signif( 90 | comparisons = list( 91 | c("compact", "midsize"), 92 | c("minivan", "suv") 93 | ), 94 | map_signif_level = TRUE, 95 | textsize = 6, 96 | margin_top = 0.08, 97 | step_increase = 0.05, 98 | tip_length = 0.01, 99 | orientation = "y" 100 | ) 101 | p2 102 | ``` 103 | 104 | Compatible with coord_flip 105 | 106 | ```{r coord_flip} 107 | p1 + coord_flip() 108 | ``` 109 | 110 | Setting the precise location 111 | 112 | This is important if you use `position="dodge"`, because in that case I cannot 113 | calculate the correct position of the bars automatically. 114 | 115 | ```{r dodge_comparison} 116 | # Calculate annotation 117 | anno <- t.test( 118 | iris[iris$Petal.Width > 1 & iris$Species == "versicolor", "Sepal.Width"], 119 | iris[iris$Species == "virginica", "Sepal.Width"] 120 | )$p.value 121 | 122 | # Make plot with custom x and y position of the bracket 123 | ggplot(iris, aes(x = Species, y = Sepal.Width, fill = Petal.Width > 1)) + 124 | geom_boxplot(position = "dodge") + 125 | geom_signif( 126 | annotation = formatC(anno, digits = 1), 127 | y_position = 4.05, xmin = 2.2, xmax = 3, 128 | tip_length = c(0.2, 0.04) 129 | ) 130 | ``` 131 | 132 | `ggsignif` is compatible with facetting (`facet_wrap` or `facet_grid`). The significance label is calculated for each facet where the axis labels listed in `comparisons` occur. Note that `ggsignif` fails to calculate the significance if the data is grouped globally (e.g., by setting `color`, `fill`, or `group` in `ggplot(aes(...))`). It is fine to group the data per geom (e.g., set the fill within `geom_boxplot(aes(fill = ...)))`. 133 | 134 | ```{r faceted_simple} 135 | ggplot(diamonds, aes(x = cut, y = carat)) + 136 | geom_boxplot(aes(fill = color)) + 137 | geom_signif(comparisons = list( 138 | c("Fair", "Good"), 139 | c("Very Good", "Ideal") 140 | )) + 141 | facet_wrap(~color) + 142 | ylim(NA, 6.3) 143 | ``` 144 | 145 | 146 | 147 | ## Advanced Example 148 | 149 | Sometimes one needs to have a very fine tuned ability to set the location of the 150 | the significance bars in combination with `facet_wrap` or `facet_grid`. In those 151 | cases it you can set the flag `manual=TRUE` and provide the annotations as a 152 | data.frame: 153 | 154 | ```{r faceted_comparison} 155 | annotation_df <- data.frame( 156 | color = c("E", "H"), 157 | start = c("Good", "Fair"), 158 | end = c("Very Good", "Good"), 159 | y = c(3.6, 4.7), 160 | label = c("Comp. 1", "Comp. 2") 161 | ) 162 | 163 | annotation_df 164 | 165 | ggplot(diamonds, aes(x = cut, y = carat)) + 166 | geom_boxplot() + 167 | geom_signif( 168 | data = annotation_df, 169 | aes(xmin = start, xmax = end, annotations = label, y_position = y), 170 | textsize = 3, vjust = -0.2, 171 | manual = TRUE 172 | ) + 173 | facet_wrap(~color) + 174 | ylim(NA, 5.3) 175 | ``` 176 | 177 | You can ignore the warning about the missing aesthetics. 178 | 179 | For further details, see: 180 | 181 | 182 | ## Maintenance 183 | 184 | This package is provided as is and we currently don't have any plans and the 185 | capacity to add any new features to it. If there is nonetheless a feature which 186 | you would like to see in the package, you are always welcome to submit pull 187 | request, which we will try to address as soon as possible. 188 | 189 | ## Code of Conduct 190 | 191 | Please note that the `ggsignif` project is released with a [Contributor Code of Conduct](https://const-ae.github.io/ggsignif/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. 192 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/vdiffr/identical-annotations.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | *** 35 | 36 | 37 | 38 | ** 39 | 40 | ** 41 | 42 | 43 | 44 | 45 | 0.0 46 | 2.5 47 | 5.0 48 | 7.5 49 | 10.0 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | S1 58 | S2 59 | Group 60 | Value 61 | 62 | Sub 63 | 64 | 65 | 66 | 67 | A 68 | B 69 | identical annotations 70 | 71 | 72 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/vdiffr/geom-works-with-identity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | *** 35 | 36 | NS 37 | 38 | *** 39 | 40 | 41 | 42 | 43 | 44 | 45 | 0.0 46 | 2.5 47 | 5.0 48 | 7.5 49 | 10.0 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | S1 58 | S2 59 | Group 60 | Value 61 | 62 | Sub 63 | 64 | 65 | 66 | 67 | A 68 | B 69 | geom works with identity 70 | 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ggsignif: Significance Brackets for ‘ggplot2’ 3 | 4 | [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/ggsignif)](https://cran.r-project.org/package=ggsignif) 5 | [![R build 6 | status](https://github.com/const-ae/ggsignif/workflows/R-CMD-check/badge.svg)](https://github.com/const-ae/ggsignif/actions) 7 | [![Total downloads 8 | badge](https://cranlogs.r-pkg.org/badges/grand-total/ggsignif?color=blue)](https://CRAN.R-project.org/package=ggsignif) 9 | [![Codecov test 10 | coverage](https://codecov.io/gh/const-ae/ggsignif/branch/main/graph/badge.svg)](https://app.codecov.io/gh/const-ae/ggsignif?branch=main) 11 | [![lifecycle](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html) 12 | 13 | 14 | 15 | ## Introduction 16 | 17 | This package provides an easy way to indicate if two groups are 18 | significantly different. Commonly this is shown by a bar on top 19 | connecting the groups of interest which itself is annotated with the 20 | level of significance (NS, \*, \*\*, \*\*\*). The package provides a 21 | single layer (`geom_signif`) that takes the groups for comparison and 22 | the test (t.test, wilcox etc.) and adds the annotation to the plot. 23 | 24 | ## Citation 25 | 26 | If you wish to cite this package in a publication, you can run the 27 | following command in your R console: 28 | 29 | ``` r 30 | citation("ggsignif") 31 | #> To cite 'ggsignif' in publications use: 32 | #> 33 | #> Ahlmann-Eltze, C., & Patil, I. (2021). ggsignif: R Package for 34 | #> Displaying Significance Brackets for 'ggplot2'. PsyArxiv. 35 | #> doi:10.31234/osf.io/7awm6 36 | #> 37 | #> A BibTeX entry for LaTeX users is 38 | #> 39 | #> @Article{, 40 | #> title = {{ggsignif}: R Package for Displaying Significance Brackets for {'ggplot2'}}, 41 | #> author = {Ahlmann-Eltze Constantin and Indrajeet Patil}, 42 | #> year = {2021}, 43 | #> journal = {PsyArxiv}, 44 | #> url = {https://psyarxiv.com/7awm6}, 45 | #> doi = {10.31234/osf.io/7awm6}, 46 | #> } 47 | ``` 48 | 49 | ## Example 50 | 51 | You can first install this package from `CRAN`: 52 | 53 | ``` r 54 | install.packages("ggsignif") 55 | ``` 56 | 57 | Or get the latest development version: 58 | 59 | ``` r 60 | install.packages("remotes") 61 | remotes::install_github("const-ae/ggsignif") 62 | ``` 63 | 64 | Plot significance 65 | 66 | ``` r 67 | library(ggplot2) 68 | library(ggsignif) 69 | 70 | p1 <- ggplot(mpg, aes(class, hwy)) + 71 | geom_boxplot() + 72 | geom_signif( 73 | comparisons = list(c("compact", "midsize"), c("minivan", "suv")), 74 | map_signif_level = TRUE, textsize = 6 75 | ) + 76 | ylim(NA, 48) 77 | p1 78 | ``` 79 | 80 | 81 | 82 | Control the direction (either `x` or `y`) via `orientation` 83 | 84 | ``` r 85 | p2 <- ggplot( 86 | data = mpg, 87 | mapping = aes( 88 | x = hwy, 89 | y = class 90 | ) 91 | ) + 92 | geom_boxplot( 93 | orientation = "y" 94 | ) + 95 | geom_signif( 96 | comparisons = list( 97 | c("compact", "midsize"), 98 | c("minivan", "suv") 99 | ), 100 | map_signif_level = TRUE, 101 | textsize = 6, 102 | margin_top = 0.08, 103 | step_increase = 0.05, 104 | tip_length = 0.01, 105 | orientation = "y" 106 | ) 107 | p2 108 | ``` 109 | 110 | 111 | 112 | Compatible with coord_flip 113 | 114 | ``` r 115 | p1 + coord_flip() 116 | ``` 117 | 118 | 119 | 120 | Setting the precise location 121 | 122 | This is important if you use `position="dodge"`, because in that case I 123 | cannot calculate the correct position of the bars automatically. 124 | 125 | ``` r 126 | # Calculate annotation 127 | anno <- t.test( 128 | iris[iris$Petal.Width > 1 & iris$Species == "versicolor", "Sepal.Width"], 129 | iris[iris$Species == "virginica", "Sepal.Width"] 130 | )$p.value 131 | 132 | # Make plot with custom x and y position of the bracket 133 | ggplot(iris, aes(x = Species, y = Sepal.Width, fill = Petal.Width > 1)) + 134 | geom_boxplot(position = "dodge") + 135 | geom_signif( 136 | annotation = formatC(anno, digits = 1), 137 | y_position = 4.05, xmin = 2.2, xmax = 3, 138 | tip_length = c(0.2, 0.04) 139 | ) 140 | ``` 141 | 142 | 143 | 144 | `ggsignif` is compatible with facetting (`facet_wrap` or `facet_grid`). 145 | The significance label is calculated for each facet where the axis 146 | labels listed in `comparisons` occur. Note that `ggsignif` fails to 147 | calculate the significance if the data is grouped globally (e.g., by 148 | setting `color`, `fill`, or `group` in `ggplot(aes(...))`). It is fine 149 | to group the data per geom (e.g., set the fill within 150 | `geom_boxplot(aes(fill = ...)))`. 151 | 152 | ``` r 153 | ggplot(diamonds, aes(x = cut, y = carat)) + 154 | geom_boxplot(aes(fill = color)) + 155 | geom_signif(comparisons = list( 156 | c("Fair", "Good"), 157 | c("Very Good", "Ideal") 158 | )) + 159 | facet_wrap(~color) + 160 | ylim(NA, 6.3) 161 | ``` 162 | 163 | 164 | 165 | ## Advanced Example 166 | 167 | Sometimes one needs to have a very fine tuned ability to set the 168 | location of the the significance bars in combination with `facet_wrap` 169 | or `facet_grid`. In those cases it you can set the flag `manual=TRUE` 170 | and provide the annotations as a data.frame: 171 | 172 | ``` r 173 | annotation_df <- data.frame( 174 | color = c("E", "H"), 175 | start = c("Good", "Fair"), 176 | end = c("Very Good", "Good"), 177 | y = c(3.6, 4.7), 178 | label = c("Comp. 1", "Comp. 2") 179 | ) 180 | 181 | annotation_df 182 | #> color start end y label 183 | #> 1 E Good Very Good 3.6 Comp. 1 184 | #> 2 H Fair Good 4.7 Comp. 2 185 | 186 | ggplot(diamonds, aes(x = cut, y = carat)) + 187 | geom_boxplot() + 188 | geom_signif( 189 | data = annotation_df, 190 | aes(xmin = start, xmax = end, annotations = label, y_position = y), 191 | textsize = 3, vjust = -0.2, 192 | manual = TRUE 193 | ) + 194 | facet_wrap(~color) + 195 | ylim(NA, 5.3) 196 | ``` 197 | 198 | 199 | 200 | You can ignore the warning about the missing aesthetics. 201 | 202 | For further details, see: 203 | 204 | 205 | ## Maintenance 206 | 207 | This package is provided as is and we currently don’t have any plans and 208 | the capacity to add any new features to it. If there is nonetheless a 209 | feature which you would like to see in the package, you are always 210 | welcome to submit pull request, which we will try to address as soon as 211 | possible. 212 | 213 | ## Code of Conduct 214 | 215 | Please note that the `ggsignif` project is released with a [Contributor 216 | Code of 217 | Conduct](https://const-ae.github.io/ggsignif/CODE_OF_CONDUCT.html). By 218 | contributing to this project, you agree to abide by its terms. 219 | -------------------------------------------------------------------------------- /man/stat_signif.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geom_signif.R 3 | \name{stat_signif} 4 | \alias{stat_signif} 5 | \alias{geom_signif} 6 | \title{Create significance layer} 7 | \usage{ 8 | stat_signif( 9 | mapping = NULL, 10 | data = NULL, 11 | position = "identity", 12 | na.rm = FALSE, 13 | show.legend = NA, 14 | inherit.aes = TRUE, 15 | comparisons = NULL, 16 | test = "wilcox.test", 17 | test.args = NULL, 18 | annotations = NULL, 19 | map_signif_level = FALSE, 20 | y_position = NULL, 21 | xmin = NULL, 22 | xmax = NULL, 23 | margin_top = 0.05, 24 | step_increase = 0, 25 | tip_length = 0.03, 26 | size = 0.5, 27 | textsize = 3.88, 28 | family = "", 29 | vjust = 0, 30 | parse = FALSE, 31 | manual = FALSE, 32 | orientation = NA, 33 | ... 34 | ) 35 | 36 | geom_signif( 37 | mapping = NULL, 38 | data = NULL, 39 | stat = "signif", 40 | position = "identity", 41 | na.rm = FALSE, 42 | show.legend = NA, 43 | inherit.aes = TRUE, 44 | comparisons = NULL, 45 | test = "wilcox.test", 46 | test.args = NULL, 47 | annotations = NULL, 48 | map_signif_level = FALSE, 49 | y_position = NULL, 50 | xmin = NULL, 51 | xmax = NULL, 52 | margin_top = 0.05, 53 | step_increase = 0, 54 | extend_line = 0, 55 | tip_length = 0.03, 56 | size = 0.5, 57 | textsize = 3.88, 58 | family = "", 59 | vjust = 0, 60 | parse = FALSE, 61 | manual = FALSE, 62 | orientation = NA, 63 | ... 64 | ) 65 | } 66 | \arguments{ 67 | \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}}. If specified and 68 | \code{inherit.aes = TRUE} (the default), it is combined with the default mapping 69 | at the top level of the plot. You must supply \code{mapping} if there is no plot 70 | mapping.} 71 | 72 | \item{data}{The data to be displayed in this layer. There are three 73 | options: 74 | 75 | If \code{NULL}, the default, the data is inherited from the plot 76 | data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. 77 | 78 | A \code{data.frame}, or other object, will override the plot 79 | data. All objects will be fortified to produce a data frame. See 80 | \code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. 81 | 82 | A \code{function} will be called with a single argument, 83 | the plot data. The return value must be a \code{data.frame}, and 84 | will be used as the layer data. A \code{function} can be created 85 | from a \code{formula} (e.g. \code{~ head(.x, 10)}).} 86 | 87 | \item{position}{Position adjustment, either as a string naming the adjustment 88 | (e.g. \code{"jitter"} to use \code{position_jitter}), or the result of a call to a 89 | position adjustment function. Use the latter if you need to change the 90 | settings of the adjustment.} 91 | 92 | \item{na.rm}{If \code{FALSE} (the default), removes missing values with 93 | a warning. If \code{TRUE} silently removes missing values.} 94 | 95 | \item{show.legend}{logical. Should this layer be included in the legends? 96 | \code{NA}, the default, includes if any aesthetics are mapped. 97 | \code{FALSE} never includes, and \code{TRUE} always includes. 98 | It can also be a named logical vector to finely select the aesthetics to 99 | display.} 100 | 101 | \item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, 102 | rather than combining with them. This is most useful for helper functions 103 | that define both data and aesthetics and shouldn't inherit behaviour from 104 | the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} 105 | 106 | \item{comparisons}{A list of length-2 vectors. The entries in the vector are 107 | either the names of 2 values on the x-axis or the 2 integers that 108 | correspond to the index of the columns of interest.} 109 | 110 | \item{test}{the name of the statistical test that is applied to the values of 111 | the 2 columns (e.g. \code{t.test}, \code{wilcox.test} etc.). If you implement a 112 | custom test make sure that it returns a list that has an entry called 113 | \code{p.value}.} 114 | 115 | \item{test.args}{additional arguments for the test method} 116 | 117 | \item{annotations}{character vector with alternative annotations, if not null 118 | test is ignored} 119 | 120 | \item{map_signif_level}{Boolean value, if the p-value are directly written as 121 | annotation or asterisks are used instead. Alternatively one can provide a 122 | named numeric vector to create custom mappings from p-values to annotation: 123 | For example: \code{c("***"=0.001, "**"=0.01, "*"=0.05)}. 124 | Alternatively, one can provide a function that takes a numeric argument 125 | (the p-value) and returns a string.} 126 | 127 | \item{y_position}{numeric vector with the y positions of the brackets} 128 | 129 | \item{xmin, xmax}{numeric vector with the positions of the left and right 130 | sides of the brackets, respectively} 131 | 132 | \item{margin_top}{numeric vector how much higher that the maximum value that 133 | bars start as fraction of total height} 134 | 135 | \item{step_increase}{numeric vector with the increase in fraction of total 136 | height for every additional comparison to minimize overlap.} 137 | 138 | \item{tip_length}{numeric vector with the fraction of total height that the 139 | bar goes down to indicate the precise column} 140 | 141 | \item{size}{change the width of the lines of the bracket} 142 | 143 | \item{textsize}{change the size of the text} 144 | 145 | \item{family}{change the font used for the text} 146 | 147 | \item{vjust}{move the text up or down relative to the bracket} 148 | 149 | \item{parse}{If \code{TRUE}, the labels will be parsed into expressions and 150 | displayed as described in \code{?plotmath}.} 151 | 152 | \item{manual}{Boolean flag that indicates that the parameters are provided 153 | with a data.frame. This option is necessary if one wants to plot different 154 | annotations per facet.} 155 | 156 | \item{orientation}{The orientation of the layer. The default (‘NA’) 157 | automatically determines the orientation from the aesthetic mapping. 158 | In the rare event that this fails it can be given explicitly by setting 159 | 'orientation' to either "x" or "y"} 160 | 161 | \item{...}{other arguments passed on to \code{layer}. These are 162 | often aesthetics, used to set an aesthetic to a fixed value, like 163 | \code{color = "red"} or \code{size = 3}. They may also be parameters 164 | to the paired geom/stat.} 165 | 166 | \item{stat}{The statistical transformation to use on the data for this 167 | layer, either as a \code{ggproto} \code{Geom} subclass or as a string naming the 168 | stat stripped of the \code{stat_} prefix (e.g. \code{"count"} rather than 169 | \code{"stat_count"})} 170 | 171 | \item{extend_line}{Numeric that allows to shorten (negative values) or extend 172 | (positive value) the horizontal line between groups for each comparison; 173 | defaults to 0.} 174 | } 175 | \description{ 176 | Create significance layer 177 | } 178 | \examples{ 179 | \dontrun{ 180 | library(ggplot2) 181 | library(ggsignif) 182 | 183 | ggplot(mpg, aes(class, hwy)) + 184 | geom_boxplot() + 185 | geom_signif(comparisons = list( 186 | c("compact", "pickup"), 187 | c("subcompact", "suv") 188 | )) 189 | 190 | ggplot(mpg, aes(class, hwy)) + 191 | geom_boxplot() + 192 | geom_signif( 193 | comparisons = list( 194 | c("compact", "pickup"), 195 | c("subcompact", "suv") 196 | ), 197 | map_signif_level = function(p) sprintf("p = \%.2g", p) 198 | ) 199 | 200 | ggplot(mpg, aes(class, hwy)) + 201 | geom_boxplot() + 202 | geom_signif( 203 | annotations = c("First", "Second"), 204 | y_position = c(30, 40), xmin = c(4, 1), xmax = c(5, 3) 205 | ) 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /tests/testthat/test-geom_signific_basic.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | 3 | test_that("the plotting works", { 4 | print( 5 | ggplot(mpg, aes(x = manufacturer, y = displ)) + 6 | geom_boxplot() + 7 | stat_signif( 8 | comparisons = list(c("audi", "ford"), c("hyundai", "nissan")), 9 | map_signif_level = TRUE, 10 | test = "wilcox.test", test.args = list(alternative = "two.sided"), 11 | margin_top = 0.02, step_increase = 0, tip_length = 0.01 12 | ) + 13 | theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 14 | facet_wrap(~ as.factor(year), scale = "free") 15 | ) 16 | expect_equal(1, 1) 17 | }) 18 | 19 | test_that("geom works as well as stat works", { 20 | print( 21 | ggplot(mpg, aes(x = manufacturer, y = displ)) + 22 | geom_boxplot() + 23 | geom_signif( 24 | comparisons = list(c("audi", "ford"), c("hyundai", "nissan")), 25 | annotations = c("Interesting", "Too far apart"), 26 | # map_signif_level=TRUE, 27 | test = "wilcox.test", test.args = list(alternative = "two.sided"), 28 | margin_top = 0.02, step_increase = 0, tip_length = 0.01 29 | ) + 30 | theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 31 | facet_wrap(~ as.factor(year), scale = "free") 32 | ) 33 | expect_equal(1, 1) 34 | }) 35 | 36 | 37 | test_that("non-sense gives warning", { 38 | expect_warning(print( 39 | ggplot(mpg, aes(y = hwy, x = displ, group = manufacturer)) + 40 | geom_point() + 41 | stat_signif(comparisons = list(c("audi", "ford"), c("hyundai", "nissan"))) 42 | )) 43 | }) 44 | 45 | 46 | test_that("geom_signif with identity works", { 47 | dat <- data.frame( 48 | Group = c("S1", "S1", "S2", "S2"), 49 | Sub = c("A", "B", "A", "B"), 50 | Value = c(3, 5, 7, 8) 51 | ) 52 | 53 | ## Define base plot 54 | print( 55 | ggplot(dat, aes(Group, Value)) + 56 | geom_bar(aes(fill = Sub), 57 | stat = "identity", 58 | position = "dodge", 59 | width = .5 60 | ) + 61 | geom_signif( 62 | stat = "identity", 63 | data = data.frame( 64 | m = c(0.875, 1.875), 65 | xend = c(1.125, 2.125), 66 | n = c(5.8, 8.5), 67 | annotation = c("***", "NS") 68 | ), 69 | aes( 70 | x = m, 71 | xend = xend, 72 | y = n, 73 | yend = n, 74 | annotation = annotation 75 | ) 76 | ) + 77 | geom_signif( 78 | comparisons = list(c("S1", "S2")), 79 | annotations = "***", 80 | y_position = 9.3, 81 | tip_length = 0 82 | ) + 83 | scale_fill_manual(values = c("grey80", "grey20")) 84 | ) 85 | expect_equal(1, 1) 86 | }) 87 | 88 | 89 | 90 | test_that("you can change the linetype", { 91 | print( 92 | ggplot(iris, aes(x = Species, y = Sepal.Length)) + 93 | geom_boxplot() + 94 | geom_signif( 95 | comparisons = list(c("versicolor", "virginica")), 96 | map_signif_level = TRUE, 97 | linetype = 3, 98 | alpha = 1, 99 | color = "blue", 100 | size = 1, 101 | textsize = 8 102 | ) + 103 | ylim(c(NA, 8.5)) 104 | ) 105 | expect_equal(1, 1) 106 | }) 107 | 108 | 109 | test_that("identical annotations are plotted separetly", { 110 | dat <- data.frame( 111 | Group = c("S1", "S1", "S2", "S2"), 112 | Sub = c("A", "B", "A", "B"), 113 | Value = c(3, 5, 7, 8) 114 | ) 115 | 116 | print( 117 | ggplot(dat, aes(Group, Value)) + 118 | geom_bar(aes(fill = Sub), stat = "identity", position = "dodge", width = .5) + 119 | geom_signif( 120 | comparisons = list(c("S1", "S2")), annotations = "***", 121 | y_position = 9.3, tip_length = 0, vjust = 0.4 122 | ) + 123 | geom_signif( 124 | stat = "identity", 125 | data = data.frame( 126 | x = c(0.875, 1.875), xend = c(1.125, 2.125), 127 | y = c(5.8, 8.5), annotation = c("**", "**") 128 | ), 129 | aes( 130 | x = x, 131 | xend = xend, 132 | y = y, 133 | yend = y, 134 | annotation = annotation, 135 | group = c(1, 2) 136 | ) 137 | ) + 138 | scale_fill_manual(values = c("grey80", "grey20")) 139 | ) 140 | expect_equal(1, 1) 141 | }) 142 | 143 | test_that("multiple comparisons can be made to the same element", { 144 | set.seed(1) 145 | print( 146 | ggplot( 147 | data.frame(y = runif(100), x = sample(c("A", "B", "C", "D"), size = 100, replace = TRUE)), 148 | aes(x = x, y = y) 149 | ) + 150 | geom_boxplot() + 151 | geom_signif( 152 | comparisons = list(c(1, 2), c(2, 3), c(1, 3), c(1, 4), c(2, 4), c(1, 2)), 153 | step_increase = .1 154 | ) 155 | ) 156 | expect_equal(1, 1) 157 | }) 158 | 159 | 160 | test_that("plots with xmin, xmax work", { 161 | library(datasets) 162 | 163 | data(airquality) 164 | airquality$Month <- factor(airquality$Month, 165 | labels = c("May", "Jun", "Jul", "Aug", "Sep") 166 | ) 167 | airquality_trimmed <- airquality[which(airquality$Month == "Jul" | 168 | airquality$Month == "Aug" | 169 | airquality$Month == "Sep"), ] 170 | airquality_trimmed$Temp.f <- factor(ifelse(airquality_trimmed$Temp > mean(airquality_trimmed$Temp), 1, 0), 171 | labels = c("Low temp", "High temp") 172 | ) 173 | 174 | print(ggplot(airquality_trimmed, aes(x = Month, y = Ozone, fill = Temp.f)) + 175 | geom_boxplot(alpha = 0.7, width = 0.4) + 176 | geom_signif( 177 | y_position = 150, xmin = c(0.9, 1.9), xmax = c(1.1, 2.1), 178 | annotations = c("***", "NS") 179 | ) + 180 | scale_y_continuous( 181 | name = "Mean ozone in\nparts per billion", 182 | breaks = seq(0, 175, 25), 183 | limits = c(0, 175) 184 | ) + 185 | scale_x_discrete(name = "Month") + 186 | ggtitle("Boxplot of mean ozone by month") + 187 | theme_bw() + 188 | theme( 189 | plot.title = element_text(size = 14, face = "bold"), 190 | text = element_text(size = 12), 191 | axis.title = element_text(face = "bold"), 192 | axis.text.x = element_text(size = 11), 193 | legend.position = "bottom" 194 | ) + 195 | scale_fill_brewer(palette = "Accent") + 196 | labs(fill = "Temperature")) 197 | expect_equal(1, 1) 198 | }) 199 | 200 | 201 | test_that("manually annotated plots work", { 202 | ggplot( 203 | data = diamonds, 204 | aes( 205 | x = cut, 206 | y = price 207 | ) 208 | ) + 209 | geom_boxplot() + 210 | geom_signif( 211 | data = data.frame( 212 | color = c("E", "E", "G"), 213 | annotations = c("123", "abc", "xyz"), 214 | xmin = c(1, 4, 2), 215 | xmax = c(2, 3, 3) 216 | ), 217 | aes(annotations = annotations, xmin = xmin, xmax = xmax), 218 | manual = TRUE, 219 | y_position = 20000 220 | ) + 221 | facet_wrap(~color) + 222 | ylim(NA, 22000) 223 | expect_equal(1, 1) 224 | }) 225 | 226 | test_that("test method which return text work", { 227 | magnitude_test <- function(x, y, ...) { 228 | change <- mean(y) / mean(x) 229 | p <- t.test(x, y)$p.value 230 | stars <- if (p < 0.001) { 231 | "***" 232 | } else if (p < 0.01) { 233 | "**" 234 | } else if (p < 0.05) { 235 | "*" 236 | } else { 237 | "" 238 | } 239 | list(p.value = paste0(signif(change, digits = 2), stars)) 240 | } 241 | 242 | ggplot(mpg, aes(x = manufacturer, y = displ)) + 243 | geom_boxplot() + 244 | stat_signif( 245 | comparisons = list(c("audi", "ford"), c("hyundai", "nissan")), 246 | test = magnitude_test, 247 | margin_top = 0.02, step_increase = 0, tip_length = 0.01 248 | ) + 249 | theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 250 | facet_wrap(~ as.factor(year), scale = "free") 251 | expect_equal(1, 1) 252 | }) 253 | -------------------------------------------------------------------------------- /paper/arxiv.sty: -------------------------------------------------------------------------------- 1 | \NeedsTeXFormat{LaTeX2e} 2 | 3 | \ProcessOptions\relax 4 | 5 | % fonts 6 | \renewcommand{\rmdefault}{ptm} 7 | \renewcommand{\sfdefault}{phv} 8 | 9 | % set page geometry 10 | \usepackage[verbose=true,letterpaper]{geometry} 11 | \AtBeginDocument{ 12 | \newgeometry{ 13 | textheight=9in, 14 | textwidth=6.5in, 15 | top=1in, 16 | headheight=14pt, 17 | headsep=25pt, 18 | footskip=30pt 19 | } 20 | } 21 | 22 | \widowpenalty=10000 23 | \clubpenalty=10000 24 | \flushbottom 25 | \sloppy 26 | 27 | 28 | 29 | \newcommand{\headeright}{A Preprint} 30 | \newcommand{\undertitle}{A Preprint} 31 | \newcommand{\shorttitle}{\@title} 32 | 33 | \usepackage{fancyhdr} 34 | \fancyhf{} 35 | \pagestyle{fancy} 36 | \renewcommand{\headrulewidth}{0.4pt} 37 | \fancyheadoffset{0pt} 38 | \rhead{\scshape \footnotesize \headeright} 39 | \chead{\shorttitle} 40 | \cfoot{\thepage} 41 | 42 | 43 | %Handling Keywords 44 | \def\keywordname{{\bfseries \emph{Keywords}}}% 45 | \def\keywords#1{\par\addvspace\medskipamount{\rightskip=0pt plus1cm 46 | \def\and{\ifhmode\unskip\nobreak\fi\ $\cdot$ 47 | }\noindent\keywordname\enspace\ignorespaces#1\par}} 48 | 49 | % font sizes with reduced leading 50 | \renewcommand{\normalsize}{% 51 | \@setfontsize\normalsize\@xpt\@xipt 52 | \abovedisplayskip 7\p@ \@plus 2\p@ \@minus 5\p@ 53 | \abovedisplayshortskip \z@ \@plus 3\p@ 54 | \belowdisplayskip \abovedisplayskip 55 | \belowdisplayshortskip 4\p@ \@plus 3\p@ \@minus 3\p@ 56 | } 57 | \normalsize 58 | \renewcommand{\small}{% 59 | \@setfontsize\small\@ixpt\@xpt 60 | \abovedisplayskip 6\p@ \@plus 1.5\p@ \@minus 4\p@ 61 | \abovedisplayshortskip \z@ \@plus 2\p@ 62 | \belowdisplayskip \abovedisplayskip 63 | \belowdisplayshortskip 3\p@ \@plus 2\p@ \@minus 2\p@ 64 | } 65 | \renewcommand{\footnotesize}{\@setfontsize\footnotesize\@ixpt\@xpt} 66 | \renewcommand{\scriptsize}{\@setfontsize\scriptsize\@viipt\@viiipt} 67 | \renewcommand{\tiny}{\@setfontsize\tiny\@vipt\@viipt} 68 | \renewcommand{\large}{\@setfontsize\large\@xiipt{14}} 69 | \renewcommand{\Large}{\@setfontsize\Large\@xivpt{16}} 70 | \renewcommand{\LARGE}{\@setfontsize\LARGE\@xviipt{20}} 71 | \renewcommand{\huge}{\@setfontsize\huge\@xxpt{23}} 72 | \renewcommand{\Huge}{\@setfontsize\Huge\@xxvpt{28}} 73 | 74 | % sections with less space 75 | \providecommand{\section}{} 76 | \renewcommand{\section}{% 77 | \@startsection{section}{1}{\z@}% 78 | {-2.0ex \@plus -0.5ex \@minus -0.2ex}% 79 | { 1.5ex \@plus 0.3ex \@minus 0.2ex}% 80 | {\large\bf\raggedright}% 81 | } 82 | \providecommand{\subsection}{} 83 | \renewcommand{\subsection}{% 84 | \@startsection{subsection}{2}{\z@}% 85 | {-1.8ex \@plus -0.5ex \@minus -0.2ex}% 86 | { 0.8ex \@plus 0.2ex}% 87 | {\normalsize\bf\raggedright}% 88 | } 89 | \providecommand{\subsubsection}{} 90 | \renewcommand{\subsubsection}{% 91 | \@startsection{subsubsection}{3}{\z@}% 92 | {-1.5ex \@plus -0.5ex \@minus -0.2ex}% 93 | { 0.5ex \@plus 0.2ex}% 94 | {\normalsize\bf\raggedright}% 95 | } 96 | \providecommand{\paragraph}{} 97 | \renewcommand{\paragraph}{% 98 | \@startsection{paragraph}{4}{\z@}% 99 | {1.5ex \@plus 0.5ex \@minus 0.2ex}% 100 | {-1em}% 101 | {\normalsize\bf}% 102 | } 103 | \providecommand{\subparagraph}{} 104 | \renewcommand{\subparagraph}{% 105 | \@startsection{subparagraph}{5}{\z@}% 106 | {1.5ex \@plus 0.5ex \@minus 0.2ex}% 107 | {-1em}% 108 | {\normalsize\bf}% 109 | } 110 | \providecommand{\subsubsubsection}{} 111 | \renewcommand{\subsubsubsection}{% 112 | \vskip5pt{\noindent\normalsize\rm\raggedright}% 113 | } 114 | 115 | % float placement 116 | \renewcommand{\topfraction }{0.85} 117 | \renewcommand{\bottomfraction }{0.4} 118 | \renewcommand{\textfraction }{0.1} 119 | \renewcommand{\floatpagefraction}{0.7} 120 | 121 | \newlength{\@abovecaptionskip}\setlength{\@abovecaptionskip}{7\p@} 122 | \newlength{\@belowcaptionskip}\setlength{\@belowcaptionskip}{\z@} 123 | 124 | \setlength{\abovecaptionskip}{\@abovecaptionskip} 125 | \setlength{\belowcaptionskip}{\@belowcaptionskip} 126 | 127 | % swap above/belowcaptionskip lengths for tables 128 | \renewenvironment{table} 129 | {\setlength{\abovecaptionskip}{\@belowcaptionskip}% 130 | \setlength{\belowcaptionskip}{\@abovecaptionskip}% 131 | \@float{table}} 132 | {\end@float} 133 | 134 | % footnote formatting 135 | \setlength{\footnotesep }{6.65\p@} 136 | \setlength{\skip\footins}{9\p@ \@plus 4\p@ \@minus 2\p@} 137 | \renewcommand{\footnoterule}{\kern-3\p@ \hrule width 12pc \kern 2.6\p@} 138 | \setcounter{footnote}{0} 139 | 140 | % paragraph formatting 141 | \setlength{\parindent}{\z@} 142 | \setlength{\parskip }{5.5\p@} 143 | 144 | % list formatting 145 | \setlength{\topsep }{4\p@ \@plus 1\p@ \@minus 2\p@} 146 | \setlength{\partopsep }{1\p@ \@plus 0.5\p@ \@minus 0.5\p@} 147 | \setlength{\itemsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@} 148 | \setlength{\parsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@} 149 | \setlength{\leftmargin }{3pc} 150 | \setlength{\leftmargini }{\leftmargin} 151 | \setlength{\leftmarginii }{2em} 152 | \setlength{\leftmarginiii}{1.5em} 153 | \setlength{\leftmarginiv }{1.0em} 154 | \setlength{\leftmarginv }{0.5em} 155 | \def\@listi {\leftmargin\leftmargini} 156 | \def\@listii {\leftmargin\leftmarginii 157 | \labelwidth\leftmarginii 158 | \advance\labelwidth-\labelsep 159 | \topsep 2\p@ \@plus 1\p@ \@minus 0.5\p@ 160 | \parsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@ 161 | \itemsep \parsep} 162 | \def\@listiii{\leftmargin\leftmarginiii 163 | \labelwidth\leftmarginiii 164 | \advance\labelwidth-\labelsep 165 | \topsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@ 166 | \parsep \z@ 167 | \partopsep 0.5\p@ \@plus 0\p@ \@minus 0.5\p@ 168 | \itemsep \topsep} 169 | \def\@listiv {\leftmargin\leftmarginiv 170 | \labelwidth\leftmarginiv 171 | \advance\labelwidth-\labelsep} 172 | \def\@listv {\leftmargin\leftmarginv 173 | \labelwidth\leftmarginv 174 | \advance\labelwidth-\labelsep} 175 | \def\@listvi {\leftmargin\leftmarginvi 176 | \labelwidth\leftmarginvi 177 | \advance\labelwidth-\labelsep} 178 | 179 | % create title 180 | \providecommand{\maketitle}{} 181 | \renewcommand{\maketitle}{% 182 | \par 183 | \begingroup 184 | \renewcommand{\thefootnote}{\fnsymbol{footnote}} 185 | % for perfect author name centering 186 | \renewcommand{\@makefnmark}{\hbox to \z@{$^{\@thefnmark}$\hss}} 187 | % The footnote-mark was overlapping the footnote-text, 188 | % added the following to fix this problem (MK) 189 | \long\def\@makefntext##1{% 190 | \parindent 1em\noindent 191 | \hbox to 1.8em{\hss $\m@th ^{\@thefnmark}$}##1 192 | } 193 | \thispagestyle{empty} 194 | \@maketitle 195 | \@thanks 196 | %\@notice 197 | \endgroup 198 | \let\maketitle\relax 199 | \let\thanks\relax 200 | } 201 | 202 | % rules for title box at top of first page 203 | \newcommand{\@toptitlebar}{ 204 | \hrule height 2\p@ 205 | \vskip 0.25in 206 | \vskip -\parskip% 207 | } 208 | \newcommand{\@bottomtitlebar}{ 209 | \vskip 0.29in 210 | \vskip -\parskip 211 | \hrule height 2\p@ 212 | \vskip 0.09in% 213 | } 214 | 215 | % create title (includes both anonymized and non-anonymized versions) 216 | \providecommand{\@maketitle}{} 217 | \renewcommand{\@maketitle}{% 218 | \vbox{% 219 | \hsize\textwidth 220 | \linewidth\hsize 221 | \vskip 0.1in 222 | \@toptitlebar 223 | \centering 224 | {\LARGE\sc \@title\par} 225 | \@bottomtitlebar 226 | \textsc{\undertitle}\\ 227 | \vskip 0.1in 228 | \def\And{% 229 | \end{tabular}\hfil\linebreak[0]\hfil% 230 | \begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces% 231 | } 232 | \def\AND{% 233 | \end{tabular}\hfil\linebreak[4]\hfil% 234 | \begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces% 235 | } 236 | \begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\@author\end{tabular}% 237 | \vskip 0.4in \@minus 0.1in \center{\@date} \vskip 0.2in 238 | } 239 | } 240 | 241 | % add conference notice to bottom of first page 242 | \newcommand{\ftype@noticebox}{8} 243 | \newcommand{\@notice}{% 244 | % give a bit of extra room back to authors on first page 245 | \enlargethispage{2\baselineskip}% 246 | \@float{noticebox}[b]% 247 | \footnotesize\@noticestring% 248 | \end@float% 249 | } 250 | 251 | % abstract styling 252 | \renewenvironment{abstract} 253 | { 254 | \centerline 255 | {\large \bfseries \scshape Abstract} 256 | \begin{quote} 257 | } 258 | { 259 | \end{quote} 260 | } 261 | 262 | \endinput 263 | -------------------------------------------------------------------------------- /paper/paper.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | 3 | \usepackage{arxiv} 4 | 5 | \usepackage[utf8]{inputenc} % allow utf-8 input 6 | \usepackage[T1]{fontenc} % use 8-bit T1 fonts 7 | \usepackage{lmodern} % https://github.com/rstudio/rticles/issues/343 8 | \usepackage{hyperref} % hyperlinks 9 | \usepackage{url} % simple URL typesetting 10 | \usepackage{booktabs} % professional-quality tables 11 | \usepackage{amsfonts} % blackboard math symbols 12 | \usepackage{nicefrac} % compact symbols for 1/2, etc. 13 | \usepackage{microtype} % microtypography 14 | \usepackage{lipsum} 15 | \usepackage{graphicx} 16 | 17 | \title{ggsignif: R Package for Displaying Significance Brackets for 18 | `ggplot2'} 19 | 20 | \author{ 21 | Constantin Ahlmann-Eltze 22 | \\ 23 | The European Molecular Biology Laboratory, Heidelberg, Germany \\ 24 | \\ 25 | \texttt{} \\ 26 | \And 27 | Indrajeet Patil 28 | \\ 29 | Center for Humans and Machines, Max Planck Institute for Human 30 | Development, Berlin, Germany \\ 31 | \\ 32 | \texttt{} \\ 33 | } 34 | 35 | \usepackage{color} 36 | \usepackage{fancyvrb} 37 | \newcommand{\VerbBar}{|} 38 | \newcommand{\VERB}{\Verb[commandchars=\\\{\}]} 39 | \DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}} 40 | % Add ',fontsize=\small' for more characters per line 41 | \usepackage{framed} 42 | \definecolor{shadecolor}{RGB}{248,248,248} 43 | \newenvironment{Shaded}{\begin{snugshade}}{\end{snugshade}} 44 | \newcommand{\AlertTok}[1]{\textcolor[rgb]{0.94,0.16,0.16}{#1}} 45 | \newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}} 46 | \newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.77,0.63,0.00}{#1}} 47 | \newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.00,0.00,0.81}{#1}} 48 | \newcommand{\BuiltInTok}[1]{#1} 49 | \newcommand{\CharTok}[1]{\textcolor[rgb]{0.31,0.60,0.02}{#1}} 50 | \newcommand{\CommentTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textit{#1}}} 51 | \newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}} 52 | \newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.00,0.00,0.00}{#1}} 53 | \newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{\textbf{#1}}} 54 | \newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{#1}} 55 | \newcommand{\DecValTok}[1]{\textcolor[rgb]{0.00,0.00,0.81}{#1}} 56 | \newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}} 57 | \newcommand{\ErrorTok}[1]{\textcolor[rgb]{0.64,0.00,0.00}{\textbf{#1}}} 58 | \newcommand{\ExtensionTok}[1]{#1} 59 | \newcommand{\FloatTok}[1]{\textcolor[rgb]{0.00,0.00,0.81}{#1}} 60 | \newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.00,0.00,0.00}{#1}} 61 | \newcommand{\ImportTok}[1]{#1} 62 | \newcommand{\InformationTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}} 63 | \newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.13,0.29,0.53}{\textbf{#1}}} 64 | \newcommand{\NormalTok}[1]{#1} 65 | \newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.81,0.36,0.00}{\textbf{#1}}} 66 | \newcommand{\OtherTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{#1}} 67 | \newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textit{#1}}} 68 | \newcommand{\RegionMarkerTok}[1]{#1} 69 | \newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.00,0.00,0.00}{#1}} 70 | \newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.31,0.60,0.02}{#1}} 71 | \newcommand{\StringTok}[1]{\textcolor[rgb]{0.31,0.60,0.02}{#1}} 72 | \newcommand{\VariableTok}[1]{\textcolor[rgb]{0.00,0.00,0.00}{#1}} 73 | \newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.31,0.60,0.02}{#1}} 74 | \newcommand{\WarningTok}[1]{\textcolor[rgb]{0.56,0.35,0.01}{\textbf{\textit{#1}}}} 75 | 76 | % Pandoc citation processing 77 | \newlength{\csllabelwidth} 78 | \setlength{\csllabelwidth}{3em} 79 | \newlength{\cslhangindent} 80 | \setlength{\cslhangindent}{1.5em} 81 | % for Pandoc 2.8 to 2.10.1 82 | \newenvironment{cslreferences}% 83 | {}% 84 | {\par} 85 | % For Pandoc 2.11+ 86 | \newenvironment{CSLReferences}[3] % #1 hanging-ident, #2 entry spacing 87 | {% don't indent paragraphs 88 | \setlength{\parindent}{0pt} 89 | % turn on hanging indent if param 1 is 1 90 | \ifodd #1 \everypar{\setlength{\hangindent}{\cslhangindent}}\ignorespaces\fi 91 | % set entry spacing 92 | \ifnum #2 > 0 93 | \setlength{\parskip}{#2\baselineskip} 94 | \fi 95 | }% 96 | {} 97 | \usepackage{calc} % for calculating minipage widths 98 | \newcommand{\CSLBlock}[1]{#1\hfill\break} 99 | \newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}} 100 | \newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{#1}} 101 | \newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1} 102 | 103 | 104 | 105 | \begin{document} 106 | \maketitle 107 | 108 | \def\tightlist{} 109 | 110 | 111 | \begin{abstract} 112 | Research hypotheses are often concerned with the difference between two 113 | groups and statistical tests provide indicators (like \emph{p}-values or 114 | Bayes factors) about the evidence for or against such differences. The R 115 | package, \texttt{ggsignif} provides a quick way to visualize such 116 | pairwise indicators as an annotation in a plot, for example showing if a 117 | difference is statistically significant. \texttt{ggsignif} follows the 118 | principles of the grammar of graphics 119 | (\protect\hyperlink{ref-Wilkinson2012}{Wilkinson, 2012}) and provides a 120 | new layer that can be added to plots made with the \texttt{ggplot2} 121 | package (\protect\hyperlink{ref-Wickham2016}{Wickham, 2016}). 122 | \end{abstract} 123 | 124 | \keywords{ 125 | R 126 | \and 127 | ggplot2 128 | \and 129 | ggplot2-extension 130 | } 131 | 132 | \hypertarget{statement-of-need}{% 133 | \section{Statement of Need}\label{statement-of-need}} 134 | 135 | During the exploratory analysis of data with discrete covariates, 136 | researchers commonly start with a one-way ANOVA to see if there are any 137 | differences in the group means. This is typically followed up with 138 | multiple comparisons to see if specific levels of the discrete 139 | covariates differ significantly. The \texttt{ggsignif} package provides 140 | a way to graphically display all or a few (depending on the context of 141 | the research hypotheses) of such comparisons. It is also used by other R 142 | package developers as the back-end for graphical display of pairwise 143 | comparisons, such as \texttt{ggpubr} 144 | (\protect\hyperlink{ref-Kassambara2020}{Kassambara, 2020}), 145 | \texttt{ggstatsplot} (\protect\hyperlink{ref-Patil2018}{Patil, 2018}), 146 | and more. These packages demonstrate how \texttt{ggsignif} can be 147 | extended to display results from any type of pairwise comparisons test 148 | (e.g., Bayesian \emph{t}-test, Dunn test, etc.). 149 | 150 | \hypertarget{features}{% 151 | \section{Features}\label{features}} 152 | 153 | The following is a simple example demonstrating how a group difference 154 | can be annotated using \texttt{geom\_signif} layers from 155 | \texttt{ggsignif} package. 156 | 157 | \begin{Shaded} 158 | \begin{Highlighting}[] 159 | \FunctionTok{set.seed}\NormalTok{(}\DecValTok{123}\NormalTok{)} 160 | \FunctionTok{library}\NormalTok{(ggplot2)} 161 | \FunctionTok{library}\NormalTok{(ggsignif)} 162 | 163 | \FunctionTok{ggplot}\NormalTok{(mpg, }\FunctionTok{aes}\NormalTok{(class, hwy)) }\SpecialCharTok{+} 164 | \FunctionTok{geom\_boxplot}\NormalTok{() }\SpecialCharTok{+} 165 | \FunctionTok{geom\_signif}\NormalTok{(} 166 | \AttributeTok{comparisons =} \FunctionTok{list}\NormalTok{(}\FunctionTok{c}\NormalTok{(}\StringTok{"compact"}\NormalTok{, }\StringTok{"midsize"}\NormalTok{), }\FunctionTok{c}\NormalTok{(}\StringTok{"minivan"}\NormalTok{, }\StringTok{"suv"}\NormalTok{)),} 167 | \AttributeTok{map\_signif\_level =} \ConstantTok{TRUE} 168 | \NormalTok{ ) }\SpecialCharTok{+} 169 | \FunctionTok{ylim}\NormalTok{(}\ConstantTok{NA}\NormalTok{, }\DecValTok{48}\NormalTok{)} 170 | \end{Highlighting} 171 | \end{Shaded} 172 | 173 | \includegraphics[width=1\linewidth]{paper_files/figure-latex/simpe_comparison-1} 174 | 175 | For more advanced examples, the readers are encouraged to read the 176 | package website: \url{https://const-ae.github.io/ggsignif/}. 177 | 178 | \hypertarget{licensing-and-availability}{% 179 | \section{Licensing and Availability}\label{licensing-and-availability}} 180 | 181 | \texttt{ggsignif} is licensed under the GNU General Public License 182 | (v3.0), with all source code stored at 183 | \href{https://github.com/const-ae/ggsignif}{GitHub}, and with a 184 | corresponding issue tracker for bug reporting and feature enhancements. 185 | In the spirit of honest and open science, we encourage requests/tips for 186 | fixes, feature updates, as well as general questions and concerns via 187 | direct interaction with contributors and developers, by 188 | \href{https://github.com/const-ae/ggsignif/issues}{filing an issue}. See 189 | the 190 | \href{https://github.com/const-ae/ggsignif/blob/master/CODE_OF_CONDUCT.md}{\emph{Contribution 191 | Guidelines}} for this package. 192 | 193 | \hypertarget{acknowledgements}{% 194 | \section{Acknowledgements}\label{acknowledgements}} 195 | 196 | We would like to thank the users of \texttt{ggsignif} package for 197 | reporting bugs and for providing valuable feedback. 198 | 199 | This work was supported by the EMBL International PhD Programme 200 | (C.A.E.). 201 | 202 | \hypertarget{references}{% 203 | \section*{References}\label{references}} 204 | \addcontentsline{toc}{section}{References} 205 | 206 | \hypertarget{refs}{} 207 | \begin{CSLReferences}{1}{0} 208 | \leavevmode\hypertarget{ref-Kassambara2020}{}% 209 | Kassambara, A. (2020). \emph{{ggpubr}: '{ggplot2}' based publication 210 | ready plots}. Retrieved from 211 | \url{https://CRAN.R-project.org/package=ggpubr} 212 | 213 | \leavevmode\hypertarget{ref-Patil2018}{}% 214 | Patil, I. (2018). {ggstatsplot}: '{ggplot2}' based plots with 215 | statistical details. \emph{CRAN}. 216 | doi:\href{https://doi.org/10.5281/zenodo.2074621}{10.5281/zenodo.2074621} 217 | 218 | \leavevmode\hypertarget{ref-Wickham2016}{}% 219 | Wickham, H. (2016). \emph{{ggplot2}: Elegant graphics for data 220 | analysis}. Springer-Verlag New York. Retrieved from 221 | \url{https://ggplot2.tidyverse.org} 222 | 223 | \leavevmode\hypertarget{ref-Wilkinson2012}{}% 224 | Wilkinson, L. (2012). The grammar of graphics. \emph{Handbook of 225 | computational statistics} (pp. 375--414). Springer. 226 | 227 | \end{CSLReferences} 228 | 229 | \bibliographystyle{unsrt} 230 | \bibliography{paper.bib} 231 | 232 | 233 | \end{document} 234 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/vdiffr/flipping-aesthetics-works.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 2.8e-14 78 | 79 | 80 | 81 | 1.8e-14 82 | 83 | 84 | 85 | 86 | 87 | 88 | 2seater 89 | compact 90 | midsize 91 | minivan 92 | pickup 93 | subcompact 94 | suv 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 20 106 | 30 107 | 40 108 | hwy 109 | class 110 | flipping aesthetics works 111 | 112 | 113 | -------------------------------------------------------------------------------- /paper/apa.csl: -------------------------------------------------------------------------------- 1 | 2 | 444 | -------------------------------------------------------------------------------- /R/geom_signif.R: -------------------------------------------------------------------------------- 1 | #' Create significance layer 2 | #' 3 | #' @param comparisons A list of length-2 vectors. The entries in the vector are 4 | #' either the names of 2 values on the x-axis or the 2 integers that 5 | #' correspond to the index of the columns of interest. 6 | #' @param test the name of the statistical test that is applied to the values of 7 | #' the 2 columns (e.g. `t.test`, `wilcox.test` etc.). If you implement a 8 | #' custom test make sure that it returns a list that has an entry called 9 | #' `p.value`. 10 | #' @param test.args additional arguments for the test method 11 | #' @param annotations character vector with alternative annotations, if not null 12 | #' test is ignored 13 | #' @param map_signif_level Boolean value, if the p-value are directly written as 14 | #' annotation or asterisks are used instead. Alternatively one can provide a 15 | #' named numeric vector to create custom mappings from p-values to annotation: 16 | #' For example: `c("***"=0.001, "**"=0.01, "*"=0.05)`. 17 | #' Alternatively, one can provide a function that takes a numeric argument 18 | #' (the p-value) and returns a string. 19 | #' @param xmin,xmax numeric vector with the positions of the left and right 20 | #' sides of the brackets, respectively 21 | #' @param y_position numeric vector with the y positions of the brackets 22 | #' @param size change the width of the lines of the bracket 23 | #' @param textsize change the size of the text 24 | #' @param family change the font used for the text 25 | #' @param vjust move the text up or down relative to the bracket 26 | #' @param margin_top numeric vector how much higher that the maximum value that 27 | #' bars start as fraction of total height 28 | #' @param step_increase numeric vector with the increase in fraction of total 29 | #' height for every additional comparison to minimize overlap. 30 | #' @param extend_line Numeric that allows to shorten (negative values) or extend 31 | #' (positive value) the horizontal line between groups for each comparison; 32 | #' defaults to 0. 33 | #' @param tip_length numeric vector with the fraction of total height that the 34 | #' bar goes down to indicate the precise column 35 | #' @param parse If `TRUE`, the labels will be parsed into expressions and 36 | #' displayed as described in `?plotmath`. 37 | #' @param manual Boolean flag that indicates that the parameters are provided 38 | #' with a data.frame. This option is necessary if one wants to plot different 39 | #' annotations per facet. 40 | #' @param na.rm If `FALSE` (the default), removes missing values with 41 | #' a warning. If `TRUE` silently removes missing values. 42 | #' @param orientation The orientation of the layer. The default (‘NA’) 43 | #' automatically determines the orientation from the aesthetic mapping. 44 | #' In the rare event that this fails it can be given explicitly by setting 45 | #' 'orientation' to either "x" or "y" 46 | #' @param ... other arguments passed on to `layer`. These are 47 | #' often aesthetics, used to set an aesthetic to a fixed value, like 48 | #' `color = "red"` or `size = 3`. They may also be parameters 49 | #' to the paired geom/stat. 50 | #' 51 | #' @inheritParams ggplot2::layer 52 | #' 53 | #' @examples 54 | #' \dontrun{ 55 | #' library(ggplot2) 56 | #' library(ggsignif) 57 | #' 58 | #' ggplot(mpg, aes(class, hwy)) + 59 | #' geom_boxplot() + 60 | #' geom_signif(comparisons = list( 61 | #' c("compact", "pickup"), 62 | #' c("subcompact", "suv") 63 | #' )) 64 | #' 65 | #' ggplot(mpg, aes(class, hwy)) + 66 | #' geom_boxplot() + 67 | #' geom_signif( 68 | #' comparisons = list( 69 | #' c("compact", "pickup"), 70 | #' c("subcompact", "suv") 71 | #' ), 72 | #' map_signif_level = function(p) sprintf("p = %.2g", p) 73 | #' ) 74 | #' 75 | #' ggplot(mpg, aes(class, hwy)) + 76 | #' geom_boxplot() + 77 | #' geom_signif( 78 | #' annotations = c("First", "Second"), 79 | #' y_position = c(30, 40), xmin = c(4, 1), xmax = c(5, 3) 80 | #' ) 81 | #' } 82 | #' 83 | #' @export 84 | stat_signif <- function(mapping = NULL, 85 | data = NULL, 86 | position = "identity", 87 | na.rm = FALSE, 88 | show.legend = NA, 89 | inherit.aes = TRUE, 90 | comparisons = NULL, 91 | test = "wilcox.test", 92 | test.args = NULL, 93 | annotations = NULL, 94 | map_signif_level = FALSE, 95 | y_position = NULL, 96 | xmin = NULL, 97 | xmax = NULL, 98 | margin_top = 0.05, 99 | step_increase = 0, 100 | tip_length = 0.03, 101 | size = 0.5, 102 | textsize = 3.88, 103 | family = "", 104 | vjust = 0, 105 | parse = FALSE, 106 | manual = FALSE, 107 | orientation = NA, 108 | ...) { 109 | if (manual) { 110 | if (!is.null(data) && !is.null(mapping)) { 111 | if (!"x" %in% names(data)) mapping$x <- 1 112 | if (!"y" %in% names(data)) mapping$y <- 1 113 | } else { 114 | stop("If manual mode is selected you need to provide the data and mapping parameters") 115 | } 116 | } 117 | 118 | ggplot2::layer( 119 | stat = StatSignif, data = data, mapping = mapping, geom = "signif", 120 | position = position, show.legend = show.legend, inherit.aes = inherit.aes, 121 | params = list( 122 | comparisons = comparisons, 123 | test = test, 124 | test.args = test.args, 125 | annotations = annotations, 126 | map_signif_level = map_signif_level, 127 | y_position = y_position, 128 | xmin = xmin, 129 | xmax = xmax, 130 | margin_top = margin_top, 131 | step_increase = step_increase, 132 | tip_length = tip_length, 133 | size = size, 134 | textsize = textsize, 135 | family = family, 136 | vjust = vjust, 137 | parse = parse, 138 | manual = manual, 139 | na.rm = na.rm, 140 | orientation = orientation, 141 | ... 142 | ) 143 | ) 144 | } 145 | 146 | 147 | GeomSignif <- ggplot2::ggproto( 148 | "GeomSignif", 149 | ggplot2::Geom, 150 | required_aes = c("x", "xend", "y", "yend", "annotation"), 151 | default_aes = ggplot2::aes( 152 | shape = 19, 153 | colour = "black", 154 | textsize = 3.88, 155 | angle = 0, 156 | hjust = 0.5, 157 | vjust = 0, 158 | alpha = NA, 159 | family = "", 160 | fontface = 1, 161 | lineheight = 1.2, 162 | linetype = 1, 163 | size = 0.5 164 | ), 165 | extra_params = c("na.rm", "orientation"), 166 | setup_params = function(data, params) { 167 | params$flipped_aes <- ggplot2::has_flipped_aes(data, params) 168 | return(params) 169 | }, 170 | draw_key = function(...) { 171 | grid::nullGrob() 172 | }, 173 | draw_group = function(data, 174 | panel_params, 175 | coord, 176 | parse = FALSE, 177 | extend_line = 0, 178 | flipped_aes = FALSE) { 179 | lab <- as.character(data$annotation) 180 | if (parse) { 181 | lab <- parse_safe(as.character(lab)) 182 | } 183 | 184 | coords <- coord$transform(data, panel_params) 185 | if (extend_line != 0 && nrow(coords) == 3) { 186 | if (coords[2, "x"] > coords[2, "xend"]) { 187 | extend_line <- -extend_line 188 | } 189 | 190 | # left vertical segment 191 | coords[1, "x"] <- coords[1, "x"] - extend_line 192 | coords[1, "xend"] <- coords[1, "xend"] - extend_line 193 | 194 | # horizontal line 195 | coords[2, "x"] <- coords[2, "x"] - extend_line 196 | coords[2, "xend"] <- coords[2, "xend"] + extend_line 197 | 198 | # right vertical segment 199 | coords[3, "x"] <- coords[3, "x"] + extend_line 200 | coords[3, "xend"] <- coords[3, "xend"] + extend_line 201 | } 202 | 203 | clp_flag <- inherits(coord, "CoordFlip") 204 | 205 | if (!any(flipped_aes, clp_flag) || all(flipped_aes, clp_flag)) { 206 | text_x <- mean(c(coords$x[1], tail(coords$xend, n = 1))) 207 | text_y <- max(c(coords$y, coords$yend)) + 0.01 208 | } else { 209 | text_x <- max(c(coords$x, coords$xend)) + 0.01 210 | text_y <- mean(c(coords$y[1], tail(coords$yend, n = 1))) 211 | 212 | if (all(coords$angle == 0)) { 213 | coords$angle <- 270 214 | } 215 | } 216 | 217 | grid::gList( 218 | grid::textGrob( 219 | label = lab, 220 | x = text_x, # mean(c(coords$x[1], tail(coords$xend, n = 1))), 221 | y = text_y, # max(c(coords$y, coords$yend)) + 0.01, 222 | default.units = "native", 223 | hjust = coords$hjust, vjust = coords$vjust, 224 | rot = coords$angle, 225 | gp = grid::gpar( 226 | col = scales::alpha(coords$colour, coords$alpha), 227 | fontsize = coords$textsize * ggplot2::.pt, 228 | fontfamily = coords$family, 229 | fontface = coords$fontface, 230 | lineheight = coords$lineheight 231 | ) 232 | ), 233 | grid::segmentsGrob( 234 | coords$x, coords$y, 235 | default.units = "native", 236 | coords$xend, coords$yend, 237 | gp = grid::gpar( 238 | col = scales::alpha(coords$colour, coords$alpha), 239 | lty = coords$linetype, 240 | lwd = coords$size * ggplot2::.pt 241 | ) 242 | ) 243 | ) 244 | } 245 | ) 246 | 247 | #' @rdname stat_signif 248 | #' @export 249 | geom_signif <- function(mapping = NULL, 250 | data = NULL, 251 | stat = "signif", 252 | position = "identity", 253 | na.rm = FALSE, 254 | show.legend = NA, 255 | inherit.aes = TRUE, 256 | comparisons = NULL, 257 | test = "wilcox.test", 258 | test.args = NULL, 259 | annotations = NULL, 260 | map_signif_level = FALSE, 261 | y_position = NULL, 262 | xmin = NULL, 263 | xmax = NULL, 264 | margin_top = 0.05, 265 | step_increase = 0, 266 | extend_line = 0, 267 | tip_length = 0.03, 268 | size = 0.5, 269 | textsize = 3.88, 270 | family = "", 271 | vjust = 0, 272 | parse = FALSE, 273 | manual = FALSE, 274 | orientation = NA, 275 | ...) { 276 | params <- list(na.rm = na.rm, ...) 277 | 278 | if (identical(stat, "signif")) { 279 | if (!is.null(data) && !is.null(mapping) && !manual) { 280 | warning("You have set data and mapping, are you sure that manual = FALSE is correct?") 281 | } 282 | 283 | if (manual) { 284 | if (is.null(mapping$annotations)) { 285 | stop("Manual mode only works if with 'annotations' is provided in mapping") 286 | } 287 | 288 | if (!is.null(data) && !is.null(mapping)) { 289 | if (!"x" %in% names(mapping)) { 290 | if ("xmin" %in% names(mapping)) { 291 | mapping$x <- mapping$xmin 292 | } else { 293 | mapping$x <- xmin 294 | } 295 | } 296 | 297 | if (!"y" %in% names(mapping)) { 298 | if ("y_position" %in% names(mapping)) { 299 | mapping$y <- mapping$y_position 300 | } else { 301 | mapping$y <- y_position 302 | } 303 | } 304 | } else { 305 | stop("If manual mode is selected you need to provide the data and mapping parameters") 306 | } 307 | } 308 | 309 | params <- c( 310 | params, 311 | list( 312 | comparisons = comparisons, 313 | test = test, 314 | test.args = test.args, 315 | annotations = annotations, 316 | map_signif_level = map_signif_level, 317 | y_position = y_position, 318 | xmin = xmin, 319 | xmax = xmax, 320 | margin_top = margin_top, 321 | step_increase = step_increase, 322 | extend_line = extend_line, 323 | tip_length = tip_length, 324 | size = size, 325 | textsize = textsize, 326 | family = family, 327 | vjust = vjust, 328 | parse = parse, 329 | manual = manual, 330 | orientation = orientation 331 | ) 332 | ) 333 | } 334 | 335 | ggplot2::layer( 336 | stat = stat, 337 | geom = GeomSignif, 338 | mapping = mapping, 339 | data = data, 340 | position = position, 341 | show.legend = show.legend, 342 | inherit.aes = inherit.aes, 343 | params = params 344 | ) 345 | } 346 | 347 | 348 | StatSignif <- ggplot2::ggproto( 349 | "StatSignif", 350 | ggplot2::Stat, 351 | required_aes = c("x", "y", "group"), 352 | extra_params = c("na.rm", "orientation"), 353 | setup_params = function(data, params) { 354 | # if(any(data$group == -1)|| any(data$group != data$x)){ 355 | params$flipped_aes <- ggplot2::has_flipped_aes(data, params) 356 | data <- ggplot2::flip_data(data, params$flipped_aes) 357 | 358 | if (any(data$group == -1)) { 359 | stop("Can only handle data with groups that are plotted on the x-axis") 360 | } 361 | 362 | if (is.character(params$test)) params$test <- match.fun(params$test) 363 | params$complete_data <- data 364 | 365 | if (is.null(params$xmin) != is.null(params$xmax) || length(params$xmin) != length(params$xmax)) { 366 | stop("If xmin or xmax is set, the other one also needs to be set and they need to contain the same number of values") 367 | } 368 | 369 | if (!is.null(params$xmin) && !is.null(params$comparisons)) { 370 | stop("Set either the xmin, xmax values or the comparisons") 371 | } 372 | 373 | if (!is.null(params$xmin) && is.null(params$y_position)) { 374 | stop("If xmin, xmax are defined also define y_position") 375 | } 376 | 377 | if (!is.null(params$y_position) && length(params$y_position) == 1) { 378 | params$y_position <- rep(params$y_position, max(length(params$comparisons), length(params$xmin), 1)) 379 | } 380 | 381 | if (length(params$margin_top) == 1) params$margin_top <- rep(params$margin_top, max(length(params$comparisons), length(params$xmin), 1)) 382 | 383 | if (length(params$step_increase) == 1) params$step_increase <- rep(params$step_increase, max(length(params$comparisons), length(params$xmin), 1)) 384 | 385 | if (length(params$tip_length) == 1) params$tip_length <- rep(params$tip_length, max(length(params$comparisons), length(params$xmin), 1) * 2) 386 | 387 | if (length(params$tip_length) == length(params$comparisons)) params$tip_length <- rep(params$tip_length, each = 2) 388 | 389 | if (length(params$tip_length) == length(params$xmin)) params$tip_length <- rep(params$tip_length, each = 2) 390 | 391 | if (!is.null(params$annotations) && length(params$annotations) == 1) { 392 | params$annotations <- rep(params$annotations, max(length(params$comparisons), length(params$xmin), 1)) 393 | } 394 | 395 | if (!is.null(params$annotations) && length(params$annotations) != max(length(params$comparisons), length(params$xmin), 1)) { 396 | stop(paste0( 397 | "annotations contains a different number of elements (", length(params$annotations), 398 | ") than comparisons or xmin (", max(length(params$comparisons), length(params$xmin), 1), ")." 399 | )) 400 | } 401 | 402 | if (all(is.logical(params$map_signif_level)) && all(params$map_signif_level == TRUE)) { 403 | params$map_signif_level <- c("***" = 0.001, "**" = 0.01, "*" = 0.05) 404 | } else if (is.numeric(params$map_signif_level)) { 405 | if (is.null(names(params$map_signif_level))) { 406 | if (length(params$map_signif_level) <= 3) { 407 | names(params$map_signif_level) <- tail(c("***", "**", "*"), n = length(params$map_signif_level)) 408 | } else { 409 | stop('Cannot handle un-named map for significance values, please provide in the following format: c("***"=0.001, "**"=0.01, "*"=0.05)') 410 | } 411 | } 412 | } 413 | return(params) 414 | }, 415 | compute_group = function(data, 416 | scales, 417 | comparisons, 418 | test, 419 | test.args, 420 | complete_data, 421 | annotations, 422 | map_signif_level, 423 | y_position, 424 | xmax, 425 | xmin, 426 | margin_top, 427 | step_increase, 428 | tip_length, 429 | manual, 430 | flipped_aes = FALSE) { 431 | data <- ggplot2::flip_data(data, flipped_aes) 432 | scales <- ggplot2::flip_data(scales, flipped_aes) 433 | 434 | if ("annotations" %in% colnames(data)) { 435 | annotations <- data[["annotations"]] 436 | } 437 | 438 | if ("y_position" %in% colnames(data)) { 439 | y_position <- data[["y_position"]] 440 | } 441 | 442 | if ("xmax" %in% colnames(data)) { 443 | xmax <- data[["xmax"]] 444 | } 445 | 446 | if ("xmin" %in% colnames(data)) { 447 | xmin <- data[["xmin"]] 448 | } 449 | 450 | if ("map_signif_level" %in% colnames(data)) { 451 | map_signif_level <- data[["map_signif_level"]] 452 | } 453 | 454 | if ("tip_length" %in% colnames(data)) { 455 | tip_length <- rep(data[["tip_length"]], each = 2) 456 | } 457 | 458 | if (!is.null(comparisons)) { 459 | i <- 0 460 | result <- lapply(comparisons, function(comp) { 461 | i <<- i + 1 462 | # All entries in group should be the same 463 | if (scales$x$map(comp[1]) == data$group[1] || manual) { 464 | test_result <- if (is.null(annotations)) { 465 | group_1 <- complete_data$y[complete_data$x == scales$x$map(comp[1]) & 466 | complete_data$PANEL == data$PANEL[1]] 467 | group_2 <- complete_data$y[complete_data$x == scales$x$map(comp[2]) & 468 | complete_data$PANEL == data$PANEL[1]] 469 | p_value <- do.call(test, c(list(group_1, group_2), test.args))$p.value 470 | if (is.numeric(map_signif_level)) { 471 | temp_value <- names(which.min(map_signif_level[which(map_signif_level > p_value)])) 472 | if (is.null(temp_value)) { 473 | "NS." 474 | } else { 475 | temp_value 476 | } 477 | } else if (is.function(map_signif_level)) { 478 | map_signif_level(p_value) 479 | } else { 480 | if (is.numeric(p_value)) { 481 | if (p_value < .Machine$double.eps) { 482 | sprintf("p < %.2e", .Machine$double.eps) 483 | } else { 484 | as.character(sprintf("%.2g", p_value)) 485 | } 486 | } else { 487 | as.character(p_value) 488 | } 489 | } 490 | } else { 491 | annotations[i] 492 | } 493 | y_scale_range <- (scales$y$range$range[2] - scales$y$range$range[1]) 494 | if (is.null(y_position)) { 495 | y_pos <- scales$y$range$range[2] + y_scale_range * margin_top[i] + y_scale_range * step_increase[i] * (i - 1) 496 | } else { 497 | y_pos <- y_position[i] + y_scale_range * margin_top[i] + y_scale_range * step_increase[i] * (i - 1) 498 | } 499 | 500 | data.frame( 501 | x = c(min(comp[1], comp[2]), min(comp[1], comp[2]), max(comp[1], comp[2])), 502 | xend = c(min(comp[1], comp[2]), max(comp[1], comp[2]), max(comp[1], comp[2])), 503 | y = c(y_pos - y_scale_range * tip_length[(i - 1) * 2 + 1], y_pos, y_pos), 504 | yend = c(y_pos, y_pos, y_pos - y_scale_range * tip_length[(i - 1) * 2 + 2]), 505 | annotation = test_result, 506 | group = paste(c(comp, i), collapse = "-") 507 | ) 508 | } 509 | }) 510 | 511 | df <- do.call(rbind, result) 512 | } else { 513 | if ((data$x[1] == min(complete_data$x) & data$group[1] == min(complete_data$group)) | manual) { 514 | y_scale_range <- (scales$y$range$range[2] - scales$y$range$range[1]) 515 | 516 | if (is.character(xmin)) { 517 | xmin <- scales$x$map(xmin) 518 | } 519 | 520 | if (is.character(xmax)) { 521 | xmax <- scales$x$map(xmax) 522 | } 523 | 524 | if ("expression" %in% class(annotations)) { 525 | stop("annotations must be a character vector. To use plotmath set parse=TRUE.") 526 | } 527 | 528 | df <- data.frame( 529 | x = c(xmin, xmin, xmax), 530 | xend = c(xmin, xmax, xmax), 531 | y = c( 532 | y_position - y_scale_range * tip_length[seq_len(length(tip_length)) %% 2 == 1], 533 | y_position, 534 | y_position 535 | ), 536 | yend = c( 537 | y_position, 538 | y_position, 539 | y_position - y_scale_range * tip_length[seq_len(length(tip_length)) %% 2 == 0] 540 | ), 541 | annotation = rep(annotations, times = 3), group = if (manual) { 542 | rep(data$group, times = 3) 543 | } else { 544 | rep(seq_along(xmin), times = 3) 545 | } 546 | ) 547 | } else { 548 | df <- NULL 549 | } 550 | } 551 | 552 | if (!is.null(df)) { 553 | df$flipped_aes <- flipped_aes 554 | df <- ggplot2::flip_data(df, flipped_aes) 555 | } 556 | 557 | return(df) 558 | } 559 | ) 560 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU General Public License 2 | ========================== 3 | 4 | _Version 3, 29 June 2007_ 5 | _Copyright © 2007 Free Software Foundation, Inc. <>_ 6 | 7 | Everyone is permitted to copy and distribute verbatim copies of this license 8 | document, but changing it is not allowed. 9 | 10 | ## Preamble 11 | 12 | The GNU General Public License is a free, copyleft license for software and other 13 | kinds of works. 14 | 15 | The licenses for most software and other practical works are designed to take away 16 | your freedom to share and change the works. By contrast, the GNU General Public 17 | License is intended to guarantee your freedom to share and change all versions of a 18 | program--to make sure it remains free software for all its users. We, the Free 19 | Software Foundation, use the GNU General Public License for most of our software; it 20 | applies also to any other work released this way by its authors. You can apply it to 21 | your programs, too. 22 | 23 | When we speak of free software, we are referring to freedom, not price. Our General 24 | Public Licenses are designed to make sure that you have the freedom to distribute 25 | copies of free software (and charge for them if you wish), that you receive source 26 | code or can get it if you want it, that you can change the software or use pieces of 27 | it in new free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you these rights or 30 | asking you to surrender the rights. Therefore, you have certain responsibilities if 31 | you distribute copies of the software, or if you modify it: responsibilities to 32 | respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether gratis or for a fee, 35 | you must pass on to the recipients the same freedoms that you received. You must make 36 | sure that they, too, receive or can get the source code. And you must show them these 37 | terms so they know their rights. 38 | 39 | Developers that use the GNU GPL protect your rights with two steps: **(1)** assert 40 | copyright on the software, and **(2)** offer you this License giving you legal permission 41 | to copy, distribute and/or modify it. 42 | 43 | For the developers' and authors' protection, the GPL clearly explains that there is 44 | no warranty for this free software. For both users' and authors' sake, the GPL 45 | requires that modified versions be marked as changed, so that their problems will not 46 | be attributed erroneously to authors of previous versions. 47 | 48 | Some devices are designed to deny users access to install or run modified versions of 49 | the software inside them, although the manufacturer can do so. This is fundamentally 50 | incompatible with the aim of protecting users' freedom to change the software. The 51 | systematic pattern of such abuse occurs in the area of products for individuals to 52 | use, which is precisely where it is most unacceptable. Therefore, we have designed 53 | this version of the GPL to prohibit the practice for those products. If such problems 54 | arise substantially in other domains, we stand ready to extend this provision to 55 | those domains in future versions of the GPL, as needed to protect the freedom of 56 | users. 57 | 58 | Finally, every program is threatened constantly by software patents. States should 59 | not allow patents to restrict development and use of software on general-purpose 60 | computers, but in those that do, we wish to avoid the special danger that patents 61 | applied to a free program could make it effectively proprietary. To prevent this, the 62 | GPL assures that patents cannot be used to render the program non-free. 63 | 64 | The precise terms and conditions for copying, distribution and modification follow. 65 | 66 | ## TERMS AND CONDITIONS 67 | 68 | ### 0. Definitions 69 | 70 | This License refers to version 3 of the GNU General Public License. 71 | 72 | Copyright also means copyright-like laws that apply to other kinds of 73 | works, such as semiconductor masks. 74 | 75 | The Program refers to any copyrightable work licensed under this 76 | License. Each licensee is addressed as you. Licensees and 77 | recipients may be individuals or organizations. 78 | 79 | To modify a work means to copy from or adapt all or part of the work in 80 | a fashion requiring copyright permission, other than the making of an exact copy. The 81 | resulting work is called a modified version of the earlier work or a 82 | work based on the earlier work. 83 | 84 | A covered work means either the unmodified Program or a work based on 85 | the Program. 86 | 87 | To propagate a work means to do anything with it that, without 88 | permission, would make you directly or secondarily liable for infringement under 89 | applicable copyright law, except executing it on a computer or modifying a private 90 | copy. Propagation includes copying, distribution (with or without modification), 91 | making available to the public, and in some countries other activities as well. 92 | 93 | To convey a work means any kind of propagation that enables other 94 | parties to make or receive copies. Mere interaction with a user through a computer 95 | network, with no transfer of a copy, is not conveying. 96 | 97 | An interactive user interface displays Appropriate Legal Notices to the 98 | extent that it includes a convenient and prominently visible feature that **(1)** 99 | displays an appropriate copyright notice, and **(2)** tells the user that there is no 100 | warranty for the work (except to the extent that warranties are provided), that 101 | licensees may convey the work under this License, and how to view a copy of this 102 | License. If the interface presents a list of user commands or options, such as a 103 | menu, a prominent item in the list meets this criterion. 104 | 105 | ### 1. Source Code 106 | 107 | The source code for a work means the preferred form of the work for 108 | making modifications to it. Object code means any non-source form of a 109 | work. 110 | 111 | A Standard Interface means an interface that either is an official 112 | standard defined by a recognized standards body, or, in the case of interfaces 113 | specified for a particular programming language, one that is widely used among 114 | developers working in that language. 115 | 116 | The System Libraries of an executable work include anything, other than 117 | the work as a whole, that **(a)** is included in the normal form of packaging a Major 118 | Component, but which is not part of that Major Component, and **(b)** serves only to 119 | enable use of the work with that Major Component, or to implement a Standard 120 | Interface for which an implementation is available to the public in source code form. 121 | A Major Component, in this context, means a major essential component 122 | (kernel, window system, and so on) of the specific operating system (if any) on which 123 | the executable work runs, or a compiler used to produce the work, or an object code 124 | interpreter used to run it. 125 | 126 | The Corresponding Source for a work in object code form means all the 127 | source code needed to generate, install, and (for an executable work) run the object 128 | code and to modify the work, including scripts to control those activities. However, 129 | it does not include the work's System Libraries, or general-purpose tools or 130 | generally available free programs which are used unmodified in performing those 131 | activities but which are not part of the work. For example, Corresponding Source 132 | includes interface definition files associated with source files for the work, and 133 | the source code for shared libraries and dynamically linked subprograms that the work 134 | is specifically designed to require, such as by intimate data communication or 135 | control flow between those subprograms and other parts of the work. 136 | 137 | The Corresponding Source need not include anything that users can regenerate 138 | automatically from other parts of the Corresponding Source. 139 | 140 | The Corresponding Source for a work in source code form is that same work. 141 | 142 | ### 2. Basic Permissions 143 | 144 | All rights granted under this License are granted for the term of copyright on the 145 | Program, and are irrevocable provided the stated conditions are met. This License 146 | explicitly affirms your unlimited permission to run the unmodified Program. The 147 | output from running a covered work is covered by this License only if the output, 148 | given its content, constitutes a covered work. This License acknowledges your rights 149 | of fair use or other equivalent, as provided by copyright law. 150 | 151 | You may make, run and propagate covered works that you do not convey, without 152 | conditions so long as your license otherwise remains in force. You may convey covered 153 | works to others for the sole purpose of having them make modifications exclusively 154 | for you, or provide you with facilities for running those works, provided that you 155 | comply with the terms of this License in conveying all material for which you do not 156 | control copyright. Those thus making or running the covered works for you must do so 157 | exclusively on your behalf, under your direction and control, on terms that prohibit 158 | them from making any copies of your copyrighted material outside their relationship 159 | with you. 160 | 161 | Conveying under any other circumstances is permitted solely under the conditions 162 | stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 163 | 164 | ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law 165 | 166 | No covered work shall be deemed part of an effective technological measure under any 167 | applicable law fulfilling obligations under article 11 of the WIPO copyright treaty 168 | adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention 169 | of such measures. 170 | 171 | When you convey a covered work, you waive any legal power to forbid circumvention of 172 | technological measures to the extent such circumvention is effected by exercising 173 | rights under this License with respect to the covered work, and you disclaim any 174 | intention to limit operation or modification of the work as a means of enforcing, 175 | against the work's users, your or third parties' legal rights to forbid circumvention 176 | of technological measures. 177 | 178 | ### 4. Conveying Verbatim Copies 179 | 180 | You may convey verbatim copies of the Program's source code as you receive it, in any 181 | medium, provided that you conspicuously and appropriately publish on each copy an 182 | appropriate copyright notice; keep intact all notices stating that this License and 183 | any non-permissive terms added in accord with section 7 apply to the code; keep 184 | intact all notices of the absence of any warranty; and give all recipients a copy of 185 | this License along with the Program. 186 | 187 | You may charge any price or no price for each copy that you convey, and you may offer 188 | support or warranty protection for a fee. 189 | 190 | ### 5. Conveying Modified Source Versions 191 | 192 | You may convey a work based on the Program, or the modifications to produce it from 193 | the Program, in the form of source code under the terms of section 4, provided that 194 | you also meet all of these conditions: 195 | 196 | * **a)** The work must carry prominent notices stating that you modified it, and giving a 197 | relevant date. 198 | * **b)** The work must carry prominent notices stating that it is released under this 199 | License and any conditions added under section 7. This requirement modifies the 200 | requirement in section 4 to keep intact all notices. 201 | * **c)** You must license the entire work, as a whole, under this License to anyone who 202 | comes into possession of a copy. This License will therefore apply, along with any 203 | applicable section 7 additional terms, to the whole of the work, and all its parts, 204 | regardless of how they are packaged. This License gives no permission to license the 205 | work in any other way, but it does not invalidate such permission if you have 206 | separately received it. 207 | * **d)** If the work has interactive user interfaces, each must display Appropriate Legal 208 | Notices; however, if the Program has interactive interfaces that do not display 209 | Appropriate Legal Notices, your work need not make them do so. 210 | 211 | A compilation of a covered work with other separate and independent works, which are 212 | not by their nature extensions of the covered work, and which are not combined with 213 | it such as to form a larger program, in or on a volume of a storage or distribution 214 | medium, is called an aggregate if the compilation and its resulting 215 | copyright are not used to limit the access or legal rights of the compilation's users 216 | beyond what the individual works permit. Inclusion of a covered work in an aggregate 217 | does not cause this License to apply to the other parts of the aggregate. 218 | 219 | ### 6. Conveying Non-Source Forms 220 | 221 | You may convey a covered work in object code form under the terms of sections 4 and 222 | 5, provided that you also convey the machine-readable Corresponding Source under the 223 | terms of this License, in one of these ways: 224 | 225 | * **a)** Convey the object code in, or embodied in, a physical product (including a 226 | physical distribution medium), accompanied by the Corresponding Source fixed on a 227 | durable physical medium customarily used for software interchange. 228 | * **b)** Convey the object code in, or embodied in, a physical product (including a 229 | physical distribution medium), accompanied by a written offer, valid for at least 230 | three years and valid for as long as you offer spare parts or customer support for 231 | that product model, to give anyone who possesses the object code either **(1)** a copy of 232 | the Corresponding Source for all the software in the product that is covered by this 233 | License, on a durable physical medium customarily used for software interchange, for 234 | a price no more than your reasonable cost of physically performing this conveying of 235 | source, or **(2)** access to copy the Corresponding Source from a network server at no 236 | charge. 237 | * **c)** Convey individual copies of the object code with a copy of the written offer to 238 | provide the Corresponding Source. This alternative is allowed only occasionally and 239 | noncommercially, and only if you received the object code with such an offer, in 240 | accord with subsection 6b. 241 | * **d)** Convey the object code by offering access from a designated place (gratis or for 242 | a charge), and offer equivalent access to the Corresponding Source in the same way 243 | through the same place at no further charge. You need not require recipients to copy 244 | the Corresponding Source along with the object code. If the place to copy the object 245 | code is a network server, the Corresponding Source may be on a different server 246 | (operated by you or a third party) that supports equivalent copying facilities, 247 | provided you maintain clear directions next to the object code saying where to find 248 | the Corresponding Source. Regardless of what server hosts the Corresponding Source, 249 | you remain obligated to ensure that it is available for as long as needed to satisfy 250 | these requirements. 251 | * **e)** Convey the object code using peer-to-peer transmission, provided you inform 252 | other peers where the object code and Corresponding Source of the work are being 253 | offered to the general public at no charge under subsection 6d. 254 | 255 | A separable portion of the object code, whose source code is excluded from the 256 | Corresponding Source as a System Library, need not be included in conveying the 257 | object code work. 258 | 259 | A User Product is either **(1)** a consumer product, which 260 | means any tangible personal property which is normally used for personal, family, or 261 | household purposes, or **(2)** anything designed or sold for incorporation into a 262 | dwelling. In determining whether a product is a consumer product, doubtful cases 263 | shall be resolved in favor of coverage. For a particular product received by a 264 | particular user, normally used refers to a typical or common use of 265 | that class of product, regardless of the status of the particular user or of the way 266 | in which the particular user actually uses, or expects or is expected to use, the 267 | product. A product is a consumer product regardless of whether the product has 268 | substantial commercial, industrial or non-consumer uses, unless such uses represent 269 | the only significant mode of use of the product. 270 | 271 | Installation Information for a User Product means any methods, 272 | procedures, authorization keys, or other information required to install and execute 273 | modified versions of a covered work in that User Product from a modified version of 274 | its Corresponding Source. The information must suffice to ensure that the continued 275 | functioning of the modified object code is in no case prevented or interfered with 276 | solely because modification has been made. 277 | 278 | If you convey an object code work under this section in, or with, or specifically for 279 | use in, a User Product, and the conveying occurs as part of a transaction in which 280 | the right of possession and use of the User Product is transferred to the recipient 281 | in perpetuity or for a fixed term (regardless of how the transaction is 282 | characterized), the Corresponding Source conveyed under this section must be 283 | accompanied by the Installation Information. But this requirement does not apply if 284 | neither you nor any third party retains the ability to install modified object code 285 | on the User Product (for example, the work has been installed in ROM). 286 | 287 | The requirement to provide Installation Information does not include a requirement to 288 | continue to provide support service, warranty, or updates for a work that has been 289 | modified or installed by the recipient, or for the User Product in which it has been 290 | modified or installed. Access to a network may be denied when the modification itself 291 | materially and adversely affects the operation of the network or violates the rules 292 | and protocols for communication across the network. 293 | 294 | Corresponding Source conveyed, and Installation Information provided, in accord with 295 | this section must be in a format that is publicly documented (and with an 296 | implementation available to the public in source code form), and must require no 297 | special password or key for unpacking, reading or copying. 298 | 299 | ### 7. Additional Terms 300 | 301 | Additional permissions are terms that supplement the terms of this 302 | License by making exceptions from one or more of its conditions. Additional 303 | permissions that are applicable to the entire Program shall be treated as though they 304 | were included in this License, to the extent that they are valid under applicable 305 | law. If additional permissions apply only to part of the Program, that part may be 306 | used separately under those permissions, but the entire Program remains governed by 307 | this License without regard to the additional permissions. 308 | 309 | When you convey a copy of a covered work, you may at your option remove any 310 | additional permissions from that copy, or from any part of it. (Additional 311 | permissions may be written to require their own removal in certain cases when you 312 | modify the work.) You may place additional permissions on material, added by you to a 313 | covered work, for which you have or can give appropriate copyright permission. 314 | 315 | Notwithstanding any other provision of this License, for material you add to a 316 | covered work, you may (if authorized by the copyright holders of that material) 317 | supplement the terms of this License with terms: 318 | 319 | * **a)** Disclaiming warranty or limiting liability differently from the terms of 320 | sections 15 and 16 of this License; or 321 | * **b)** Requiring preservation of specified reasonable legal notices or author 322 | attributions in that material or in the Appropriate Legal Notices displayed by works 323 | containing it; or 324 | * **c)** Prohibiting misrepresentation of the origin of that material, or requiring that 325 | modified versions of such material be marked in reasonable ways as different from the 326 | original version; or 327 | * **d)** Limiting the use for publicity purposes of names of licensors or authors of the 328 | material; or 329 | * **e)** Declining to grant rights under trademark law for use of some trade names, 330 | trademarks, or service marks; or 331 | * **f)** Requiring indemnification of licensors and authors of that material by anyone 332 | who conveys the material (or modified versions of it) with contractual assumptions of 333 | liability to the recipient, for any liability that these contractual assumptions 334 | directly impose on those licensors and authors. 335 | 336 | All other non-permissive additional terms are considered further 337 | restrictions within the meaning of section 10. If the Program as you received 338 | it, or any part of it, contains a notice stating that it is governed by this License 339 | along with a term that is a further restriction, you may remove that term. If a 340 | license document contains a further restriction but permits relicensing or conveying 341 | under this License, you may add to a covered work material governed by the terms of 342 | that license document, provided that the further restriction does not survive such 343 | relicensing or conveying. 344 | 345 | If you add terms to a covered work in accord with this section, you must place, in 346 | the relevant source files, a statement of the additional terms that apply to those 347 | files, or a notice indicating where to find the applicable terms. 348 | 349 | Additional terms, permissive or non-permissive, may be stated in the form of a 350 | separately written license, or stated as exceptions; the above requirements apply 351 | either way. 352 | 353 | ### 8. Termination 354 | 355 | You may not propagate or modify a covered work except as expressly provided under 356 | this License. Any attempt otherwise to propagate or modify it is void, and will 357 | automatically terminate your rights under this License (including any patent licenses 358 | granted under the third paragraph of section 11). 359 | 360 | However, if you cease all violation of this License, then your license from a 361 | particular copyright holder is reinstated **(a)** provisionally, unless and until the 362 | copyright holder explicitly and finally terminates your license, and **(b)** permanently, 363 | if the copyright holder fails to notify you of the violation by some reasonable means 364 | prior to 60 days after the cessation. 365 | 366 | Moreover, your license from a particular copyright holder is reinstated permanently 367 | if the copyright holder notifies you of the violation by some reasonable means, this 368 | is the first time you have received notice of violation of this License (for any 369 | work) from that copyright holder, and you cure the violation prior to 30 days after 370 | your receipt of the notice. 371 | 372 | Termination of your rights under this section does not terminate the licenses of 373 | parties who have received copies or rights from you under this License. If your 374 | rights have been terminated and not permanently reinstated, you do not qualify to 375 | receive new licenses for the same material under section 10. 376 | 377 | ### 9. Acceptance Not Required for Having Copies 378 | 379 | You are not required to accept this License in order to receive or run a copy of the 380 | Program. Ancillary propagation of a covered work occurring solely as a consequence of 381 | using peer-to-peer transmission to receive a copy likewise does not require 382 | acceptance. However, nothing other than this License grants you permission to 383 | propagate or modify any covered work. These actions infringe copyright if you do not 384 | accept this License. Therefore, by modifying or propagating a covered work, you 385 | indicate your acceptance of this License to do so. 386 | 387 | ### 10. Automatic Licensing of Downstream Recipients 388 | 389 | Each time you convey a covered work, the recipient automatically receives a license 390 | from the original licensors, to run, modify and propagate that work, subject to this 391 | License. You are not responsible for enforcing compliance by third parties with this 392 | License. 393 | 394 | An entity transaction is a transaction transferring control of an 395 | organization, or substantially all assets of one, or subdividing an organization, or 396 | merging organizations. If propagation of a covered work results from an entity 397 | transaction, each party to that transaction who receives a copy of the work also 398 | receives whatever licenses to the work the party's predecessor in interest had or 399 | could give under the previous paragraph, plus a right to possession of the 400 | Corresponding Source of the work from the predecessor in interest, if the predecessor 401 | has it or can get it with reasonable efforts. 402 | 403 | You may not impose any further restrictions on the exercise of the rights granted or 404 | affirmed under this License. For example, you may not impose a license fee, royalty, 405 | or other charge for exercise of rights granted under this License, and you may not 406 | initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging 407 | that any patent claim is infringed by making, using, selling, offering for sale, or 408 | importing the Program or any portion of it. 409 | 410 | ### 11. Patents 411 | 412 | A contributor is a copyright holder who authorizes use under this 413 | License of the Program or a work on which the Program is based. The work thus 414 | licensed is called the contributor's contributor version. 415 | 416 | A contributor's essential patent claims are all patent claims owned or 417 | controlled by the contributor, whether already acquired or hereafter acquired, that 418 | would be infringed by some manner, permitted by this License, of making, using, or 419 | selling its contributor version, but do not include claims that would be infringed 420 | only as a consequence of further modification of the contributor version. For 421 | purposes of this definition, control includes the right to grant patent 422 | sublicenses in a manner consistent with the requirements of this License. 423 | 424 | Each contributor grants you a non-exclusive, worldwide, royalty-free patent license 425 | under the contributor's essential patent claims, to make, use, sell, offer for sale, 426 | import and otherwise run, modify and propagate the contents of its contributor 427 | version. 428 | 429 | In the following three paragraphs, a patent license is any express 430 | agreement or commitment, however denominated, not to enforce a patent (such as an 431 | express permission to practice a patent or covenant not to sue for patent 432 | infringement). To grant such a patent license to a party means to make 433 | such an agreement or commitment not to enforce a patent against the party. 434 | 435 | If you convey a covered work, knowingly relying on a patent license, and the 436 | Corresponding Source of the work is not available for anyone to copy, free of charge 437 | and under the terms of this License, through a publicly available network server or 438 | other readily accessible means, then you must either **(1)** cause the Corresponding 439 | Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the 440 | patent license for this particular work, or **(3)** arrange, in a manner consistent with 441 | the requirements of this License, to extend the patent license to downstream 442 | recipients. Knowingly relying means you have actual knowledge that, but 443 | for the patent license, your conveying the covered work in a country, or your 444 | recipient's use of the covered work in a country, would infringe one or more 445 | identifiable patents in that country that you have reason to believe are valid. 446 | 447 | If, pursuant to or in connection with a single transaction or arrangement, you 448 | convey, or propagate by procuring conveyance of, a covered work, and grant a patent 449 | license to some of the parties receiving the covered work authorizing them to use, 450 | propagate, modify or convey a specific copy of the covered work, then the patent 451 | license you grant is automatically extended to all recipients of the covered work and 452 | works based on it. 453 | 454 | A patent license is discriminatory if it does not include within the 455 | scope of its coverage, prohibits the exercise of, or is conditioned on the 456 | non-exercise of one or more of the rights that are specifically granted under this 457 | License. You may not convey a covered work if you are a party to an arrangement with 458 | a third party that is in the business of distributing software, under which you make 459 | payment to the third party based on the extent of your activity of conveying the 460 | work, and under which the third party grants, to any of the parties who would receive 461 | the covered work from you, a discriminatory patent license **(a)** in connection with 462 | copies of the covered work conveyed by you (or copies made from those copies), or **(b)** 463 | primarily for and in connection with specific products or compilations that contain 464 | the covered work, unless you entered into that arrangement, or that patent license 465 | was granted, prior to 28 March 2007. 466 | 467 | Nothing in this License shall be construed as excluding or limiting any implied 468 | license or other defenses to infringement that may otherwise be available to you 469 | under applicable patent law. 470 | 471 | ### 12. No Surrender of Others' Freedom 472 | 473 | If conditions are imposed on you (whether by court order, agreement or otherwise) 474 | that contradict the conditions of this License, they do not excuse you from the 475 | conditions of this License. If you cannot convey a covered work so as to satisfy 476 | simultaneously your obligations under this License and any other pertinent 477 | obligations, then as a consequence you may not convey it at all. For example, if you 478 | agree to terms that obligate you to collect a royalty for further conveying from 479 | those to whom you convey the Program, the only way you could satisfy both those terms 480 | and this License would be to refrain entirely from conveying the Program. 481 | 482 | ### 13. Use with the GNU Affero General Public License 483 | 484 | Notwithstanding any other provision of this License, you have permission to link or 485 | combine any covered work with a work licensed under version 3 of the GNU Affero 486 | General Public License into a single combined work, and to convey the resulting work. 487 | The terms of this License will continue to apply to the part which is the covered 488 | work, but the special requirements of the GNU Affero General Public License, section 489 | 13, concerning interaction through a network will apply to the combination as such. 490 | 491 | ### 14. Revised Versions of this License 492 | 493 | The Free Software Foundation may publish revised and/or new versions of the GNU 494 | General Public License from time to time. Such new versions will be similar in spirit 495 | to the present version, but may differ in detail to address new problems or concerns. 496 | 497 | Each version is given a distinguishing version number. If the Program specifies that 498 | a certain numbered version of the GNU General Public License or any later 499 | version applies to it, you have the option of following the terms and 500 | conditions either of that numbered version or of any later version published by the 501 | Free Software Foundation. If the Program does not specify a version number of the GNU 502 | General Public License, you may choose any version ever published by the Free 503 | Software Foundation. 504 | 505 | If the Program specifies that a proxy can decide which future versions of the GNU 506 | General Public License can be used, that proxy's public statement of acceptance of a 507 | version permanently authorizes you to choose that version for the Program. 508 | 509 | Later license versions may give you additional or different permissions. However, no 510 | additional obligations are imposed on any author or copyright holder as a result of 511 | your choosing to follow a later version. 512 | 513 | ### 15. Disclaimer of Warranty 514 | 515 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 516 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 517 | PROVIDE THE PROGRAM AS IS WITHOUT WARRANTY OF ANY KIND, EITHER 518 | EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 519 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE 520 | QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE 521 | DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 522 | 523 | ### 16. Limitation of Liability 524 | 525 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY 526 | COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS 527 | PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, 528 | INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 529 | PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE 530 | OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE 531 | WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 532 | POSSIBILITY OF SUCH DAMAGES. 533 | 534 | ### 17. Interpretation of Sections 15 and 16 535 | 536 | If the disclaimer of warranty and limitation of liability provided above cannot be 537 | given local legal effect according to their terms, reviewing courts shall apply local 538 | law that most closely approximates an absolute waiver of all civil liability in 539 | connection with the Program, unless a warranty or assumption of liability accompanies 540 | a copy of the Program in return for a fee. 541 | 542 | _END OF TERMS AND CONDITIONS_ 543 | 544 | ## How to Apply These Terms to Your New Programs 545 | 546 | If you develop a new program, and you want it to be of the greatest possible use to 547 | the public, the best way to achieve this is to make it free software which everyone 548 | can redistribute and change under these terms. 549 | 550 | To do so, attach the following notices to the program. It is safest to attach them 551 | to the start of each source file to most effectively state the exclusion of warranty; 552 | and each file should have at least the copyright line and a pointer to 553 | where the full notice is found. 554 | 555 | 556 | Copyright (C) 2018 Indrajeet Patil 557 | 558 | This program is free software: you can redistribute it and/or modify 559 | it under the terms of the GNU General Public License as published by 560 | the Free Software Foundation, either version 3 of the License, or 561 | (at your option) any later version. 562 | 563 | This program is distributed in the hope that it will be useful, 564 | but WITHOUT ANY WARRANTY; without even the implied warranty of 565 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 566 | GNU General Public License for more details. 567 | 568 | You should have received a copy of the GNU General Public License 569 | along with this program. If not, see . 570 | 571 | Also add information on how to contact you by electronic and paper mail. 572 | 573 | If the program does terminal interaction, make it output a short notice like this 574 | when it starts in an interactive mode: 575 | 576 | ipmisc Copyright (C) 2018 Indrajeet Patil 577 | This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. 578 | This is free software, and you are welcome to redistribute it 579 | under certain conditions; type 'show c' for details. 580 | 581 | The hypothetical commands `show w` and `show c` should show the appropriate parts of 582 | the General Public License. Of course, your program's commands might be different; 583 | for a GUI interface, you would use an about box. 584 | 585 | You should also get your employer (if you work as a programmer) or school, if any, to 586 | sign a copyright disclaimer for the program, if necessary. For more 587 | information on this, and how to apply and follow the GNU GPL, see 588 | <>. 589 | 590 | The GNU General Public License does not permit incorporating your program into 591 | proprietary programs. If your program is a subroutine library, you may consider it 592 | more useful to permit linking proprietary applications with the library. If this is 593 | what you want to do, use the GNU Lesser General Public License instead of this 594 | License. But first, please read 595 | <>. 596 | --------------------------------------------------------------------------------