├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ └── pkgdown.yaml ├── LICENSE ├── .lintr ├── figures ├── ggvenn_list.png └── ggvenn_data.frame.png ├── R ├── ggvenn-package.R ├── globals.R ├── ggvenn.R ├── geom_venn.R ├── data_preparation.R └── venn_geometry.R ├── .gitignore ├── .Rbuildignore ├── tests ├── testthat │ ├── test-color_size.R │ ├── test-show_elements.R │ ├── test-percentages.R │ ├── test-data_types.R │ ├── test-stats.R │ ├── _snaps │ │ ├── color_size.md │ │ ├── show_elements.md │ │ ├── percentages.md │ │ ├── geom_venn.md │ │ ├── set-totals.md │ │ ├── ggvenn.md │ │ └── stats.md │ ├── test-set-totals.R │ ├── helper-normalize.R │ ├── test-geom_venn.R │ └── test-ggvenn.R └── testthat.R ├── ggvenn.Rproj ├── _pkgdown.yml ├── ..Rcheck └── 00check.log ├── man ├── get_venn_table.Rd ├── ggvenn-package.Rd ├── data_preparation.Rd ├── ggvenn.Rd └── geom_venn.Rd ├── examples.R ├── DESCRIPTION ├── LICENSE.md ├── NAMESPACE ├── NEWS.md └── README.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2019-2021 2 | COPYRIGHT HOLDER: Linlin Yan 3 | -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: linters_with_defaults(line_length_linter = line_length_linter(120)) 2 | -------------------------------------------------------------------------------- /figures/ggvenn_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanlinlin82/ggvenn/HEAD/figures/ggvenn_list.png -------------------------------------------------------------------------------- /figures/ggvenn_data.frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanlinlin82/ggvenn/HEAD/figures/ggvenn_data.frame.png -------------------------------------------------------------------------------- /R/ggvenn-package.R: -------------------------------------------------------------------------------- 1 | #' @keywords internal 2 | "_PACKAGE" 3 | 4 | ## usethis namespace: start 5 | ## usethis namespace: end 6 | NULL 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | ggvenn.Rcheck/ 6 | DESCRIPTION 7 | ggvenn_*.tar.gz 8 | docs 9 | ..Rcheck/ 10 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^figures/.* 2 | ^examples\.R$ 3 | ^\.github/.* 4 | ^\.Rproj\.user/.* 5 | ^\.git.* 6 | ^\.lintr$ 7 | ^LICENSE\.md$ 8 | ^_pkgdown\.yml$ 9 | ^docs$ 10 | ^ggvenn\.Rproj$ 11 | -------------------------------------------------------------------------------- /tests/testthat/test-color_size.R: -------------------------------------------------------------------------------- 1 | test_that("color and size", { 2 | a <- list(A = 1:4, B = c(1,3,5)) 3 | 4 | expect_snapshot( 5 | normalize_ggvenn_output( 6 | ggvenn(a, stroke_linetype = 2, stroke_size = 0.5, 7 | set_name_color = "red", set_name_size = 15, 8 | fill_color = c("pink", "gold")) 9 | ) 10 | ) 11 | }) 12 | -------------------------------------------------------------------------------- /ggvenn.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /tests/testthat/test-show_elements.R: -------------------------------------------------------------------------------- 1 | test_that("show_elements", { 2 | a <- list(A = c("apple", "pear", "peach"), B = c("apple", "lemon")) 3 | 4 | expect_snapshot( 5 | normalize_ggvenn_output( 6 | ggvenn(a, show_elements = TRUE) 7 | ) 8 | ) 9 | 10 | expect_snapshot( 11 | normalize_ggvenn_output( 12 | ggvenn(a, show_elements = TRUE, label_sep = "\n") # show elements in line 13 | ) 14 | ) 15 | }) 16 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | # This file is part of the standard setup for testthat. 2 | # It is recommended that you do not modify it. 3 | # 4 | # Where should you do additional test configuration? 5 | # Learn more about the roles of various files in: 6 | # * https://r-pkgs.org/testing-design.html#sec-tests-files-overview 7 | # * https://testthat.r-lib.org/articles/special-files.html 8 | 9 | library(testthat) 10 | library(ggvenn) 11 | 12 | test_check("ggvenn") 13 | -------------------------------------------------------------------------------- /tests/testthat/test-percentages.R: -------------------------------------------------------------------------------- 1 | test_that("stats", { 2 | 3 | a <- list(A = 1:5, B = 1:2) 4 | 5 | expect_snapshot( 6 | normalize_ggvenn_output( 7 | ggvenn(a, show_percentage = FALSE) 8 | ) 9 | ) 10 | 11 | expect_snapshot( 12 | normalize_ggvenn_output( 13 | ggvenn(a, show_percentage = TRUE) 14 | ) 15 | ) 16 | 17 | expect_snapshot( 18 | normalize_ggvenn_output( 19 | ggvenn(a, show_percentage = TRUE, digits = 2) 20 | ) 21 | ) 22 | }) 23 | -------------------------------------------------------------------------------- /tests/testthat/test-data_types.R: -------------------------------------------------------------------------------- 1 | test_that("multiplication works", { 2 | a <- list(A = 1:5, B = 4:6) 3 | d <- dplyr::tibble(key = 1:6, 4 | A = c(rep(TRUE, 5), FALSE), 5 | B = rep(c(FALSE, TRUE), each = 3)) 6 | 7 | expect_identical(a, data_frame_to_list(d)) # TRUE 8 | 9 | # list_to_data_frame creates _key column, not key column 10 | d_expected <- d 11 | names(d_expected)[names(d_expected) == 'key'] <- '_key' 12 | expect_identical(d_expected, list_to_data_frame(a)) # TRUE 13 | }) 14 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://yanlinlin82.github.io/ggvenn/ 2 | 3 | template: 4 | bootstrap: 5 5 | 6 | navbar: 7 | structure: 8 | left: [intro, reference, news] 9 | right: [github] 10 | 11 | reference: 12 | - title: "Main functions" 13 | contents: 14 | - ggvenn 15 | - geom_venn 16 | - title: "Utility functions" 17 | contents: 18 | - data_frame_to_list 19 | - list_to_data_frame 20 | - get_venn_table 21 | 22 | news: 23 | releases: 24 | - text: "ggvenn 0.1.17" 25 | href: https://github.com/yanlinlin82/ggvenn/tree/master 26 | -------------------------------------------------------------------------------- /..Rcheck/00check.log: -------------------------------------------------------------------------------- 1 | * using log directory ‘/work/Projects/Others/ggvenn/..Rcheck’ 2 | * using R version 4.5.1 (2025-06-13) 3 | * using platform: x86_64-pc-linux-gnu 4 | * R was compiled by 5 | gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 6 | GNU Fortran (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 7 | * running under: Ubuntu 24.04.3 LTS 8 | * using session charset: UTF-8 9 | * using options ‘--no-manual --no-vignettes’ 10 | * checking for file ‘./DESCRIPTION’ ... ERROR 11 | Required fields missing or empty: 12 | ‘Author’ ‘Maintainer’ 13 | * DONE 14 | Status: 1 ERROR 15 | -------------------------------------------------------------------------------- /man/get_venn_table.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ggvenn.R 3 | \name{get_venn_table} 4 | \alias{get_venn_table} 5 | \title{Extract Venn diagram data table from ggvenn plot} 6 | \usage{ 7 | get_venn_table(g) 8 | } 9 | \arguments{ 10 | \item{g}{A ggplot object created by ggvenn()} 11 | } 12 | \value{ 13 | A data frame containing the Venn diagram intersection data 14 | } 15 | \description{ 16 | Extract Venn diagram data table from ggvenn plot 17 | } 18 | \examples{ 19 | library(ggvenn) 20 | g <- ggvenn(list(A = 1:5, B = 4:9, C = c(2:3, 8:12), D = c(1, 5, 9))) 21 | get_venn_table(g) 22 | } 23 | -------------------------------------------------------------------------------- /R/globals.R: -------------------------------------------------------------------------------- 1 | utils::globalVariables(c( 2 | "theta", "x_raw", "y_raw", 3 | "A", "B", "C", "D", "group", 4 | "head", "hjust", "n", "text", 5 | "vjust", "x", "y", 6 | "name", "xend", "yend", 7 | "gen_circle", 8 | "min_overlap_for_text", 9 | "gen_circle_2", "gen_label_pos_2", "gen_text_pos_2", "gen_seg_pos_2", "calc_scale_info_2", 10 | "gen_circle_3", "gen_label_pos_3", "gen_text_pos_3", "gen_seg_pos_3", 11 | "gen_circle_4", "gen_label_pos_4", "gen_text_pos_4", "gen_seg_pos_4", 12 | "gen_circle_5", "gen_label_pos_5", "gen_text_pos_5", "gen_seg_pos_5", 13 | "gen_circle_6", "gen_label_pos_6", "gen_text_pos_6", "gen_seg_pos_6", 14 | "gen_circle_7", "gen_label_pos_7", "gen_text_pos_7", "gen_seg_pos_7", 15 | "gen_circle_8", "gen_label_pos_8", "gen_text_pos_8", "gen_seg_pos_8", 16 | "prepare_venn_data" 17 | )) 18 | -------------------------------------------------------------------------------- /man/ggvenn-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ggvenn-package.R 3 | \docType{package} 4 | \name{ggvenn-package} 5 | \alias{ggvenn-package} 6 | \title{ggvenn: Draw Venn Diagram by 'ggplot2'} 7 | \description{ 8 | An easy-to-use way to draw pretty Venn diagrams using 'ggplot2'. This package provides functions to create Venn diagrams with customizable colors, labels, and styling options. 9 | } 10 | \seealso{ 11 | Useful links: 12 | \itemize{ 13 | \item \url{https://yanlinlin82.github.io/ggvenn/} 14 | \item \url{https://github.com/yanlinlin82/ggvenn} 15 | \item Report bugs at \url{https://github.com/yanlinlin82/ggvenn/issues} 16 | } 17 | 18 | } 19 | \author{ 20 | \strong{Maintainer}: Linlin Yan \email{yanlinlin82@gmail.com} (\href{https://orcid.org/0000-0002-4990-6239}{ORCID}) 21 | 22 | } 23 | \keyword{internal} 24 | -------------------------------------------------------------------------------- /tests/testthat/test-stats.R: -------------------------------------------------------------------------------- 1 | test_that("stats", { 2 | a <- list( 3 | "Set 1" = c(1, 3, 5, 7, 9), 4 | "Set 2" = c(1, 5, 9, 13), 5 | "Set 3" = c(1, 2, 8, 9), 6 | "Set 4" = c(6, 7, 10, 12) 7 | ) 8 | 9 | expect_snapshot(normalize_ggvenn_output(ggvenn(a))) 10 | expect_snapshot(normalize_ggvenn_output(ggvenn(a, show_percentage = FALSE))) 11 | expect_snapshot(normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE))) 12 | expect_snapshot(normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE, show_stats = "c"))) 13 | expect_snapshot(normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE, show_stats = "p"))) 14 | expect_snapshot(normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE, show_stats = "cp"))) 15 | expect_snapshot(normalize_ggvenn_output(ggvenn(a, show_stats = "c"))) 16 | expect_snapshot(normalize_ggvenn_output(ggvenn(a, show_stats = "p"))) 17 | expect_snapshot(normalize_ggvenn_output(ggvenn(a, show_stats = "cp"))) 18 | }) 19 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/color_size.md: -------------------------------------------------------------------------------- 1 | # color and size 2 | 3 | Code 4 | normalize_ggvenn_output(ggvenn(a, stroke_linetype = 2, stroke_size = 0.5, 5 | set_name_color = "red", set_name_size = 15, fill_color = c("pink", "gold"))) 6 | Output 7 | [[1]] 8 | [[1]]$A 9 | [1] TRUE TRUE TRUE TRUE FALSE 10 | 11 | [[1]]$B 12 | [1] TRUE FALSE TRUE FALSE TRUE 13 | 14 | [[1]]$label 15 | [1] 1 2 3 4 5 16 | 17 | [[1]]$PANEL 18 | [1] 1 1 1 1 1 19 | Levels: 1 20 | 21 | [[1]]$group 22 | [1] 3 2 3 2 1 23 | attr(,"n") 24 | [1] 3 25 | 26 | [[1]]$xmin 27 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 28 | 29 | [[1]]$xmax 30 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 31 | 32 | [[1]]$ymin 33 | [1] -1.199874 -1.199874 -1.199874 -1.199874 -1.199874 34 | 35 | [[1]]$ymax 36 | [1] 1.4 1.4 1.4 1.4 1.4 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples.R: -------------------------------------------------------------------------------- 1 | library(ggvenn) 2 | library(patchwork) 3 | 4 | a <- list(A = 1:5, B = 4:9, C = 3:7, D = 1:20, E = 15:19) 5 | g1 <- ggvenn(a, c("A", "B")) # draw two-set venn 6 | g2 <- ggvenn(a, c("A", "B", "C")) # draw three-set venn 7 | g3 <- ggvenn(a, c("A", "B", "C", "D")) # draw four-set venn 8 | g4 <- ggvenn(a) # without set names, all elements in list will be chose to draw venn 9 | 10 | if (!dir.exists("figures")) { 11 | dir.create("figures", showWarnings = FALSE) 12 | } 13 | 14 | g <- (g1 | g2) / (g3 | g4) 15 | ggsave( 16 | "figures/ggvenn_list.png", 17 | g, 18 | width = 12, height = 12, units = "in", 19 | dpi = 300, bg = "white" 20 | ) 21 | 22 | d <- data.frame( 23 | id = 1:32, 24 | A = 1:32 %% 2 == 1, 25 | B = (1:32 %/% 2) %% 2 == 1, 26 | C = (1:32 %/% 4) %% 2 == 1, 27 | D = (1:32 %/% 8) %% 2 == 1, 28 | E = (1:32 %/% 16) %% 2 == 1 29 | ) 30 | 31 | g5 <- ggvenn(d, element_column = "id", show_elements = TRUE) 32 | 33 | ggsave( 34 | "figures/ggvenn_data.frame.png", 35 | g5, 36 | width = 12, height = 12, units = "in", 37 | dpi = 300, bg = "white" 38 | ) 39 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: ggvenn 2 | Title: Draw Venn Diagram by 'ggplot2' 3 | Version: 0.1.19 4 | Authors@R: person(given = "Linlin", family = "Yan", 5 | role = c("aut", "cre"), 6 | email = "yanlinlin82@gmail.com", 7 | comment = c(ORCID = "0000-0002-4990-6239")) 8 | Author: Linlin Yan [aut, cre] () 9 | Maintainer: Linlin Yan 10 | Description: An easy-to-use way to draw pretty Venn diagrams using 'ggplot2'. 11 | This package provides functions to create Venn diagrams with customizable 12 | colors, labels, and styling options. 13 | URL: https://yanlinlin82.github.io/ggvenn/, https://github.com/yanlinlin82/ggvenn 14 | BugReports: https://github.com/yanlinlin82/ggvenn/issues 15 | License: MIT + file LICENSE 16 | Encoding: UTF-8 17 | RoxygenNote: 7.3.3 18 | Depends: ggplot2 19 | Suggests: 20 | rmarkdown, 21 | testthat (>= 3.0.0) 22 | Imports: 23 | dplyr (>= 1.1.0), 24 | grid, 25 | rlang, 26 | scales (>= 1.2.0) 27 | Roxygen: list(markdown = TRUE) 28 | Config/testthat/edition: 3 29 | -------------------------------------------------------------------------------- /tests/testthat/test-set-totals.R: -------------------------------------------------------------------------------- 1 | test_that("stats", { 2 | a <- list( 3 | "Set 1" = c(1, 3, 5, 7, 9), 4 | "Set 2" = c(1, 5, 9, 13), 5 | "Set 3" = c(1, 2, 8, 9), 6 | "Set 4" = c(6, 7, 10, 12) 7 | ) 8 | 9 | expect_snapshot( 10 | normalize_ggvenn_output( 11 | ggvenn( 12 | a, 13 | show_percentage = FALSE, 14 | show_elements = FALSE, 15 | text_size = 6 16 | ) 17 | ) 18 | ) 19 | 20 | expect_snapshot( 21 | normalize_ggvenn_output( 22 | ggvenn( 23 | a, 24 | show_set_totals = "c", 25 | show_elements = FALSE, 26 | text_size = 6 27 | ) 28 | ) 29 | ) 30 | 31 | expect_snapshot( 32 | normalize_ggvenn_output( 33 | ggvenn( 34 | a, 35 | show_set_totals = "p", 36 | show_elements = FALSE, 37 | text_size = 6 38 | ) 39 | ) 40 | ) 41 | 42 | expect_snapshot( 43 | normalize_ggvenn_output( 44 | ggvenn( 45 | a, 46 | show_set_totals = "cp", 47 | show_elements = FALSE, 48 | text_size = 6 49 | ) 50 | ) 51 | ) 52 | }) 53 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 Linlin Yan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(data_frame_to_list) 4 | export(geom_venn) 5 | export(get_venn_table) 6 | export(ggvenn) 7 | export(list_to_data_frame) 8 | importFrom(dplyr,"%>%") 9 | importFrom(dplyr,as_label) 10 | importFrom(dplyr,as_tibble) 11 | importFrom(dplyr,count) 12 | importFrom(dplyr,filter) 13 | importFrom(dplyr,inner_join) 14 | importFrom(dplyr,mutate) 15 | importFrom(dplyr,select_if) 16 | importFrom(dplyr,tibble) 17 | importFrom(dplyr,tribble) 18 | importFrom(ggplot2,aes) 19 | importFrom(ggplot2,coord_fixed) 20 | importFrom(ggplot2,expansion) 21 | importFrom(ggplot2,geom_polygon) 22 | importFrom(ggplot2,geom_segment) 23 | importFrom(ggplot2,geom_text) 24 | importFrom(ggplot2,ggplot) 25 | importFrom(ggplot2,ggplot_build) 26 | importFrom(ggplot2,guides) 27 | importFrom(ggplot2,layer) 28 | importFrom(ggplot2,scale_fill_manual) 29 | importFrom(ggplot2,scale_x_continuous) 30 | importFrom(ggplot2,scale_x_discrete) 31 | importFrom(ggplot2,scale_y_continuous) 32 | importFrom(ggplot2,scale_y_discrete) 33 | importFrom(ggplot2,theme_void) 34 | importFrom(rlang,sym) 35 | importFrom(rlang,syms) 36 | importFrom(stats,na.omit) 37 | -------------------------------------------------------------------------------- /tests/testthat/helper-normalize.R: -------------------------------------------------------------------------------- 1 | # Helper function to normalize numeric data for cross-platform snapshot consistency 2 | normalize_ggplot_data <- function(data, digits = 6) { 3 | if (is.list(data)) { 4 | # Recursively normalize list elements 5 | lapply(data, normalize_ggplot_data, digits = digits) 6 | } else if (is.data.frame(data)) { 7 | # Normalize numeric columns in data frames 8 | for (col in names(data)) { 9 | if (is.numeric(data[[col]])) { 10 | data[[col]] <- round(data[[col]], digits = digits) 11 | } 12 | } 13 | data 14 | } else if (is.numeric(data)) { 15 | # Normalize numeric vectors 16 | round(data, digits = digits) 17 | } else { 18 | # Return other types as-is 19 | data 20 | } 21 | } 22 | 23 | # Helper function to normalize ggplot_build output for snapshots 24 | normalize_ggplot_build <- function(plot, digits = 6) { 25 | build_data <- ggplot2::ggplot_build(plot)$data 26 | normalize_ggplot_data(build_data, digits = digits) 27 | } 28 | 29 | # Helper function for ggvenn specific normalization 30 | normalize_ggvenn_output <- function(plot, digits = 6) { 31 | normalize_ggplot_build(plot, digits = digits) 32 | } 33 | -------------------------------------------------------------------------------- /man/data_preparation.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data_preparation.R 3 | \name{data_preparation} 4 | \alias{data_preparation} 5 | \alias{data_frame_to_list} 6 | \alias{list_to_data_frame} 7 | \title{Utility functions for data type conversion between data.frame and list.} 8 | \usage{ 9 | data_frame_to_list(x) 10 | 11 | list_to_data_frame(x) 12 | } 13 | \arguments{ 14 | \item{x}{A data.frame with logical columns representing sets, or a list of sets.} 15 | } 16 | \value{ 17 | A list of sets or a data.frame with logical columns representing sets. 18 | } 19 | \description{ 20 | Utility functions for data type conversion between data.frame and list. 21 | } 22 | \examples{ 23 | # Convert data.frame to list 24 | d <- dplyr::tibble(name = 1:6, 25 | A = c(rep(TRUE, 5), FALSE), 26 | B = rep(c(FALSE, TRUE), each = 3)) 27 | print(d) 28 | data_frame_to_list(d) 29 | 30 | # Convert list to data.frame 31 | a <- list(A = 1:5, B = 4:6) 32 | print(a) 33 | list_to_data_frame(a) 34 | 35 | # Round-trip conversion 36 | identical(a, data_frame_to_list(list_to_data_frame(a))) # TRUE 37 | identical(d, list_to_data_frame(data_frame_to_list(d))) # TRUE 38 | } 39 | -------------------------------------------------------------------------------- /tests/testthat/test-geom_venn.R: -------------------------------------------------------------------------------- 1 | test_that("geom_venn", { 2 | 3 | d <- tibble(value = c(1, 2, 3, 4, 5, 6, 7, 8), 4 | `Set 1` = c(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE), 5 | `Set 2` = c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE), 6 | `Set 3` = c(TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE), 7 | `Set 4` = c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE)) 8 | 9 | # draw two-set venn (use A, B in aes) 10 | expect_snapshot( 11 | normalize_ggplot_build( 12 | ggplot(d, aes(A = `Set 1`, B = `Set 2`)) + 13 | geom_venn() + theme_void() + coord_fixed() 14 | ) 15 | ) 16 | 17 | # draw three-set venn (use A, B, C in aes) 18 | expect_snapshot( 19 | normalize_ggplot_build( 20 | ggplot(d, aes(A = `Set 1`, B = `Set 2`, C = `Set 3`)) + 21 | geom_venn() + theme_void() + coord_fixed() 22 | ) 23 | ) 24 | 25 | # draw four-set venn (use A, B, C, D in aes) 26 | expect_snapshot( 27 | normalize_ggplot_build( 28 | ggplot(d, aes(A = `Set 1`, B = `Set 2`, C = `Set 3`, D = `Set 4`)) + 29 | geom_venn() + theme_void() + coord_fixed() 30 | ) 31 | ) 32 | }) 33 | -------------------------------------------------------------------------------- /tests/testthat/test-ggvenn.R: -------------------------------------------------------------------------------- 1 | test_that("ggvenn + list", { 2 | #library(ggvenn) 3 | 4 | a <- list("Set 1" = c(1, 3, 5, 7, 9), 5 | "Set 2" = c(1, 5, 9, 13), 6 | "Set 3" = c(1, 2, 8, 9), 7 | "Set 4" = c(6, 7, 10, 12)) 8 | 9 | expect_snapshot( 10 | normalize_ggvenn_output(ggvenn(a, c("Set 1", "Set 2"))) # draw two-set venn 11 | ) 12 | 13 | expect_snapshot( 14 | normalize_ggvenn_output(ggvenn(a, c("Set 1", "Set 2", "Set 3"))) # draw three-set venn 15 | ) 16 | 17 | expect_snapshot( 18 | normalize_ggvenn_output(ggvenn(a)) # without set names, the first 4 elements in list will be chose to draw four-set venn 19 | ) 20 | }) 21 | 22 | test_that("ggvenn + data.frame", { 23 | 24 | d <- dplyr::tibble(value = c(1, 2, 3, 5, 6, 7, 8, 9), 25 | `Set 1` = c(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE), 26 | `Set 2` = c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE), 27 | `Set 3` = c(TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE), 28 | `Set 4` = c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE)) 29 | 30 | expect_snapshot(normalize_ggvenn_output(ggvenn(d, c("Set 1", "Set 2")))) # draw two-set venn 31 | expect_snapshot(normalize_ggvenn_output(ggvenn(d, c("Set 1", "Set 2", "Set 3")))) # draw three-set venn 32 | expect_snapshot(normalize_ggvenn_output(ggvenn(d))) # without set names, the first 4 logical column in data.frame will be chose to draw four-set venn 33 | }) 34 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.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 | workflow_dispatch: 9 | 10 | name: R Package Check 11 | 12 | permissions: read-all 13 | 14 | jobs: 15 | R-CMD-check: 16 | runs-on: ${{ matrix.config.os }} 17 | 18 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | config: 24 | - {os: macos-latest, r: 'release'} 25 | - {os: windows-latest, r: 'release'} 26 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 27 | - {os: ubuntu-latest, r: 'release'} 28 | - {os: ubuntu-latest, r: 'oldrel-1'} 29 | 30 | env: 31 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 32 | R_KEEP_PKG_SOURCE: yes 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - uses: r-lib/actions/setup-pandoc@v2 38 | 39 | - uses: r-lib/actions/setup-r@v2 40 | with: 41 | r-version: ${{ matrix.config.r }} 42 | http-user-agent: ${{ matrix.config.http-user-agent }} 43 | use-public-rspm: true 44 | 45 | - uses: r-lib/actions/setup-r-dependencies@v2 46 | with: 47 | extra-packages: any::rcmdcheck 48 | needs: check 49 | 50 | - uses: r-lib/actions/check-r-package@v2 51 | with: 52 | upload-snapshots: true 53 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 54 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/show_elements.md: -------------------------------------------------------------------------------- 1 | # show_elements 2 | 3 | Code 4 | normalize_ggvenn_output(ggvenn(a, show_elements = TRUE)) 5 | Output 6 | [[1]] 7 | [[1]]$A 8 | [1] TRUE TRUE TRUE FALSE 9 | 10 | [[1]]$B 11 | [1] TRUE FALSE FALSE TRUE 12 | 13 | [[1]]$label 14 | [1] "apple" "pear" "peach" "lemon" 15 | 16 | [[1]]$PANEL 17 | [1] 1 1 1 1 18 | Levels: 1 19 | 20 | [[1]]$group 21 | [1] 3 2 2 1 22 | attr(,"n") 23 | [1] 3 24 | 25 | [[1]]$xmin 26 | [1] -1.866163 -1.866163 -1.866163 -1.866163 27 | 28 | [[1]]$xmax 29 | [1] 1.866667 1.866667 1.866667 1.866667 30 | 31 | [[1]]$ymin 32 | [1] -1.199874 -1.199874 -1.199874 -1.199874 33 | 34 | [[1]]$ymax 35 | [1] 1.4 1.4 1.4 1.4 36 | 37 | 38 | 39 | --- 40 | 41 | Code 42 | normalize_ggvenn_output(ggvenn(a, show_elements = TRUE, label_sep = "\n")) 43 | Output 44 | [[1]] 45 | [[1]]$A 46 | [1] TRUE TRUE TRUE FALSE 47 | 48 | [[1]]$B 49 | [1] TRUE FALSE FALSE TRUE 50 | 51 | [[1]]$label 52 | [1] "apple" "pear" "peach" "lemon" 53 | 54 | [[1]]$PANEL 55 | [1] 1 1 1 1 56 | Levels: 1 57 | 58 | [[1]]$group 59 | [1] 3 2 2 1 60 | attr(,"n") 61 | [1] 3 62 | 63 | [[1]]$xmin 64 | [1] -1.866163 -1.866163 -1.866163 -1.866163 65 | 66 | [[1]]$xmax 67 | [1] 1.866667 1.866667 1.866667 1.866667 68 | 69 | [[1]]$ymin 70 | [1] -1.199874 -1.199874 -1.199874 -1.199874 71 | 72 | [[1]]$ymax 73 | [1] 1.4 1.4 1.4 1.4 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /.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 | release: 7 | types: [published] 8 | workflow_dispatch: 9 | 10 | name: Build and Deploy Documentation 11 | 12 | permissions: read-all 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | permissions: 23 | contents: write 24 | # Only run for pushes to main/master branches and releases 25 | if: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')) || github.event_name == 'release' 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: r-lib/actions/setup-r-dependencies@v2 36 | with: 37 | extra-packages: any::pkgdown, local::. 38 | needs: website 39 | 40 | - name: Build site 41 | run: | 42 | # Install pkgdown if not already installed 43 | if (!requireNamespace("pkgdown", quietly = TRUE)) { 44 | install.packages("pkgdown") 45 | } 46 | 47 | # Build the site with better error handling 48 | tryCatch({ 49 | pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 50 | }, error = function(e) { 51 | cat("Error building site:", e$message, "\n") 52 | # Fallback to regular build_site 53 | pkgdown::build_site() 54 | }) 55 | 56 | # Ensure docs directory exists for deployment 57 | if (!dir.exists("docs")) { 58 | cat("Creating docs directory...\n") 59 | dir.create("docs", recursive = TRUE) 60 | } 61 | shell: Rscript {0} 62 | 63 | - name: Deploy to GitHub pages 🚀 64 | if: github.event_name != 'pull_request' 65 | uses: JamesIves/github-pages-deploy-action@v4.5.0 66 | with: 67 | clean: true 68 | branch: gh-pages 69 | folder: docs 70 | token: ${{ secrets.GITHUB_TOKEN }} 71 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/percentages.md: -------------------------------------------------------------------------------- 1 | # stats 2 | 3 | Code 4 | normalize_ggvenn_output(ggvenn(a, show_percentage = FALSE)) 5 | Output 6 | [[1]] 7 | [[1]]$A 8 | [1] TRUE TRUE TRUE TRUE TRUE 9 | 10 | [[1]]$B 11 | [1] TRUE TRUE FALSE FALSE FALSE 12 | 13 | [[1]]$label 14 | [1] 1 2 3 4 5 15 | 16 | [[1]]$PANEL 17 | [1] 1 1 1 1 1 18 | Levels: 1 19 | 20 | [[1]]$group 21 | [1] 2 2 1 1 1 22 | attr(,"n") 23 | [1] 2 24 | 25 | [[1]]$xmin 26 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 27 | 28 | [[1]]$xmax 29 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 30 | 31 | [[1]]$ymin 32 | [1] -1.199874 -1.199874 -1.199874 -1.199874 -1.199874 33 | 34 | [[1]]$ymax 35 | [1] 1.4 1.4 1.4 1.4 1.4 36 | 37 | 38 | 39 | --- 40 | 41 | Code 42 | normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE)) 43 | Output 44 | [[1]] 45 | [[1]]$A 46 | [1] TRUE TRUE TRUE TRUE TRUE 47 | 48 | [[1]]$B 49 | [1] TRUE TRUE FALSE FALSE FALSE 50 | 51 | [[1]]$label 52 | [1] 1 2 3 4 5 53 | 54 | [[1]]$PANEL 55 | [1] 1 1 1 1 1 56 | Levels: 1 57 | 58 | [[1]]$group 59 | [1] 2 2 1 1 1 60 | attr(,"n") 61 | [1] 2 62 | 63 | [[1]]$xmin 64 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 65 | 66 | [[1]]$xmax 67 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 68 | 69 | [[1]]$ymin 70 | [1] -1.199874 -1.199874 -1.199874 -1.199874 -1.199874 71 | 72 | [[1]]$ymax 73 | [1] 1.4 1.4 1.4 1.4 1.4 74 | 75 | 76 | 77 | --- 78 | 79 | Code 80 | normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE, digits = 2)) 81 | Output 82 | [[1]] 83 | [[1]]$A 84 | [1] TRUE TRUE TRUE TRUE TRUE 85 | 86 | [[1]]$B 87 | [1] TRUE TRUE FALSE FALSE FALSE 88 | 89 | [[1]]$label 90 | [1] 1 2 3 4 5 91 | 92 | [[1]]$PANEL 93 | [1] 1 1 1 1 1 94 | Levels: 1 95 | 96 | [[1]]$group 97 | [1] 2 2 1 1 1 98 | attr(,"n") 99 | [1] 2 100 | 101 | [[1]]$xmin 102 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 103 | 104 | [[1]]$xmax 105 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 106 | 107 | [[1]]$ymin 108 | [1] -1.199874 -1.199874 -1.199874 -1.199874 -1.199874 109 | 110 | [[1]]$ymax 111 | [1] 1.4 1.4 1.4 1.4 1.4 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # ggvenn NEWS 2 | 3 | ## Version 0.1.19 (2025-10-08, a quick fix) 4 | 5 | - Fix: Resolve count and percentage display issues in Venn diagrams 6 | - Enhancement: Improve README documentation with comprehensive usage examples and feature descriptions 7 | 8 | ## Version 0.1.18 (2025-10-08) 9 | 10 | - Feature: Extend Venn diagram support from 4 to 5 sets (with geometry functions for up to 8 sets) 11 | - Feature: Add `get_venn_table()` function to extract Venn diagram intersection data 12 | - Feature: Add text truncation and element display options with `max_elements` and `text_truncate` parameters 13 | - Feature: Remove lifecycle dependency and enhance parameter flexibility 14 | - Fix: Correct intersection calculation and display with `auto_scale=TRUE` 15 | - Fix: Improve element display logic and label positioning 16 | - Refactor: Consolidate data conversion functions and improve code organization 17 | 18 | ## Version 0.1.17 (2025-10-06) 19 | 20 | - Feature: Add `show_set_totals` and `show_stats` arguments for enhanced display flexibility 21 | - Feature: Add data conversion utility functions (`data_frame_to_list`, `list_to_data_frame`) 22 | - Feature: Add comma separation option for large numbers (#42) 23 | - Fix: Allow individual stroke color/alpha settings for each set (#33) 24 | - Fix: Replace deprecated `size` aesthetic with `linewidth` for ggplot2 3.4.0 compatibility 25 | - Refactor: Improve `geom_venn` function structure and readability 26 | 27 | ## Version 0.1.10 (2023-03-31) 28 | 29 | - Adapt to CRAN policies 30 | 31 | ## Version 0.1.9 (2023-03-31) 32 | 33 | - Omit NA in list (#35) 34 | 35 | ## Version 0.1.8 (2021-01-10) 36 | 37 | - Solve notes/warnings for CRAN check 38 | 39 | ## Version 0.1.7 (2020-12-24) 40 | 41 | - Fix issues: Allow run ggvenn() without loading the package (#12) 42 | - Add @importFrom declarations (#12) 43 | 44 | ## Version 0.1.6 (2020-12-14) 45 | 46 | - Allow to change precision for percentages (#11) 47 | 48 | ## Version 0.1.5 (2020-09-04) 49 | 50 | - Allow to customize label sep (#9) 51 | 52 | ## Version 0.1.4 (2020-08-16) 53 | 54 | - Correct usage for old dplyr-1.0.0 (#8) 55 | - Fix error from masked dplyr::count (#7) 56 | 57 | ## Version 0.1.3 (2020-03-13) 58 | 59 | - Allow hiding percentage (#5, #6) 60 | - Use option 'show_percentage' instead of 'value_type' 61 | 62 | ## Version 0.1.2 (2020-02-27) 63 | 64 | - Allow to show set elements (#4) 65 | 66 | ## Version 0.1.1 (2019-12-17) 67 | 68 | - Fix error in count (A & B & !C & D) part (#3) 69 | - Adapt to using 'data.table' 70 | - Add code to check such typos 71 | 72 | ## Version 0.1.0 (2019-12-17) 73 | 74 | - Allow to set colors and other attributes (#2) 75 | 76 | ## Version 0.0.0.9000 (2019-12-17) 77 | 78 | - Initial release with basic implementation 79 | - Implement ggplot (layer) grammar 80 | - Basic venn diagram functionality 81 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/geom_venn.md: -------------------------------------------------------------------------------- 1 | # geom_venn 2 | 3 | Code 4 | normalize_ggplot_build(ggplot(d, aes(A = `Set 1`, B = `Set 2`)) + geom_venn() + 5 | theme_void() + coord_fixed()) 6 | Output 7 | [[1]] 8 | [[1]]$A 9 | [1] TRUE FALSE TRUE TRUE FALSE TRUE FALSE TRUE 10 | 11 | [[1]]$B 12 | [1] TRUE FALSE FALSE TRUE FALSE FALSE FALSE TRUE 13 | 14 | [[1]]$PANEL 15 | [1] 1 1 1 1 1 1 1 1 16 | Levels: 1 17 | 18 | [[1]]$group 19 | [1] 3 1 2 3 1 2 1 3 20 | attr(,"n") 21 | [1] 3 22 | 23 | [[1]]$xmin 24 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 25 | [8] -1.866163 26 | 27 | [[1]]$xmax 28 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 29 | 30 | [[1]]$ymin 31 | [1] -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 32 | 33 | [[1]]$ymax 34 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 35 | 36 | 37 | 38 | --- 39 | 40 | Code 41 | normalize_ggplot_build(ggplot(d, aes(A = `Set 1`, B = `Set 2`, C = `Set 3`)) + 42 | geom_venn() + theme_void() + coord_fixed()) 43 | Output 44 | [[1]] 45 | [[1]]$A 46 | [1] TRUE FALSE TRUE TRUE FALSE TRUE FALSE TRUE 47 | 48 | [[1]]$B 49 | [1] TRUE FALSE FALSE TRUE FALSE FALSE FALSE TRUE 50 | 51 | [[1]]$C 52 | [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE TRUE 53 | 54 | [[1]]$PANEL 55 | [1] 1 1 1 1 1 1 1 1 56 | Levels: 1 57 | 58 | [[1]]$group 59 | [1] 5 2 3 4 1 3 2 5 60 | attr(,"n") 61 | [1] 5 62 | 63 | [[1]]$xmin 64 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 65 | [8] -1.866163 66 | 67 | [[1]]$xmax 68 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 69 | 70 | [[1]]$ymin 71 | [1] -2 -2 -2 -2 -2 -2 -2 -2 72 | 73 | [[1]]$ymax 74 | [1] 2 2 2 2 2 2 2 2 75 | 76 | 77 | 78 | --- 79 | 80 | Code 81 | normalize_ggplot_build(ggplot(d, aes(A = `Set 1`, B = `Set 2`, C = `Set 3`, D = `Set 4`)) + 82 | geom_venn() + theme_void() + coord_fixed()) 83 | Output 84 | [[1]] 85 | [[1]]$A 86 | [1] TRUE FALSE TRUE TRUE FALSE TRUE FALSE TRUE 87 | 88 | [[1]]$B 89 | [1] TRUE FALSE FALSE TRUE FALSE FALSE FALSE TRUE 90 | 91 | [[1]]$C 92 | [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE TRUE 93 | 94 | [[1]]$D 95 | [1] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE 96 | 97 | [[1]]$PANEL 98 | [1] 1 1 1 1 1 1 1 1 99 | Levels: 1 100 | 101 | [[1]]$group 102 | [1] 6 2 3 5 1 4 2 6 103 | attr(,"n") 104 | [1] 6 105 | 106 | [[1]]$xmin 107 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 108 | [8] -2.085847 109 | 110 | [[1]]$xmax 111 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 112 | 113 | [[1]]$ymin 114 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 115 | [8] -1.885847 116 | 117 | [[1]]$ymax 118 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /man/ggvenn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/ggvenn.R 3 | \name{ggvenn} 4 | \alias{ggvenn} 5 | \title{Plot venn diagram as an independent function. It supports both data frame and list as input.} 6 | \usage{ 7 | ggvenn( 8 | data, 9 | columns = NULL, 10 | element_column = NULL, 11 | show_elements = FALSE, 12 | show_set_totals = "none", 13 | show_stats = c("cp", "c", "p"), 14 | show_counts = TRUE, 15 | show_percentage = TRUE, 16 | digits = 1, 17 | label_sep = ",", 18 | count_column = NULL, 19 | show_outside = c("auto", "none", "always"), 20 | auto_scale = FALSE, 21 | fill_color = default_color_list, 22 | fill_alpha = 0.5, 23 | stroke_color = "black", 24 | stroke_alpha = 1, 25 | stroke_size = 1, 26 | stroke_linetype = "solid", 27 | set_name_color = "black", 28 | set_name_size = 6, 29 | text_color = "black", 30 | text_size = 4, 31 | comma_sep = FALSE, 32 | padding = 0.2, 33 | max_elements = 6, 34 | text_truncate = TRUE 35 | ) 36 | } 37 | \arguments{ 38 | \item{data}{A data.frame or a list as input data.} 39 | 40 | \item{columns}{A character vector use as index to select columns/elements.} 41 | 42 | \item{element_column}{A single character value use as column name to select elements. 43 | It is only allowed when data is a data.frame.} 44 | 45 | \item{show_elements}{Show set elements instead of count/percentage.} 46 | 47 | \item{show_set_totals}{Show total count (c) and/or percentage (p) for each set. 48 | Pass a string like "cp" to show both. Any other string like "none" to hide both.} 49 | 50 | \item{show_stats}{Show count (c) and/or percentage (p) for each set. 51 | Pass a string like "cp" to show both. Any other string like "none" to hide both.} 52 | 53 | \item{show_counts}{Show count for each set.} 54 | 55 | \item{show_percentage}{Show percentage for each set.} 56 | 57 | \item{digits}{The desired number of digits after the decimal point.} 58 | 59 | \item{label_sep}{Separator character for displaying elements.} 60 | 61 | \item{count_column}{Specify column for element repeat count.} 62 | 63 | \item{show_outside}{Show outside elements (not belongs to any set). Options: "auto", "none", "always".} 64 | 65 | \item{auto_scale}{Allow automatically resizing circles according to element counts (only for 2-set diagrams).} 66 | 67 | \item{fill_color}{Filling colors in circles. Can be a single color or a vector of colors for each set.} 68 | 69 | \item{fill_alpha}{Transparency for filling circles. Can be a single value or a vector for each set.} 70 | 71 | \item{stroke_color}{Stroke color for drawing circles. Can be a single color or a vector of colors for each set.} 72 | 73 | \item{stroke_alpha}{Transparency for drawing circles. Can be a single value or a vector for each set.} 74 | 75 | \item{stroke_size}{Stroke size for drawing circles. Can be a single value or a vector for each set.} 76 | 77 | \item{stroke_linetype}{Line type for drawing circles. Can be a single value or a vector for each set.} 78 | 79 | \item{set_name_color}{Text color for set names.} 80 | 81 | \item{set_name_size}{Text size for set names.} 82 | 83 | \item{text_color}{Text color for intersect contents.} 84 | 85 | \item{text_size}{Text size for intersect contents.} 86 | 87 | \item{comma_sep}{Whether to use comma as separator for displaying numbers.} 88 | 89 | \item{padding}{Padding for the plot. Change this to allow longer labels to be displayed.} 90 | 91 | \item{max_elements}{Maximum number of elements to display when show_elements=TRUE.} 92 | 93 | \item{text_truncate}{Whether to truncate text when elements exceed max_elements.} 94 | } 95 | \value{ 96 | The ggplot object to print or save to file. 97 | } 98 | \description{ 99 | Plot venn diagram as an independent function. It supports both data frame and list as input. 100 | } 101 | \examples{ 102 | library(ggvenn) 103 | 104 | # use list as input 105 | a <- list(A = 1:5, B = 4:9, C = c(2:3, 8:12), D = c(1, 5, 9)) 106 | ggvenn(a, c("A", "B")) 107 | ggvenn(a, c("A", "B", "C")) 108 | ggvenn(a) 109 | 110 | # use data.frame as input 111 | d <- dplyr::tibble(value = c(1, 2, 3, 5, 6, 7, 8, 9), 112 | `Set 1` = c(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE), 113 | `Set 2` = c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE), 114 | `Set 3` = c(TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE), 115 | `Set 4` = c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE)) 116 | ggvenn(d, c("Set 1", "Set 2")) 117 | ggvenn(d, c("Set 1", "Set 2", "Set 3")) 118 | ggvenn(d) 119 | 120 | # set fill color 121 | ggvenn(d, c("Set 1", "Set 2"), fill_color = c("red", "blue")) 122 | 123 | # hide percentage 124 | ggvenn(d, c("Set 1", "Set 2"), show_stats = "c") 125 | 126 | # change precision of percentages 127 | ggvenn(d, c("Set 1", "Set 2"), digits = 2) 128 | 129 | # show elements instead of count/percentage 130 | ggvenn(a, show_elements = TRUE) 131 | ggvenn(d, show_elements = TRUE, element_column = "value") 132 | 133 | } 134 | \seealso{ 135 | geom_venn 136 | } 137 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/set-totals.md: -------------------------------------------------------------------------------- 1 | # stats 2 | 3 | Code 4 | normalize_ggvenn_output(ggvenn(a, show_percentage = FALSE, show_elements = FALSE, 5 | text_size = 6)) 6 | Output 7 | [[1]] 8 | [[1]]$A 9 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 10 | 11 | [[1]]$B 12 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 13 | 14 | [[1]]$C 15 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 16 | 17 | [[1]]$D 18 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 19 | 20 | [[1]]$label 21 | [1] 1 3 5 7 9 13 2 8 6 10 12 22 | 23 | [[1]]$PANEL 24 | [1] 1 1 1 1 1 1 1 1 1 1 1 25 | Levels: 1 26 | 27 | [[1]]$group 28 | [1] 7 4 6 5 7 3 2 2 1 1 1 29 | attr(,"n") 30 | [1] 7 31 | 32 | [[1]]$xmin 33 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 34 | [8] -2.085847 -2.085847 -2.085847 -2.085847 35 | 36 | [[1]]$xmax 37 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 38 | [9] 2.085382 2.085382 2.085382 39 | 40 | [[1]]$ymin 41 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 42 | [8] -1.885847 -1.885847 -1.885847 -1.885847 43 | 44 | [[1]]$ymax 45 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 46 | 47 | 48 | 49 | --- 50 | 51 | Code 52 | normalize_ggvenn_output(ggvenn(a, show_set_totals = "c", show_elements = FALSE, 53 | text_size = 6)) 54 | Output 55 | [[1]] 56 | [[1]]$A 57 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 58 | 59 | [[1]]$B 60 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 61 | 62 | [[1]]$C 63 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 64 | 65 | [[1]]$D 66 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 67 | 68 | [[1]]$label 69 | [1] 1 3 5 7 9 13 2 8 6 10 12 70 | 71 | [[1]]$PANEL 72 | [1] 1 1 1 1 1 1 1 1 1 1 1 73 | Levels: 1 74 | 75 | [[1]]$group 76 | [1] 7 4 6 5 7 3 2 2 1 1 1 77 | attr(,"n") 78 | [1] 7 79 | 80 | [[1]]$xmin 81 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 82 | [8] -2.085847 -2.085847 -2.085847 -2.085847 83 | 84 | [[1]]$xmax 85 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 86 | [9] 2.085382 2.085382 2.085382 87 | 88 | [[1]]$ymin 89 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 90 | [8] -1.885847 -1.885847 -1.885847 -1.885847 91 | 92 | [[1]]$ymax 93 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 94 | 95 | 96 | 97 | --- 98 | 99 | Code 100 | normalize_ggvenn_output(ggvenn(a, show_set_totals = "p", show_elements = FALSE, 101 | text_size = 6)) 102 | Output 103 | [[1]] 104 | [[1]]$A 105 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 106 | 107 | [[1]]$B 108 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 109 | 110 | [[1]]$C 111 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 112 | 113 | [[1]]$D 114 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 115 | 116 | [[1]]$label 117 | [1] 1 3 5 7 9 13 2 8 6 10 12 118 | 119 | [[1]]$PANEL 120 | [1] 1 1 1 1 1 1 1 1 1 1 1 121 | Levels: 1 122 | 123 | [[1]]$group 124 | [1] 7 4 6 5 7 3 2 2 1 1 1 125 | attr(,"n") 126 | [1] 7 127 | 128 | [[1]]$xmin 129 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 130 | [8] -2.085847 -2.085847 -2.085847 -2.085847 131 | 132 | [[1]]$xmax 133 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 134 | [9] 2.085382 2.085382 2.085382 135 | 136 | [[1]]$ymin 137 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 138 | [8] -1.885847 -1.885847 -1.885847 -1.885847 139 | 140 | [[1]]$ymax 141 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 142 | 143 | 144 | 145 | --- 146 | 147 | Code 148 | normalize_ggvenn_output(ggvenn(a, show_set_totals = "cp", show_elements = FALSE, 149 | text_size = 6)) 150 | Output 151 | [[1]] 152 | [[1]]$A 153 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 154 | 155 | [[1]]$B 156 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 157 | 158 | [[1]]$C 159 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 160 | 161 | [[1]]$D 162 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 163 | 164 | [[1]]$label 165 | [1] 1 3 5 7 9 13 2 8 6 10 12 166 | 167 | [[1]]$PANEL 168 | [1] 1 1 1 1 1 1 1 1 1 1 1 169 | Levels: 1 170 | 171 | [[1]]$group 172 | [1] 7 4 6 5 7 3 2 2 1 1 1 173 | attr(,"n") 174 | [1] 7 175 | 176 | [[1]]$xmin 177 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 178 | [8] -2.085847 -2.085847 -2.085847 -2.085847 179 | 180 | [[1]]$xmax 181 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 182 | [9] 2.085382 2.085382 2.085382 183 | 184 | [[1]]$ymin 185 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 186 | [8] -1.885847 -1.885847 -1.885847 -1.885847 187 | 188 | [[1]]$ymax 189 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/ggvenn.md: -------------------------------------------------------------------------------- 1 | # ggvenn + list 2 | 3 | Code 4 | normalize_ggvenn_output(ggvenn(a, c("Set 1", "Set 2"))) 5 | Output 6 | [[1]] 7 | [[1]]$A 8 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 9 | 10 | [[1]]$B 11 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 12 | 13 | [[1]]$label 14 | [1] 1 3 5 7 9 13 2 8 6 10 12 15 | 16 | [[1]]$PANEL 17 | [1] 1 1 1 1 1 1 1 1 1 1 1 18 | Levels: 1 19 | 20 | [[1]]$group 21 | [1] 4 3 4 3 4 2 1 1 1 1 1 22 | attr(,"n") 23 | [1] 4 24 | 25 | [[1]]$xmin 26 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 27 | [8] -1.866163 -1.866163 -1.866163 -1.866163 28 | 29 | [[1]]$xmax 30 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 31 | [9] 1.866667 1.866667 1.866667 32 | 33 | [[1]]$ymin 34 | [1] -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 35 | 36 | [[1]]$ymax 37 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 38 | 39 | 40 | 41 | --- 42 | 43 | Code 44 | normalize_ggvenn_output(ggvenn(a, c("Set 1", "Set 2", "Set 3"))) 45 | Output 46 | [[1]] 47 | [[1]]$A 48 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 49 | 50 | [[1]]$B 51 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 52 | 53 | [[1]]$C 54 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 55 | 56 | [[1]]$label 57 | [1] 1 3 5 7 9 13 2 8 6 10 12 58 | 59 | [[1]]$PANEL 60 | [1] 1 1 1 1 1 1 1 1 1 1 1 61 | Levels: 1 62 | 63 | [[1]]$group 64 | [1] 6 4 5 4 6 3 2 2 1 1 1 65 | attr(,"n") 66 | [1] 6 67 | 68 | [[1]]$xmin 69 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 70 | [8] -1.866163 -1.866163 -1.866163 -1.866163 71 | 72 | [[1]]$xmax 73 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 74 | [9] 1.866667 1.866667 1.866667 75 | 76 | [[1]]$ymin 77 | [1] -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 78 | 79 | [[1]]$ymax 80 | [1] 2 2 2 2 2 2 2 2 2 2 2 81 | 82 | 83 | 84 | --- 85 | 86 | Code 87 | normalize_ggvenn_output(ggvenn(a)) 88 | Output 89 | [[1]] 90 | [[1]]$A 91 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 92 | 93 | [[1]]$B 94 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 95 | 96 | [[1]]$C 97 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 98 | 99 | [[1]]$D 100 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 101 | 102 | [[1]]$label 103 | [1] 1 3 5 7 9 13 2 8 6 10 12 104 | 105 | [[1]]$PANEL 106 | [1] 1 1 1 1 1 1 1 1 1 1 1 107 | Levels: 1 108 | 109 | [[1]]$group 110 | [1] 7 4 6 5 7 3 2 2 1 1 1 111 | attr(,"n") 112 | [1] 7 113 | 114 | [[1]]$xmin 115 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 116 | [8] -2.085847 -2.085847 -2.085847 -2.085847 117 | 118 | [[1]]$xmax 119 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 120 | [9] 2.085382 2.085382 2.085382 121 | 122 | [[1]]$ymin 123 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 124 | [8] -1.885847 -1.885847 -1.885847 -1.885847 125 | 126 | [[1]]$ymax 127 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 128 | 129 | 130 | 131 | # ggvenn + data.frame 132 | 133 | Code 134 | normalize_ggvenn_output(ggvenn(d, c("Set 1", "Set 2"))) 135 | Output 136 | [[1]] 137 | [[1]]$A 138 | [1] TRUE FALSE TRUE TRUE FALSE TRUE FALSE TRUE 139 | 140 | [[1]]$B 141 | [1] TRUE FALSE FALSE TRUE FALSE FALSE FALSE TRUE 142 | 143 | [[1]]$PANEL 144 | [1] 1 1 1 1 1 1 1 1 145 | Levels: 1 146 | 147 | [[1]]$group 148 | [1] 3 1 2 3 1 2 1 3 149 | attr(,"n") 150 | [1] 3 151 | 152 | [[1]]$xmin 153 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 154 | [8] -1.866163 155 | 156 | [[1]]$xmax 157 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 158 | 159 | [[1]]$ymin 160 | [1] -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 -1.4 161 | 162 | [[1]]$ymax 163 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 164 | 165 | 166 | 167 | --- 168 | 169 | Code 170 | normalize_ggvenn_output(ggvenn(d, c("Set 1", "Set 2", "Set 3"))) 171 | Output 172 | [[1]] 173 | [[1]]$A 174 | [1] TRUE FALSE TRUE TRUE FALSE TRUE FALSE TRUE 175 | 176 | [[1]]$B 177 | [1] TRUE FALSE FALSE TRUE FALSE FALSE FALSE TRUE 178 | 179 | [[1]]$C 180 | [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE TRUE 181 | 182 | [[1]]$PANEL 183 | [1] 1 1 1 1 1 1 1 1 184 | Levels: 1 185 | 186 | [[1]]$group 187 | [1] 5 2 3 4 1 3 2 5 188 | attr(,"n") 189 | [1] 5 190 | 191 | [[1]]$xmin 192 | [1] -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 -1.866163 193 | [8] -1.866163 194 | 195 | [[1]]$xmax 196 | [1] 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 1.866667 197 | 198 | [[1]]$ymin 199 | [1] -2 -2 -2 -2 -2 -2 -2 -2 200 | 201 | [[1]]$ymax 202 | [1] 2 2 2 2 2 2 2 2 203 | 204 | 205 | 206 | --- 207 | 208 | Code 209 | normalize_ggvenn_output(ggvenn(d)) 210 | Output 211 | [[1]] 212 | [[1]]$A 213 | [1] TRUE FALSE TRUE TRUE FALSE TRUE FALSE TRUE 214 | 215 | [[1]]$B 216 | [1] TRUE FALSE FALSE TRUE FALSE FALSE FALSE TRUE 217 | 218 | [[1]]$C 219 | [1] TRUE TRUE FALSE FALSE FALSE FALSE TRUE TRUE 220 | 221 | [[1]]$D 222 | [1] FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE 223 | 224 | [[1]]$PANEL 225 | [1] 1 1 1 1 1 1 1 1 226 | Levels: 1 227 | 228 | [[1]]$group 229 | [1] 6 2 3 5 1 4 2 6 230 | attr(,"n") 231 | [1] 6 232 | 233 | [[1]]$xmin 234 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 235 | [8] -2.085847 236 | 237 | [[1]]$xmax 238 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 239 | 240 | [[1]]$ymin 241 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 242 | [8] -1.885847 243 | 244 | [[1]]$ymax 245 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 246 | 247 | 248 | 249 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ggvenn 2 | 3 | Venn Diagram by ggplot2, with really easy-to-use API. 4 | 5 | 6 | [![R-CMD-check](https://github.com/yanlinlin82/ggvenn/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/yanlinlin82/ggvenn/actions/workflows/R-CMD-check.yaml) 7 | 8 | 9 | ## Screenshots 10 | 11 | ![Venn Plot from list](figures/ggvenn_list.png) 12 | 13 | ![Venn Plot from data.frame](figures/ggvenn_data.frame.png) 14 | 15 | ## Installation 16 | 17 | ```{r} 18 | install.packages("ggvenn") # install via CRAN 19 | ``` 20 | 21 | or 22 | 23 | ```{r} 24 | if (!require(devtools)) install.packages("devtools") 25 | devtools::install_github("yanlinlin82/ggvenn") # install via GitHub (for latest version) 26 | ``` 27 | 28 | ## Quick Start 29 | 30 | This package provides two main functions: `ggvenn()` and `geom_venn()`. It supports both `list` and `data.frame` type data as input. 31 | 32 | ### Basic Usage 33 | 34 | For `list` data (each element is a set): 35 | 36 | ```{r} 37 | library(ggvenn) 38 | 39 | a <- list(A = 1:5, B = 4:9, C = 3:7, D = 1:20, E = 15:19) 40 | ggvenn(a, c("A", "B")) # draw two-set venn 41 | ggvenn(a, c("A", "B", "C")) # draw three-set venn 42 | ggvenn(a, c("A", "B", "C", "D")) # draw four-set venn 43 | ggvenn(a) # without set names, all elements in list will be chosen to draw venn 44 | ``` 45 | 46 | For `data.frame` data (each logical column is a set): 47 | 48 | ```{r} 49 | d <- data.frame( 50 | id = 1:32, 51 | A = 1:32 %% 2 == 1, 52 | B = (1:32 %/% 2) %% 2 == 1, 53 | C = (1:32 %/% 4) %% 2 == 1, 54 | D = (1:32 %/% 8) %% 2 == 1, 55 | E = (1:32 %/% 16) %% 2 == 1 56 | ) 57 | ggvenn(d, c("A", "B")) # draw two-set venn 58 | ggvenn(d, c("A", "B", "C")) # draw three-set venn 59 | ggvenn(d, c("A", "B", "C", "D")) # draw four-set venn 60 | ggvenn(d) # without set names, all logical columns in data.frame will be chosen to draw venn 61 | ggvenn(d, element_column = "id", show_elements = TRUE) 62 | ``` 63 | 64 | ### Key Features 65 | 66 | - **Two functions**: `ggvenn()` for standalone plots, `geom_venn()` for ggplot2 grammar 67 | - **Multiple sets**: Supports 2-8 sets (optimal for 2-4 sets) 68 | - **Flexible input**: Works with both lists and data.frames 69 | - **Customizable**: Colors, sizes, transparency, and text options 70 | - **Show elements**: Display actual elements instead of just counts 71 | - **Auto-scaling**: Automatically resize circles based on element counts (2-set diagrams) 72 | 73 | For `data.frame` data, there is also another way to plot in ggplot grammar: 74 | 75 | ```{r} 76 | # draw two-set venn (use A, B in aes) 77 | ggplot(d, aes(A = `Set 1`, B = `Set 2`)) + 78 | geom_venn() + theme_void() + coord_fixed() 79 | 80 | # draw three-set venn (use A, B, C in aes) 81 | ggplot(d, aes(A = `Set 1`, B = `Set 2`, C = `Set 3`)) + 82 | geom_venn() + theme_void() + coord_fixed() 83 | 84 | # draw four-set venn (use A, B, C, D in aes) 85 | ggplot(d, aes(A = `Set 1`, B = `Set 2`, C = `Set 3`, D = `Set 4`)) + 86 | geom_venn() + theme_void() + coord_fixed() 87 | ``` 88 | 89 | ## More Options 90 | 91 | There are more options for customizing the venn diagram. 92 | 93 | 1. Tune the color and size 94 | 95 | For filling: 96 | 97 | - `fill_color` - default is c("blue", "yellow", "green", "red") 98 | - `fill_alpha` - default is 0.5 99 | 100 | For stroke: 101 | 102 | - `stroke_color` - default is "black" 103 | - `stroke_alpha` - default is 1 104 | - `stroke_size` - default is 1 105 | - `stroke_linetype` - default is "solid" 106 | 107 | For set name: 108 | 109 | - `set_name_color` - default is "black" 110 | - `set_name_size` - default is 6 111 | 112 | For text: 113 | 114 | - `text_color` - default is "black" 115 | - `text_size` - default is 4 116 | 117 | All parameters above could be used in both `ggvenn()` and `geom_venn()`. 118 | 119 | For example: 120 | 121 | ```{r} 122 | a <- list(A = 1:4, B = c(1,3,5)) 123 | ggvenn(a, stroke_linetype = 2, stroke_size = 0.5, 124 | set_name_color = "red", set_name_size = 15, 125 | fill_color = c("pink", "gold")) 126 | ``` 127 | 128 | 2. Show elements 129 | 130 | - `show_elements` - default is FALSE 131 | - `label_sep` - text used to concatenate elements, default is "," 132 | 133 | For example: 134 | 135 | ```{r} 136 | a <- list(A = c("apple", "pear", "peach"), 137 | B = c("apple", "lemon")) 138 | ggvenn(a, show_elements = TRUE) 139 | 140 | ggvenn(a, show_elements = TRUE, label_sep = "\n") # show elements in line 141 | ``` 142 | 143 | 3. Hide percentage 144 | 145 | - `show_percentage` - default is TRUE 146 | 147 | For example: 148 | 149 | ```{r} 150 | a <- list(A = 1:5, B = 1:2) 151 | ggvenn(a, show_percentage = FALSE) 152 | ``` 153 | 154 | 4. Change digits of percentage 155 | 156 | - `digits` - default is 1 157 | 158 | For example: 159 | 160 | ```{r} 161 | a <- list(A = 1:5, B = 1:2) 162 | ggvenn(a, digits = 2) 163 | ``` 164 | 165 | 5. Show/hide statistics 166 | 167 | - `show_stats` - control what to display: "cp" (count + percentage), "c" (count only), "p" (percentage only) 168 | - `show_set_totals` - show totals for each set: "cp", "c", "p", or "none" 169 | 170 | For example: 171 | 172 | ```{r} 173 | a <- list(A = 1:5, B = 1:2) 174 | ggvenn(a, show_stats = "c") # show only counts 175 | ggvenn(a, show_stats = "p") # show only percentages 176 | ggvenn(a, show_set_totals = "cp") # show set totals 177 | ``` 178 | 179 | 6. Control outside elements 180 | 181 | - `show_outside` - show elements not belonging to any set: "auto", "none", "always" 182 | 183 | For example: 184 | 185 | ```{r} 186 | a <- list(A = 1:5, B = 4:8, C = 10:15) # element 10-15 are outside A and B 187 | ggvenn(a, c("A", "B"), show_outside = "always") 188 | ``` 189 | 190 | 7. Auto-scaling (2-set diagrams only) 191 | 192 | - `auto_scale` - automatically resize circles based on element counts 193 | 194 | For example: 195 | 196 | ```{r} 197 | a <- list(A = 1:100, B = 50:150) # very different sizes 198 | ggvenn(a, auto_scale = TRUE) 199 | ``` 200 | 201 | ## Multiple Plots Layout 202 | 203 | When creating multiple venn diagrams, you can use `patchwork` or `gridExtra` for layout: 204 | 205 | ```{r} 206 | library(ggvenn) 207 | library(patchwork) # or library(gridExtra) 208 | 209 | # Create multiple plots 210 | g1 <- ggvenn(list(A = 1:5, B = 4:8)) 211 | g2 <- ggvenn(list(A = 1:5, B = 4:8, C = 3:7)) 212 | g3 <- ggvenn(list(A = 1:5, B = 4:8, C = 3:7, D = 1:20)) 213 | g4 <- ggvenn(list(A = 1:5, B = 4:8, C = 3:7, D = 1:20, E = 15:19)) 214 | 215 | # Using patchwork (recommended) 216 | (g1 | g2) / (g3 | g4) 217 | 218 | # Using gridExtra 219 | # gridExtra::grid.arrange(g1, g2, g3, g4, ncol = 2, nrow = 2) 220 | ``` 221 | 222 | ## Data Format 223 | 224 | The `ggvenn` support two types of input data: list and data.frame. Two functions (`data_frame_to_list()` and `list_to_data_frame()`) can convert data between the two types. 225 | 226 | ```r 227 | a <- list(A = 1:5, B = 4:6) 228 | d <- dplyr::tibble(key = 1:6, 229 | A = c(rep(TRUE, 5), FALSE), 230 | B = rep(c(FALSE, TRUE), each = 3)) 231 | 232 | identical(a, data_frame_to_list(d)) # TRUE 233 | identical(d, list_to_data_frame(a)) # TRUE 234 | ``` 235 | -------------------------------------------------------------------------------- /man/geom_venn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/geom_venn.R 3 | \name{geom_venn} 4 | \alias{geom_venn} 5 | \title{Plot venn diagram as a ggplot layer object. It supports only data frame as input.} 6 | \usage{ 7 | geom_venn( 8 | mapping = NULL, 9 | data = NULL, 10 | stat = "identity", 11 | position = "identity", 12 | ..., 13 | set_names = NULL, 14 | element_column = NULL, 15 | show_elements = FALSE, 16 | show_set_totals = "none", 17 | show_stats = c("cp", "c", "p"), 18 | show_counts = TRUE, 19 | show_percentage = TRUE, 20 | digits = 1, 21 | label_sep = ",", 22 | count_column = NULL, 23 | show_outside = c("auto", "none", "always"), 24 | auto_scale = FALSE, 25 | fill_color = default_color_list, 26 | fill_alpha = 0.5, 27 | stroke_color = "black", 28 | stroke_alpha = 1, 29 | stroke_size = 1, 30 | stroke_linetype = "solid", 31 | set_name_color = "black", 32 | set_name_size = 6, 33 | text_color = "black", 34 | text_size = 4, 35 | comma_sep = FALSE, 36 | padding = 0.2, 37 | max_elements = 6, 38 | text_truncate = TRUE 39 | ) 40 | } 41 | \arguments{ 42 | \item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}}. If specified and 43 | \code{inherit.aes = TRUE} (the default), it is combined with the default mapping 44 | at the top level of the plot. You must supply \code{mapping} if there is no plot 45 | mapping.} 46 | 47 | \item{data}{A data.frame or a list as input data.} 48 | 49 | \item{stat}{The statistical transformation to use on the data for this layer, as a string.} 50 | 51 | \item{position}{A position adjustment to use on the data for this layer. This 52 | can be used in various ways, including to prevent overplotting and 53 | improving the display. The \code{position} argument accepts the following: 54 | \itemize{ 55 | \item The result of calling a position function, such as \code{position_jitter()}. 56 | This method allows for passing extra arguments to the position. 57 | \item A string naming the position adjustment. To give the position as a 58 | string, strip the function name of the \code{position_} prefix. For example, 59 | to use \code{position_jitter()}, give the position as \code{"jitter"}. 60 | \item For more information and other ways to specify the position, see the 61 | \link[ggplot2:layer_positions]{layer position} documentation. 62 | }} 63 | 64 | \item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{layer()}}'s \code{params} argument. These 65 | arguments broadly fall into one of 4 categories below. Notably, further 66 | arguments to the \code{position} argument, or aesthetics that are required 67 | can \emph{not} be passed through \code{...}. Unknown arguments that are not part 68 | of the 4 categories below are ignored. 69 | \itemize{ 70 | \item Static aesthetics that are not mapped to a scale, but are at a fixed 71 | value and apply to the layer as a whole. For example, \code{colour = "red"} 72 | or \code{linewidth = 3}. The geom's documentation has an \strong{Aesthetics} 73 | section that lists the available options. The 'required' aesthetics 74 | cannot be passed on to the \code{params}. Please note that while passing 75 | unmapped aesthetics as vectors is technically possible, the order and 76 | required length is not guaranteed to be parallel to the input data. 77 | \item When constructing a layer using 78 | a \verb{stat_*()} function, the \code{...} argument can be used to pass on 79 | parameters to the \code{geom} part of the layer. An example of this is 80 | \code{stat_density(geom = "area", outline.type = "both")}. The geom's 81 | documentation lists which parameters it can accept. 82 | \item Inversely, when constructing a layer using a 83 | \verb{geom_*()} function, the \code{...} argument can be used to pass on parameters 84 | to the \code{stat} part of the layer. An example of this is 85 | \code{geom_area(stat = "density", adjust = 0.5)}. The stat's documentation 86 | lists which parameters it can accept. 87 | \item The \code{key_glyph} argument of \code{\link[ggplot2:layer]{layer()}} may also be passed on through 88 | \code{...}. This can be one of the functions described as 89 | \link[ggplot2:draw_key]{key glyphs}, to change the display of the layer in the legend. 90 | }} 91 | 92 | \item{set_names}{Set names, use column names if omitted.} 93 | 94 | \item{element_column}{A single character value use as column name to select elements.} 95 | 96 | \item{show_elements}{Show set elements instead of count/percentage.} 97 | 98 | \item{show_set_totals}{Show total count (c) and/or percentage (p) for each set. 99 | Pass a string like "cp" to show both. Any other string like "none" to hide both.} 100 | 101 | \item{show_stats}{Show count (c) and/or percentage (p) for each set. 102 | Pass a string like "cp" to show both.} 103 | 104 | \item{show_counts}{Show count for each set.} 105 | 106 | \item{show_percentage}{Show percentage for each set.} 107 | 108 | \item{digits}{The desired number of digits after the decimal point.} 109 | 110 | \item{label_sep}{Separator character for displaying elements.} 111 | 112 | \item{count_column}{Specify column for element repeat count.} 113 | 114 | \item{show_outside}{Show outside elements (not belongs to any set). Options: "auto", "none", "always".} 115 | 116 | \item{auto_scale}{Allow automatically resizing circles according to element counts (only for 2-set diagrams).} 117 | 118 | \item{fill_color}{Filling colors in circles. Can be a single color or a vector of colors for each set.} 119 | 120 | \item{fill_alpha}{Transparency for filling circles. Can be a single value or a vector for each set.} 121 | 122 | \item{stroke_color}{Stroke color for drawing circles. Can be a single color or a vector of colors for each set.} 123 | 124 | \item{stroke_alpha}{Transparency for drawing circles. Can be a single value or a vector for each set.} 125 | 126 | \item{stroke_size}{Stroke size for drawing circles. Can be a single value or a vector for each set.} 127 | 128 | \item{stroke_linetype}{Line type for drawing circles. Can be a single value or a vector for each set.} 129 | 130 | \item{set_name_color}{Text color for set names.} 131 | 132 | \item{set_name_size}{Text size for set names.} 133 | 134 | \item{text_color}{Text color for intersect contents.} 135 | 136 | \item{text_size}{Text size for intersect contents.} 137 | 138 | \item{comma_sep}{Whether to use comma as separator for displaying numbers.} 139 | 140 | \item{padding}{Padding for the plot. Change this to allow longer labels to be displayed.} 141 | 142 | \item{max_elements}{Maximum number of elements to display when show_elements=TRUE.} 143 | 144 | \item{text_truncate}{Whether to truncate text when elements exceed max_elements.} 145 | } 146 | \value{ 147 | A ggplot layer object to add to a ggplot. 148 | } 149 | \description{ 150 | Plot venn diagram as a ggplot layer object. It supports only data frame as input. 151 | } 152 | \examples{ 153 | library(ggvenn) 154 | 155 | # use data.frame as input 156 | d <- dplyr::tibble( 157 | value = c(1, 2, 3, 5, 6, 7, 8, 9), 158 | `Set 1` = c(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE), 159 | `Set 2` = c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE), 160 | `Set 3` = c(TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE), 161 | `Set 4` = c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE) 162 | ) 163 | 164 | # ggplot gramma 165 | ggplot(d) + 166 | geom_venn(aes(A = `Set 1`, B = `Set 2`)) + 167 | coord_fixed() + 168 | theme_void() 169 | ggplot(d) + 170 | geom_venn(aes(A = `Set 1`, B = `Set 2`, C = `Set 3`)) + 171 | coord_fixed() + 172 | theme_void() 173 | ggplot(d) + 174 | geom_venn(aes(A = `Set 1`, B = `Set 2`, C = `Set 3`, D = `Set 4`)) + 175 | coord_fixed() + 176 | theme_void() 177 | 178 | # set fill color 179 | ggplot(d) + 180 | geom_venn(aes(A = `Set 1`, B = `Set 2`), fill_color = c("red", "blue")) + 181 | coord_fixed() + 182 | theme_void() 183 | 184 | # hide percentage 185 | ggplot(d) + 186 | geom_venn(aes(A = `Set 1`, B = `Set 2`), show_stats = "c") + 187 | coord_fixed() + 188 | theme_void() 189 | 190 | # change precision of percentages 191 | ggplot(d) + 192 | geom_venn(aes(A = `Set 1`, B = `Set 2`), digits = 2) + 193 | coord_fixed() + 194 | theme_void() 195 | 196 | # show elements instead of count/percentage 197 | ggplot(d) + 198 | geom_venn(aes(A = `Set 1`, B = `Set 2`, C = `Set 3`, D = `Set 4`, label = value)) + 199 | coord_fixed() + 200 | theme_void() 201 | } 202 | \seealso{ 203 | ggvenn 204 | } 205 | -------------------------------------------------------------------------------- /R/ggvenn.R: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(ggplot2) 3 | 4 | #' Plot venn diagram as an independent function. It supports both data frame and list as input. 5 | #' 6 | #' @name ggvenn 7 | #' @importFrom ggplot2 ggplot_build 8 | #' @importFrom rlang sym 9 | #' @param data A data.frame or a list as input data. 10 | #' @param columns A character vector use as index to select columns/elements. 11 | #' @param element_column A single character value use as column name to select elements. 12 | #' It is only allowed when data is a data.frame. 13 | #' @param show_elements Show set elements instead of count/percentage. 14 | #' @param show_set_totals Show total count (c) and/or percentage (p) for each set. 15 | #' Pass a string like "cp" to show both. Any other string like "none" to hide both. 16 | #' @param show_stats Show count (c) and/or percentage (p) for each set. 17 | #' Pass a string like "cp" to show both. Any other string like "none" to hide both. 18 | #' @param show_counts Show count for each set. 19 | #' @param show_percentage Show percentage for each set. 20 | #' @param digits The desired number of digits after the decimal point. 21 | #' @param fill_color Filling colors in circles. Can be a single color or a vector of colors for each set. 22 | #' @param fill_alpha Transparency for filling circles. Can be a single value or a vector for each set. 23 | #' @param stroke_color Stroke color for drawing circles. Can be a single color or a vector of colors for each set. 24 | #' @param stroke_alpha Transparency for drawing circles. Can be a single value or a vector for each set. 25 | #' @param stroke_size Stroke size for drawing circles. Can be a single value or a vector for each set. 26 | #' @param stroke_linetype Line type for drawing circles. Can be a single value or a vector for each set. 27 | #' @param set_name_color Text color for set names. 28 | #' @param set_name_size Text size for set names. 29 | #' @param text_color Text color for intersect contents. 30 | #' @param text_size Text size for intersect contents. 31 | #' @param label_sep Separator character for displaying elements. 32 | #' @param count_column Specify column for element repeat count. 33 | #' @param show_outside Show outside elements (not belongs to any set). Options: "auto", "none", "always". 34 | #' @param auto_scale Allow automatically resizing circles according to element counts (only for 2-set diagrams). 35 | #' @param comma_sep Whether to use comma as separator for displaying numbers. 36 | #' @param padding Padding for the plot. Change this to allow longer labels to be displayed. 37 | #' @param max_elements Maximum number of elements to display when show_elements=TRUE. 38 | #' @param text_truncate Whether to truncate text when elements exceed max_elements. 39 | #' @return The ggplot object to print or save to file. 40 | #' @examples 41 | #' library(ggvenn) 42 | #' 43 | #' # use list as input 44 | #' a <- list(A = 1:5, B = 4:9, C = c(2:3, 8:12), D = c(1, 5, 9)) 45 | #' ggvenn(a, c("A", "B")) 46 | #' ggvenn(a, c("A", "B", "C")) 47 | #' ggvenn(a) 48 | #' 49 | #' # use data.frame as input 50 | #' d <- dplyr::tibble(value = c(1, 2, 3, 5, 6, 7, 8, 9), 51 | #' `Set 1` = c(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE), 52 | #' `Set 2` = c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE), 53 | #' `Set 3` = c(TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE), 54 | #' `Set 4` = c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE)) 55 | #' ggvenn(d, c("Set 1", "Set 2")) 56 | #' ggvenn(d, c("Set 1", "Set 2", "Set 3")) 57 | #' ggvenn(d) 58 | #' 59 | #' # set fill color 60 | #' ggvenn(d, c("Set 1", "Set 2"), fill_color = c("red", "blue")) 61 | #' 62 | #' # hide percentage 63 | #' ggvenn(d, c("Set 1", "Set 2"), show_stats = "c") 64 | #' 65 | #' # change precision of percentages 66 | #' ggvenn(d, c("Set 1", "Set 2"), digits = 2) 67 | #' 68 | #' # show elements instead of count/percentage 69 | #' ggvenn(a, show_elements = TRUE) 70 | #' ggvenn(d, show_elements = TRUE, element_column = "value") 71 | #' 72 | #' @seealso geom_venn 73 | #' @importFrom dplyr tibble tribble as_tibble %>% select_if mutate count filter inner_join 74 | #' @importFrom ggplot2 ggplot aes geom_polygon geom_segment geom_text scale_x_continuous 75 | #' @importFrom ggplot2 scale_y_continuous scale_fill_manual guides coord_fixed theme_void 76 | #' @importFrom ggplot2 layer scale_x_discrete scale_y_discrete expansion 77 | #' @importFrom stats na.omit 78 | #' @export 79 | ggvenn <- function( 80 | data, 81 | columns = NULL, 82 | element_column = NULL, 83 | show_elements = FALSE, 84 | show_set_totals = "none", 85 | show_stats = c("cp", "c", "p"), 86 | show_counts = TRUE, 87 | show_percentage = TRUE, 88 | digits = 1, 89 | label_sep = ",", 90 | count_column = NULL, 91 | show_outside = c("auto", "none", "always"), 92 | auto_scale = FALSE, 93 | fill_color = default_color_list, 94 | fill_alpha = .5, 95 | stroke_color = "black", 96 | stroke_alpha = 1, 97 | stroke_size = 1, 98 | stroke_linetype = "solid", 99 | set_name_color = "black", 100 | set_name_size = 6, 101 | text_color = "black", 102 | text_size = 4, 103 | comma_sep = FALSE, 104 | padding = 0.2, 105 | max_elements = 6, 106 | text_truncate = TRUE 107 | ) { 108 | show_outside <- match.arg(show_outside) 109 | 110 | if (!is.data.frame(data)) { 111 | if (is.list(data)) { 112 | if (!missing(element_column)) { 113 | stop("element_column is only allowed when data is a data.frame") 114 | } 115 | data <- list_to_data_frame(data) 116 | element_column <- "_key" 117 | } else { 118 | stop("data must be a list or a data.frame") 119 | } 120 | } else { 121 | if (!missing(element_column)) { 122 | stopifnot(is.character(element_column)) 123 | stopifnot(length(element_column) == 1) 124 | stopifnot(element_column %in% names(data)) 125 | } 126 | } 127 | 128 | set_names <- character(0) 129 | if (missing(columns)) { 130 | for (name in names(data)) { 131 | if (is.logical(data[[name]])) { 132 | set_names <- c(set_names, name) 133 | } 134 | } 135 | } else { 136 | for (name in columns) { 137 | stopifnot(name %in% names(data)) 138 | stopifnot(is.logical(data[[name]])) 139 | } 140 | set_names <- columns 141 | } 142 | n_sets <- length(set_names) 143 | stopifnot(n_sets >= min_set_num && n_sets <= max_set_num) 144 | set_names <- as.list(set_names) 145 | names(set_names) <- LETTERS[seq_len(n_sets)] 146 | 147 | if (!missing(show_stats)) { 148 | show_stats <- match.arg(show_stats) 149 | if (show_stats == "cp") { 150 | show_counts <- TRUE 151 | show_percentage <- TRUE 152 | } else if (show_stats == "c") { 153 | show_counts <- TRUE 154 | show_percentage <- FALSE 155 | } else if (show_stats == "p") { 156 | show_counts <- FALSE 157 | show_percentage <- TRUE 158 | } 159 | } else { 160 | show_counts <- ifelse(missing(show_counts), TRUE, show_counts) 161 | show_percentage <- ifelse(missing(show_percentage), ifelse(n_sets >= 5, FALSE, TRUE), show_percentage) 162 | if (show_counts && show_percentage) { 163 | show_stats <- "cp" 164 | } else if (show_counts) { 165 | show_stats <- "c" 166 | } else if (show_percentage) { 167 | show_stats <- "p" 168 | } 169 | } 170 | stopifnot(show_counts || show_percentage) 171 | 172 | # Create aes mapping using modern ggplot2 syntax 173 | aes_list <- list() 174 | for (i in seq_along(set_names)) { 175 | aes_list[[LETTERS[i]]] <- sym(set_names[[i]]) 176 | } 177 | 178 | if (!is.null(element_column)) { 179 | aes_list[["label"]] <- sym(element_column) 180 | } 181 | 182 | the_aes <- do.call(aes, aes_list) 183 | 184 | g <- ggplot(data) + 185 | geom_venn( 186 | the_aes, 187 | show_elements = show_elements, 188 | show_stats = show_stats, 189 | show_counts = show_counts, 190 | show_percentage = show_percentage, 191 | digits = digits, 192 | label_sep = label_sep, 193 | count_column = count_column, 194 | show_outside = show_outside, 195 | auto_scale = auto_scale, 196 | fill_color = fill_color, 197 | fill_alpha = fill_alpha, 198 | stroke_color = stroke_color, 199 | stroke_alpha = stroke_alpha, 200 | stroke_size = stroke_size, 201 | stroke_linetype = stroke_linetype, 202 | set_name_color = set_name_color, 203 | set_name_size = set_name_size, 204 | text_color = text_color, 205 | text_size = text_size, 206 | comma_sep = comma_sep, 207 | padding = padding, 208 | max_elements = max_elements, 209 | text_truncate = text_truncate 210 | ) + 211 | coord_fixed() + 212 | theme_void() 213 | g 214 | } 215 | 216 | #' Extract Venn diagram data table from ggvenn plot 217 | #' 218 | #' @param g A ggplot object created by ggvenn() 219 | #' @return A data frame containing the Venn diagram intersection data 220 | #' @importFrom ggplot2 ggplot_build 221 | #' @export 222 | #' @examples 223 | #' library(ggvenn) 224 | #' g <- ggvenn(list(A = 1:5, B = 4:9, C = c(2:3, 8:12), D = c(1, 5, 9))) 225 | #' get_venn_table(g) 226 | get_venn_table <- function(g) { 227 | # Check if input is a ggplot object 228 | if (!inherits(g, "ggplot")) { 229 | stop("Input must be a ggplot object") 230 | } 231 | 232 | # Check if there are layers 233 | if (length(g$layers) == 0) { 234 | stop("ggplot object has no layers") 235 | } 236 | 237 | # Check if the first layer is GeomVenn 238 | first_geom <- g$layers[[1]]$geom 239 | if (!inherits(first_geom, "GeomVenn")) { 240 | stop("First layer must be geom_venn()") 241 | } 242 | 243 | # Build the plot and get data 244 | built_plot <- ggplot_build(g) 245 | 246 | # Check if the built layer has venn_data 247 | built_geom <- built_plot$plot$layers[[1]]$geom 248 | if (is.null(built_geom$venn_data)) { 249 | stop("Failed to extract venn data from geom_venn") 250 | } 251 | 252 | # Check if venn_data has elements component 253 | if (is.null(built_geom$venn_data$elements)) { 254 | stop("venn_data does not contain elements component") 255 | } 256 | 257 | # Extract data 258 | df <- built_geom$venn_data$elements 259 | df[["text"]] <- NULL 260 | df 261 | } 262 | -------------------------------------------------------------------------------- /R/geom_venn.R: -------------------------------------------------------------------------------- 1 | #' Plot venn diagram as a ggplot layer object. It supports only data frame as input. 2 | #' 3 | #' @name geom_venn 4 | #' @inheritParams ggplot2::stat_identity 5 | #' @param stat The statistical transformation to use on the data for this layer, as a string. 6 | #' @param data A data.frame or a list as input data. 7 | #' @param set_names Set names, use column names if omitted. 8 | #' @param element_column A single character value use as column name to select elements. 9 | #' @param show_elements Show set elements instead of count/percentage. 10 | #' @param show_set_totals Show total count (c) and/or percentage (p) for each set. 11 | #' Pass a string like "cp" to show both. Any other string like "none" to hide both. 12 | #' @param show_stats Show count (c) and/or percentage (p) for each set. 13 | #' Pass a string like "cp" to show both. 14 | #' @param show_counts Show count for each set. 15 | #' @param show_percentage Show percentage for each set. 16 | #' @param digits The desired number of digits after the decimal point. 17 | #' @param label_sep Separator character for displaying elements. 18 | #' @param count_column Specify column for element repeat count. 19 | #' @param show_outside Show outside elements (not belongs to any set). Options: "auto", "none", "always". 20 | #' @param auto_scale Allow automatically resizing circles according to element counts (only for 2-set diagrams). 21 | #' @param fill_color Filling colors in circles. Can be a single color or a vector of colors for each set. 22 | #' @param fill_alpha Transparency for filling circles. Can be a single value or a vector for each set. 23 | #' @param stroke_color Stroke color for drawing circles. Can be a single color or a vector of colors for each set. 24 | #' @param stroke_alpha Transparency for drawing circles. Can be a single value or a vector for each set. 25 | #' @param stroke_size Stroke size for drawing circles. Can be a single value or a vector for each set. 26 | #' @param stroke_linetype Line type for drawing circles. Can be a single value or a vector for each set. 27 | #' @param set_name_color Text color for set names. 28 | #' @param set_name_size Text size for set names. 29 | #' @param text_color Text color for intersect contents. 30 | #' @param text_size Text size for intersect contents. 31 | #' @param comma_sep Whether to use comma as separator for displaying numbers. 32 | #' @param padding Padding for the plot. Change this to allow longer labels to be displayed. 33 | #' @param max_elements Maximum number of elements to display when show_elements=TRUE. 34 | #' @param text_truncate Whether to truncate text when elements exceed max_elements. 35 | #' @return A ggplot layer object to add to a ggplot. 36 | #' @examples 37 | #' library(ggvenn) 38 | #' 39 | #' # use data.frame as input 40 | #' d <- dplyr::tibble( 41 | #' value = c(1, 2, 3, 5, 6, 7, 8, 9), 42 | #' `Set 1` = c(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE), 43 | #' `Set 2` = c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE), 44 | #' `Set 3` = c(TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE), 45 | #' `Set 4` = c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE) 46 | #' ) 47 | #' 48 | #' # ggplot gramma 49 | #' ggplot(d) + 50 | #' geom_venn(aes(A = `Set 1`, B = `Set 2`)) + 51 | #' coord_fixed() + 52 | #' theme_void() 53 | #' ggplot(d) + 54 | #' geom_venn(aes(A = `Set 1`, B = `Set 2`, C = `Set 3`)) + 55 | #' coord_fixed() + 56 | #' theme_void() 57 | #' ggplot(d) + 58 | #' geom_venn(aes(A = `Set 1`, B = `Set 2`, C = `Set 3`, D = `Set 4`)) + 59 | #' coord_fixed() + 60 | #' theme_void() 61 | #' 62 | #' # set fill color 63 | #' ggplot(d) + 64 | #' geom_venn(aes(A = `Set 1`, B = `Set 2`), fill_color = c("red", "blue")) + 65 | #' coord_fixed() + 66 | #' theme_void() 67 | #' 68 | #' # hide percentage 69 | #' ggplot(d) + 70 | #' geom_venn(aes(A = `Set 1`, B = `Set 2`), show_stats = "c") + 71 | #' coord_fixed() + 72 | #' theme_void() 73 | #' 74 | #' # change precision of percentages 75 | #' ggplot(d) + 76 | #' geom_venn(aes(A = `Set 1`, B = `Set 2`), digits = 2) + 77 | #' coord_fixed() + 78 | #' theme_void() 79 | #' 80 | #' # show elements instead of count/percentage 81 | #' ggplot(d) + 82 | #' geom_venn(aes(A = `Set 1`, B = `Set 2`, C = `Set 3`, D = `Set 4`, label = value)) + 83 | #' coord_fixed() + 84 | #' theme_void() 85 | #' @seealso ggvenn 86 | #' @importFrom dplyr as_label 87 | #' @export 88 | geom_venn <- function( 89 | mapping = NULL, 90 | data = NULL, 91 | stat = "identity", 92 | position = "identity", 93 | ..., 94 | set_names = NULL, 95 | element_column = NULL, 96 | show_elements = FALSE, 97 | show_set_totals = "none", 98 | show_stats = c("cp", "c", "p"), 99 | show_counts = TRUE, 100 | show_percentage = TRUE, 101 | digits = 1, 102 | label_sep = ",", 103 | count_column = NULL, 104 | show_outside = c("auto", "none", "always"), 105 | auto_scale = FALSE, 106 | fill_color = default_color_list, 107 | fill_alpha = .5, 108 | stroke_color = "black", 109 | stroke_alpha = 1, 110 | stroke_size = 1, 111 | stroke_linetype = "solid", 112 | set_name_color = "black", 113 | set_name_size = 6, 114 | text_color = "black", 115 | text_size = 4, 116 | comma_sep = FALSE, 117 | padding = 0.2, 118 | max_elements = 6, 119 | text_truncate = TRUE 120 | ) { 121 | # Conversion factor from typographic points to mm (same as ggplot2:::.pt) 122 | pt_to_mm <- 72.27 / 25.4 123 | 124 | 125 | show_outside <- match.arg(show_outside) 126 | 127 | if (!missing(show_stats)) { 128 | show_stats <- match.arg(show_stats) 129 | if (show_stats == "cp") { 130 | show_counts <- TRUE 131 | show_percentage <- TRUE 132 | } else if (show_stats == "c") { 133 | show_counts <- TRUE 134 | show_percentage <- FALSE 135 | } else if (show_stats == "p") { 136 | show_counts <- FALSE 137 | show_percentage <- TRUE 138 | } 139 | } else { 140 | show_counts <- ifelse(missing(show_counts), TRUE, show_counts) 141 | show_percentage <- ifelse(missing(show_percentage), TRUE, show_percentage) 142 | } 143 | stopifnot(show_counts || show_percentage) 144 | 145 | geom_venn_obj <- ggplot2::ggproto( 146 | "GeomVenn", 147 | ggplot2::Geom, 148 | required_aes = c("A", "B"), 149 | optional_aes = c("C", "D", "E", "F", "G", "H", "label"), 150 | extra_params = c("na.rm"), 151 | setup_data = function(self, data, params) { 152 | sets <- c("A", "B", "C", "D", "E", "F", "G", "H") 153 | sets <- sets[sets %in% names(data)] 154 | 155 | # Determine element_column from mapping if not provided 156 | if (is.null(element_column) && "label" %in% names(data)) { 157 | element_column <- "label" 158 | } 159 | 160 | self$venn_data <- prepare_venn_data( 161 | data, sets, element_column, 162 | show_elements, show_set_totals, show_counts, show_percentage, 163 | digits, label_sep, count_column, 164 | show_outside, auto_scale, comma_sep, 165 | max_elements, text_truncate 166 | ) 167 | 168 | all_x <- c( 169 | self$venn_data$shapes$x, 170 | self$venn_data$texts$x, 171 | self$venn_data$labels$x, 172 | self$venn_data$segs$x 173 | ) 174 | all_y <- c( 175 | self$venn_data$shapes$y, 176 | self$venn_data$texts$y, 177 | self$venn_data$labels$y, 178 | self$venn_data$segs$y 179 | ) 180 | data %>% 181 | dplyr::mutate( 182 | xmin = min(all_x) - padding, 183 | xmax = max(all_x) + padding, 184 | ymin = min(all_y) - padding, 185 | ymax = max(all_y) + padding 186 | ) 187 | }, 188 | draw_panel = function(self, data, panel_params, coord, ...) { 189 | attr <- self$customize_attributes 190 | venn <- self$venn_data 191 | 192 | d0 <- ggplot2::coord_munch(coord, venn$shapes, panel_params) 193 | d <- d0 %>% 194 | dplyr::filter(!duplicated(group)) 195 | 196 | update_column <- function(d, column, value) { 197 | if (length(value) == 1) { 198 | d[[column]] <- value 199 | } else { 200 | # For multiple values, assign based on group index 201 | d[[column]] <- value[d$group] 202 | } 203 | d 204 | } 205 | d <- update_column(d, "fill_color", attr$fill_color) 206 | d <- update_column(d, "fill_alpha", attr$fill_alpha) 207 | d <- update_column(d, "stroke_color", attr$stroke_color) 208 | d <- update_column(d, "stroke_alpha", attr$stroke_alpha) 209 | d <- update_column(d, "stroke_size", attr$stroke_size) 210 | d <- update_column(d, "stroke_linetype", attr$stroke_linetype) 211 | 212 | gl <- grid::gList( 213 | grid::polygonGrob( 214 | id = d0$group, 215 | d0$x, d0$y, default.units = "native", 216 | gp = grid::gpar( 217 | col = NA, 218 | fill = scales::alpha(d$fill_color, d$fill_alpha) 219 | ) 220 | ), 221 | grid::polygonGrob( 222 | id = d0$group, 223 | d0$x, d0$y, default.units = "native", 224 | gp = grid::gpar( 225 | col = scales::alpha(d$stroke_color, d$stroke_alpha), 226 | fill = NA, 227 | lwd = d$stroke_size * pt_to_mm, 228 | lty = d$stroke_linetype 229 | ) 230 | ) 231 | ) 232 | 233 | if (nrow(venn$labels) > 0) { 234 | # Replace set names in labels 235 | set_names <- self$set_names 236 | label_texts <- venn$labels$text 237 | updated_labels <- character(length = length(label_texts)) 238 | for (i in seq_along(label_texts)) { 239 | updated_labels[[i]] <- gsub(paste0("^", names(set_names)[[i]]), set_names[[i]], label_texts[[i]]) 240 | } 241 | d1 <- ggplot2::coord_munch(coord, venn$labels, panel_params) 242 | gl <- grid::gList( 243 | gl, 244 | grid::textGrob( 245 | updated_labels, 246 | d1$x, d1$y, default.units = "native", 247 | hjust = d1$hjust, vjust = d1$vjust, 248 | gp = grid::gpar( 249 | col = attr$set_name_color, 250 | fontsize = attr$set_name_size * pt_to_mm 251 | ) 252 | ) 253 | ) 254 | } 255 | 256 | if (nrow(venn$texts) > 0) { 257 | d2 <- ggplot2::coord_munch(coord, venn$texts, panel_params) 258 | gl <- grid::gList( 259 | gl, 260 | grid::textGrob( 261 | d2$text, 262 | d2$x, d2$y, default.units = "native", 263 | hjust = d2$hjust, vjust = d2$vjust, 264 | gp = grid::gpar(col = attr$text_color, fontsize = attr$text_size * pt_to_mm) 265 | ) 266 | ) 267 | } 268 | 269 | if (!is.null(venn$segs) && nrow(venn$segs) > 0) { 270 | d3 <- ggplot2::coord_munch(coord, venn$segs, panel_params) 271 | gl <- grid::gList( 272 | gl, 273 | grid::segmentsGrob( 274 | d3$x, d3$y, d3$xend, d3$yend, 275 | default.units = "native", 276 | gp = grid::gpar(col = attr$text_color, lwd = attr$text_size * pt_to_mm) 277 | ) 278 | ) 279 | } 280 | 281 | grid::grobTree(gl, name = "geom_venn") 282 | } 283 | ) 284 | 285 | the_layer <- ggplot2::layer( 286 | mapping = mapping, 287 | data = data, 288 | geom = geom_venn_obj, 289 | stat = stat, 290 | position = position, 291 | params = list(na.rm = TRUE, ...) 292 | ) 293 | 294 | old_compute_aesthetics <- the_layer$compute_aesthetics 295 | 296 | the_layer$compute_aesthetics <- function(self, data, plot) { 297 | if (is.null(set_names)) { 298 | self$geom$set_names <- character() 299 | for (name in names(plot$mapping)) { 300 | self$geom$set_names[name] <- dplyr::as_label(plot$mapping[[name]]) 301 | } 302 | for (name in names(self$mapping)) { 303 | self$geom$set_names[name] <- dplyr::as_label(self$mapping[[name]]) 304 | } 305 | } else { 306 | self$geom$set_names <- set_names 307 | } 308 | self$geom$customize_attributes <- list( 309 | show_counts = show_counts, 310 | show_percentage = show_percentage, 311 | show_set_totals = show_set_totals, 312 | digits = digits, 313 | label_sep = label_sep, 314 | count_column = count_column, 315 | show_outside = show_outside, 316 | auto_scale = auto_scale, 317 | fill_color = fill_color, 318 | fill_alpha = fill_alpha, 319 | stroke_color = stroke_color, 320 | stroke_alpha = stroke_alpha, 321 | stroke_size = stroke_size, 322 | stroke_linetype = stroke_linetype, 323 | set_name_color = set_name_color, 324 | set_name_size = set_name_size, 325 | text_color = text_color, 326 | text_size = text_size 327 | ) 328 | old_compute_aesthetics(data, plot) 329 | } 330 | the_layer 331 | } 332 | -------------------------------------------------------------------------------- /tests/testthat/_snaps/stats.md: -------------------------------------------------------------------------------- 1 | # stats 2 | 3 | Code 4 | normalize_ggvenn_output(ggvenn(a)) 5 | Output 6 | [[1]] 7 | [[1]]$A 8 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 9 | 10 | [[1]]$B 11 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 12 | 13 | [[1]]$C 14 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 15 | 16 | [[1]]$D 17 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 18 | 19 | [[1]]$label 20 | [1] 1 3 5 7 9 13 2 8 6 10 12 21 | 22 | [[1]]$PANEL 23 | [1] 1 1 1 1 1 1 1 1 1 1 1 24 | Levels: 1 25 | 26 | [[1]]$group 27 | [1] 7 4 6 5 7 3 2 2 1 1 1 28 | attr(,"n") 29 | [1] 7 30 | 31 | [[1]]$xmin 32 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 33 | [8] -2.085847 -2.085847 -2.085847 -2.085847 34 | 35 | [[1]]$xmax 36 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 37 | [9] 2.085382 2.085382 2.085382 38 | 39 | [[1]]$ymin 40 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 41 | [8] -1.885847 -1.885847 -1.885847 -1.885847 42 | 43 | [[1]]$ymax 44 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 45 | 46 | 47 | 48 | --- 49 | 50 | Code 51 | normalize_ggvenn_output(ggvenn(a, show_percentage = FALSE)) 52 | Output 53 | [[1]] 54 | [[1]]$A 55 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 56 | 57 | [[1]]$B 58 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 59 | 60 | [[1]]$C 61 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 62 | 63 | [[1]]$D 64 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 65 | 66 | [[1]]$label 67 | [1] 1 3 5 7 9 13 2 8 6 10 12 68 | 69 | [[1]]$PANEL 70 | [1] 1 1 1 1 1 1 1 1 1 1 1 71 | Levels: 1 72 | 73 | [[1]]$group 74 | [1] 7 4 6 5 7 3 2 2 1 1 1 75 | attr(,"n") 76 | [1] 7 77 | 78 | [[1]]$xmin 79 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 80 | [8] -2.085847 -2.085847 -2.085847 -2.085847 81 | 82 | [[1]]$xmax 83 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 84 | [9] 2.085382 2.085382 2.085382 85 | 86 | [[1]]$ymin 87 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 88 | [8] -1.885847 -1.885847 -1.885847 -1.885847 89 | 90 | [[1]]$ymax 91 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 92 | 93 | 94 | 95 | --- 96 | 97 | Code 98 | normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE)) 99 | Output 100 | [[1]] 101 | [[1]]$A 102 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 103 | 104 | [[1]]$B 105 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 106 | 107 | [[1]]$C 108 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 109 | 110 | [[1]]$D 111 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 112 | 113 | [[1]]$label 114 | [1] 1 3 5 7 9 13 2 8 6 10 12 115 | 116 | [[1]]$PANEL 117 | [1] 1 1 1 1 1 1 1 1 1 1 1 118 | Levels: 1 119 | 120 | [[1]]$group 121 | [1] 7 4 6 5 7 3 2 2 1 1 1 122 | attr(,"n") 123 | [1] 7 124 | 125 | [[1]]$xmin 126 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 127 | [8] -2.085847 -2.085847 -2.085847 -2.085847 128 | 129 | [[1]]$xmax 130 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 131 | [9] 2.085382 2.085382 2.085382 132 | 133 | [[1]]$ymin 134 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 135 | [8] -1.885847 -1.885847 -1.885847 -1.885847 136 | 137 | [[1]]$ymax 138 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 139 | 140 | 141 | 142 | --- 143 | 144 | Code 145 | normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE, show_stats = "c")) 146 | Output 147 | [[1]] 148 | [[1]]$A 149 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 150 | 151 | [[1]]$B 152 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 153 | 154 | [[1]]$C 155 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 156 | 157 | [[1]]$D 158 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 159 | 160 | [[1]]$label 161 | [1] 1 3 5 7 9 13 2 8 6 10 12 162 | 163 | [[1]]$PANEL 164 | [1] 1 1 1 1 1 1 1 1 1 1 1 165 | Levels: 1 166 | 167 | [[1]]$group 168 | [1] 7 4 6 5 7 3 2 2 1 1 1 169 | attr(,"n") 170 | [1] 7 171 | 172 | [[1]]$xmin 173 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 174 | [8] -2.085847 -2.085847 -2.085847 -2.085847 175 | 176 | [[1]]$xmax 177 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 178 | [9] 2.085382 2.085382 2.085382 179 | 180 | [[1]]$ymin 181 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 182 | [8] -1.885847 -1.885847 -1.885847 -1.885847 183 | 184 | [[1]]$ymax 185 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 186 | 187 | 188 | 189 | --- 190 | 191 | Code 192 | normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE, show_stats = "p")) 193 | Output 194 | [[1]] 195 | [[1]]$A 196 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 197 | 198 | [[1]]$B 199 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 200 | 201 | [[1]]$C 202 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 203 | 204 | [[1]]$D 205 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 206 | 207 | [[1]]$label 208 | [1] 1 3 5 7 9 13 2 8 6 10 12 209 | 210 | [[1]]$PANEL 211 | [1] 1 1 1 1 1 1 1 1 1 1 1 212 | Levels: 1 213 | 214 | [[1]]$group 215 | [1] 7 4 6 5 7 3 2 2 1 1 1 216 | attr(,"n") 217 | [1] 7 218 | 219 | [[1]]$xmin 220 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 221 | [8] -2.085847 -2.085847 -2.085847 -2.085847 222 | 223 | [[1]]$xmax 224 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 225 | [9] 2.085382 2.085382 2.085382 226 | 227 | [[1]]$ymin 228 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 229 | [8] -1.885847 -1.885847 -1.885847 -1.885847 230 | 231 | [[1]]$ymax 232 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 233 | 234 | 235 | 236 | --- 237 | 238 | Code 239 | normalize_ggvenn_output(ggvenn(a, show_percentage = TRUE, show_stats = "cp")) 240 | Output 241 | [[1]] 242 | [[1]]$A 243 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 244 | 245 | [[1]]$B 246 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 247 | 248 | [[1]]$C 249 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 250 | 251 | [[1]]$D 252 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 253 | 254 | [[1]]$label 255 | [1] 1 3 5 7 9 13 2 8 6 10 12 256 | 257 | [[1]]$PANEL 258 | [1] 1 1 1 1 1 1 1 1 1 1 1 259 | Levels: 1 260 | 261 | [[1]]$group 262 | [1] 7 4 6 5 7 3 2 2 1 1 1 263 | attr(,"n") 264 | [1] 7 265 | 266 | [[1]]$xmin 267 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 268 | [8] -2.085847 -2.085847 -2.085847 -2.085847 269 | 270 | [[1]]$xmax 271 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 272 | [9] 2.085382 2.085382 2.085382 273 | 274 | [[1]]$ymin 275 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 276 | [8] -1.885847 -1.885847 -1.885847 -1.885847 277 | 278 | [[1]]$ymax 279 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 280 | 281 | 282 | 283 | --- 284 | 285 | Code 286 | normalize_ggvenn_output(ggvenn(a, show_stats = "c")) 287 | Output 288 | [[1]] 289 | [[1]]$A 290 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 291 | 292 | [[1]]$B 293 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 294 | 295 | [[1]]$C 296 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 297 | 298 | [[1]]$D 299 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 300 | 301 | [[1]]$label 302 | [1] 1 3 5 7 9 13 2 8 6 10 12 303 | 304 | [[1]]$PANEL 305 | [1] 1 1 1 1 1 1 1 1 1 1 1 306 | Levels: 1 307 | 308 | [[1]]$group 309 | [1] 7 4 6 5 7 3 2 2 1 1 1 310 | attr(,"n") 311 | [1] 7 312 | 313 | [[1]]$xmin 314 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 315 | [8] -2.085847 -2.085847 -2.085847 -2.085847 316 | 317 | [[1]]$xmax 318 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 319 | [9] 2.085382 2.085382 2.085382 320 | 321 | [[1]]$ymin 322 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 323 | [8] -1.885847 -1.885847 -1.885847 -1.885847 324 | 325 | [[1]]$ymax 326 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 327 | 328 | 329 | 330 | --- 331 | 332 | Code 333 | normalize_ggvenn_output(ggvenn(a, show_stats = "p")) 334 | Output 335 | [[1]] 336 | [[1]]$A 337 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 338 | 339 | [[1]]$B 340 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 341 | 342 | [[1]]$C 343 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 344 | 345 | [[1]]$D 346 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 347 | 348 | [[1]]$label 349 | [1] 1 3 5 7 9 13 2 8 6 10 12 350 | 351 | [[1]]$PANEL 352 | [1] 1 1 1 1 1 1 1 1 1 1 1 353 | Levels: 1 354 | 355 | [[1]]$group 356 | [1] 7 4 6 5 7 3 2 2 1 1 1 357 | attr(,"n") 358 | [1] 7 359 | 360 | [[1]]$xmin 361 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 362 | [8] -2.085847 -2.085847 -2.085847 -2.085847 363 | 364 | [[1]]$xmax 365 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 366 | [9] 2.085382 2.085382 2.085382 367 | 368 | [[1]]$ymin 369 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 370 | [8] -1.885847 -1.885847 -1.885847 -1.885847 371 | 372 | [[1]]$ymax 373 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 374 | 375 | 376 | 377 | --- 378 | 379 | Code 380 | normalize_ggvenn_output(ggvenn(a, show_stats = "cp")) 381 | Output 382 | [[1]] 383 | [[1]]$A 384 | [1] TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE 385 | 386 | [[1]]$B 387 | [1] TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE 388 | 389 | [[1]]$C 390 | [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE 391 | 392 | [[1]]$D 393 | [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE 394 | 395 | [[1]]$label 396 | [1] 1 3 5 7 9 13 2 8 6 10 12 397 | 398 | [[1]]$PANEL 399 | [1] 1 1 1 1 1 1 1 1 1 1 1 400 | Levels: 1 401 | 402 | [[1]]$group 403 | [1] 7 4 6 5 7 3 2 2 1 1 1 404 | attr(,"n") 405 | [1] 7 406 | 407 | [[1]]$xmin 408 | [1] -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 -2.085847 409 | [8] -2.085847 -2.085847 -2.085847 -2.085847 410 | 411 | [[1]]$xmax 412 | [1] 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 2.085382 413 | [9] 2.085382 2.085382 2.085382 414 | 415 | [[1]]$ymin 416 | [1] -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 -1.885847 417 | [8] -1.885847 -1.885847 -1.885847 -1.885847 418 | 419 | [[1]]$ymax 420 | [1] 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 1.4 421 | 422 | 423 | 424 | -------------------------------------------------------------------------------- /R/data_preparation.R: -------------------------------------------------------------------------------- 1 | library(tibble) 2 | library(dplyr) 3 | 4 | #==========================================================# 5 | 6 | # Helper function to truncate text when showing elements 7 | truncate_element_text <- function(elements, max_elements, label_sep, text_truncate = TRUE) { 8 | if (!text_truncate || length(elements) <= max_elements) { 9 | return(paste(elements, collapse = label_sep)) 10 | } 11 | 12 | if (max_elements <= 0) { 13 | return("") 14 | } 15 | 16 | # Show first max_elements elements and add "..." 17 | visible_elements <- elements[1:max_elements] 18 | text <- paste(visible_elements, collapse = label_sep) 19 | 20 | if (length(elements) > max_elements) { 21 | text <- paste0(text, label_sep, "... (+", length(elements) - max_elements, " more)") 22 | } 23 | 24 | text 25 | } 26 | 27 | #' Utility functions for data type conversion between data.frame and list. 28 | #' 29 | #' @name data_preparation 30 | #' @param x A data.frame with logical columns representing sets, or a list of sets. 31 | #' @return A list of sets or a data.frame with logical columns representing sets. 32 | #' @examples 33 | #' # Convert data.frame to list 34 | #' d <- dplyr::tibble(name = 1:6, 35 | #' A = c(rep(TRUE, 5), FALSE), 36 | #' B = rep(c(FALSE, TRUE), each = 3)) 37 | #' print(d) 38 | #' data_frame_to_list(d) 39 | #' 40 | #' # Convert list to data.frame 41 | #' a <- list(A = 1:5, B = 4:6) 42 | #' print(a) 43 | #' list_to_data_frame(a) 44 | #' 45 | #' # Round-trip conversion 46 | #' identical(a, data_frame_to_list(list_to_data_frame(a))) # TRUE 47 | #' identical(d, list_to_data_frame(data_frame_to_list(d))) # TRUE 48 | #' @export 49 | data_frame_to_list <- function(x) { 50 | col_names <- colnames(x) 51 | logical_names <- colnames(x %>% select_if(is.logical)) 52 | rest_names <- col_names[!col_names %in% logical_names] 53 | keys <- seq_len(nrow(x)) 54 | if (length(rest_names) >= 1) { 55 | keys <- x[[rest_names[1]]] 56 | } 57 | lst <- lapply(logical_names, function(i) keys[x[[i]]]) 58 | names(lst) <- logical_names 59 | lst 60 | } 61 | 62 | #' @rdname data_preparation 63 | #' @export 64 | list_to_data_frame <- function(x) { 65 | keys <- unique(unlist(x)) 66 | if (length(keys) == 0) { 67 | keys <- character(0) 68 | } 69 | df <- tibble("_key" = keys) 70 | for (name in names(x)) { 71 | df[, name] <- df$`_key` %in% x[[name]] 72 | } 73 | df 74 | } 75 | 76 | #==========================================================# 77 | 78 | # Function to calculate set totals 79 | calculate_totals <- function(data, columns, show_set_totals, digits, comma_sep) { 80 | # Calculate counts for each set 81 | set_counts <- sapply(columns, function(column) { 82 | if (inherits(data, "data.frame")) { 83 | sum(data[[column]]) 84 | } else { 85 | length(data[[column]]) 86 | } 87 | }) 88 | names(set_counts) <- columns 89 | 90 | # Calculate total number of elements 91 | total_elements <- if (inherits(data, "data.frame")) { 92 | nrow(data) 93 | } else { 94 | nrow(list_to_data_frame(data)) 95 | } 96 | set_percentages <- set_counts / total_elements * 100 97 | 98 | if (comma_sep) { 99 | fmt_count <- "%s" 100 | set_counts <- sapply(set_counts, function(.x) sprintf(fmt_count, scales::label_comma()(.x))) 101 | } 102 | 103 | if (show_set_totals == "c") { 104 | return( 105 | paste0(names(set_counts), "\n", set_counts) 106 | ) 107 | } else if (show_set_totals == "p") { 108 | return( 109 | paste0(names(set_counts), "\n", round(set_percentages, digits), "%") 110 | ) 111 | } else if (show_set_totals == "cp") { 112 | return( 113 | paste0(names(set_counts), "\n", set_counts, " (", round(set_percentages, digits), "%)") 114 | ) 115 | } 116 | 117 | columns 118 | } 119 | 120 | # Helper function to generate element data frames for different set counts 121 | #' @importFrom rlang syms 122 | generate_element_df <- function(n_sets) { 123 | sets <- LETTERS[seq_len(n_sets)] 124 | 125 | # Generate all possible combinations 126 | combinations <- expand.grid(rep(list(c(TRUE, FALSE)), n_sets)) 127 | names(combinations) <- sets 128 | 129 | # Create names for combinations 130 | combination_names <- apply(combinations, 1, function(row) { 131 | active_sets <- sets[row] 132 | if (length(active_sets) == 0) { 133 | "-" 134 | } else { 135 | paste(active_sets, collapse = "") 136 | } 137 | }) 138 | 139 | # Create data frame 140 | df <- combinations 141 | df$name <- combination_names 142 | 143 | # Verify uniqueness 144 | count_cols <- sets 145 | count_result <- df %>% dplyr::count(!!!rlang::syms(count_cols)) 146 | stopifnot(all(count_result$n == 1)) 147 | 148 | df <- df %>% mutate(n = 0, text = "") 149 | df 150 | } 151 | 152 | calc_scale_info_2 <- function(auto_scale, n_sets, max_scale_diff = 5, spacing_size = 0.2) { 153 | if (auto_scale) { 154 | stopifnot(length(n_sets) == 4) 155 | ab_shared <- n_sets[[1]] 156 | b_specific <- n_sets[[2]] 157 | a_specific <- n_sets[[3]] 158 | rest <- n_sets[[4]] 159 | if (ab_shared == 0 && b_specific == 0 && a_specific == 0) { # both sets are empty 160 | a_radius <- 1 161 | b_radius <- 1 162 | overlap_size <- -spacing_size 163 | } else if (ab_shared + a_specific == 0) { # set A is empty 164 | a_radius <- 1 / max_scale_diff 165 | b_radius <- 1 166 | overlap_size <- -spacing_size 167 | } else if (ab_shared + b_specific == 0) { # set B is empty 168 | a_radius <- 1 169 | b_radius <- 1 / max_scale_diff 170 | overlap_size <- -spacing_size 171 | } else if (ab_shared == 0) { 172 | if (a_specific > b_specific) { 173 | a_radius <- 1 174 | b_radius <- max(sqrt(b_specific / a_specific), 1 / max_scale_diff) 175 | overlap_size <- -spacing_size 176 | } else { 177 | a_radius <- max(sqrt(a_specific / b_specific), 1 / max_scale_diff) 178 | b_radius <- 1 179 | overlap_size <- -spacing_size 180 | } 181 | } else if (a_specific + b_specific == 0) { # both sets are empty 182 | a_radius <- 1 183 | b_radius <- 1 184 | overlap_size <- 0.99 185 | } else if (a_specific == 0) { # B contains A 186 | a_radius <- max(sqrt(ab_shared / (ab_shared + b_specific)), 1 / max_scale_diff) 187 | b_radius <- 1 188 | overlap_size <- a_radius 189 | } else if (b_specific == 0) { # A contains B 190 | a_radius <- 1 191 | b_radius <- max(sqrt(ab_shared / (ab_shared + a_specific)), 1 / max_scale_diff) 192 | overlap_size <- b_radius 193 | } else { 194 | if (a_specific > b_specific) { # A is larger than B 195 | a_radius <- 1 196 | b_radius <- max(sqrt((ab_shared + b_specific) / (ab_shared + a_specific)), 1 / max_scale_diff) 197 | overlap_size <- b_radius * (ab_shared / (ab_shared + b_specific)) 198 | } else { # B is larger than A 199 | a_radius <- max(sqrt((ab_shared + a_specific) / (ab_shared + b_specific)), 1 / max_scale_diff) 200 | b_radius <- 1 201 | overlap_size <- a_radius * (ab_shared / (ab_shared + a_specific)) 202 | } 203 | } 204 | } else { 205 | a_radius <- 1 206 | b_radius <- 1 207 | overlap_size <- 1 / 3 208 | } 209 | c( 210 | auto_scale = auto_scale, 211 | a_radius = a_radius, 212 | b_radius = b_radius, 213 | overlap_size = overlap_size 214 | ) 215 | } 216 | 217 | # Helper function to process data frame elements 218 | process_data_frame_elements <- function( 219 | data, 220 | columns, 221 | n_sets, 222 | count_column, 223 | show_elements, 224 | label_sep, 225 | max_elements, 226 | text_truncate, 227 | element_column 228 | ) { 229 | # Validate logical columns 230 | for (i in seq_len(n_sets)) { 231 | stopifnot(is.logical(as_tibble(data)[, columns[[i]], drop = TRUE])) 232 | } 233 | 234 | # Generate element data frame 235 | df_element <- generate_element_df(n_sets) 236 | 237 | # Process each combination 238 | for (i in seq_len(nrow(df_element))) { 239 | # Create index based on combination 240 | idx <- NULL 241 | for (j in seq_len(n_sets)) { 242 | set_name <- LETTERS[j] 243 | current_idx <- (!xor(df_element[[set_name]][[i]], as_tibble(data)[, columns[[j]]])) 244 | if (is.null(idx)) { 245 | idx <- current_idx 246 | } else { 247 | idx <- idx & current_idx 248 | } 249 | } 250 | 251 | # Count elements 252 | if (is.null(count_column)) { 253 | df_element$n[[i]] <- sum(idx) 254 | } else { 255 | df_element$n[[i]] <- sum(as_tibble(data)[, count_column][idx, ]) 256 | } 257 | 258 | if (!is.null(element_column) && element_column %in% names(data)) { 259 | df_element$values[[i]] <- sort(as.vector(unlist(as_tibble(data)[idx, element_column]))) 260 | 261 | if (show_elements) { 262 | elements <- unlist(as_tibble(data)[idx, element_column]) 263 | df_element$text[[i]] <- truncate_element_text(elements, max_elements, label_sep, text_truncate) 264 | } 265 | } else { 266 | df_element$values[[i]] <- character(0) 267 | } 268 | } 269 | 270 | df_element 271 | } 272 | 273 | prepare_venn_data <- function( 274 | data, 275 | columns = NULL, 276 | element_column = NULL, 277 | show_elements = FALSE, 278 | show_set_totals = "", 279 | show_counts = TRUE, 280 | show_percentage = TRUE, 281 | digits = 1, 282 | label_sep = ",", 283 | count_column = NULL, 284 | show_outside = c("auto", "none", "always"), 285 | auto_scale = FALSE, 286 | comma_sep = FALSE, 287 | max_elements = 6, 288 | text_truncate = TRUE 289 | ) { 290 | show_outside <- match.arg(show_outside) 291 | 292 | stopifnot(is.data.frame(data)) 293 | 294 | set_names <- character(0) 295 | if (missing(columns)) { 296 | for (name in names(data)) { 297 | if (is.logical(data[[name]])) { 298 | set_names <- c(set_names, name) 299 | } 300 | } 301 | } else { 302 | for (name in columns) { 303 | stopifnot(name %in% names(data)) 304 | stopifnot(is.logical(data[[name]])) 305 | } 306 | set_names <- columns 307 | } 308 | n_sets <- length(set_names) 309 | stopifnot(n_sets >= min_set_num && n_sets <= max_set_num) 310 | 311 | if (!missing(element_column) && !is.null(element_column)) { 312 | stopifnot(is.character(element_column)) 313 | stopifnot(length(element_column) == 1) 314 | stopifnot(element_column %in% names(data)) 315 | } 316 | 317 | get_venn_funcs <- function(n_sets) { 318 | funcs <- NULL 319 | if (n_sets == 2) { 320 | funcs <- list( 321 | "calc_scale_info" = calc_scale_info_2, 322 | "gen_circle" = gen_circle_2, 323 | "gen_text_pos" = gen_text_pos_2, 324 | "gen_label_pos" = gen_label_pos_2, 325 | "gen_seg_pos" = gen_seg_pos_2 326 | ) 327 | } else if (n_sets == 3) { 328 | funcs <- list( 329 | "calc_scale_info" = NULL, 330 | "gen_circle" = gen_circle_3, 331 | "gen_text_pos" = gen_text_pos_3, 332 | "gen_label_pos" = gen_label_pos_3, 333 | "gen_seg_pos" = NULL 334 | ) 335 | } else if (n_sets == 4) { 336 | funcs <- list( 337 | "calc_scale_info" = NULL, 338 | "gen_circle" = gen_circle_4, 339 | "gen_text_pos" = gen_text_pos_4, 340 | "gen_label_pos" = gen_label_pos_4, 341 | "gen_seg_pos" = NULL 342 | ) 343 | } else if (n_sets == 5) { 344 | funcs <- list( 345 | "calc_scale_info" = NULL, 346 | "gen_circle" = gen_circle_5, 347 | "gen_text_pos" = gen_text_pos_5, 348 | "gen_label_pos" = gen_label_pos_5, 349 | "gen_seg_pos" = NULL 350 | ) 351 | } else if (n_sets == 6) { 352 | funcs <- list( 353 | "calc_scale_info" = NULL, 354 | "gen_circle" = gen_circle_6, 355 | "gen_text_pos" = gen_text_pos_6, 356 | "gen_label_pos" = gen_label_pos_6, 357 | "gen_seg_pos" = NULL 358 | ) 359 | } else if (n_sets == 7) { 360 | funcs <- list( 361 | "calc_scale_info" = NULL, 362 | "gen_circle" = gen_circle_7, 363 | "gen_text_pos" = gen_text_pos_7, 364 | "gen_label_pos" = gen_label_pos_7, 365 | "gen_seg_pos" = NULL 366 | ) 367 | } else if (n_sets == 8) { 368 | funcs <- list( 369 | "calc_scale_info" = NULL, 370 | "gen_circle" = gen_circle_8, 371 | "gen_text_pos" = gen_text_pos_8, 372 | "gen_label_pos" = gen_label_pos_8, 373 | "gen_seg_pos" = NULL 374 | ) 375 | } else { 376 | stop("logical columns in data.frame `data` or vector `columns` should be length between 2 and 5") 377 | } 378 | funcs 379 | } 380 | 381 | venn_funcs <- get_venn_funcs(n_sets) 382 | if (is.null(venn_funcs)) { 383 | stop("logical columns in data.frame `data` or vector `columns` should be length between 2 and 4") 384 | } 385 | 386 | df_element <- process_data_frame_elements( 387 | data, columns, length(columns), count_column, show_elements, label_sep, max_elements, text_truncate, element_column 388 | ) 389 | 390 | if (auto_scale) { 391 | if (is.null(venn_funcs[["calc_scale_info"]])) { 392 | stop("Error: 'auto_scale' parameter is supported for only two set venn so far.") 393 | } 394 | scale_info <- venn_funcs[["calc_scale_info"]](auto_scale, df_element$n) 395 | } else { 396 | scale_info <- NULL 397 | } 398 | 399 | df_shape <- venn_funcs[["gen_circle"]](scale_info) 400 | 401 | df_text <- venn_funcs[["gen_text_pos"]](scale_info) 402 | df_text <- df_text %>% inner_join(df_element, by = "name") 403 | 404 | df_label <- venn_funcs[["gen_label_pos"]](scale_info) 405 | 406 | df_seg <- NULL 407 | if (!is.null(venn_funcs[["gen_seg_pos"]])) { 408 | df_seg <- venn_funcs[["gen_seg_pos"]](scale_info) 409 | } 410 | 411 | df_label <- df_label %>% 412 | mutate( 413 | text = calculate_totals(data, columns, show_set_totals, digits, comma_sep), 414 | hjust = 0.5 415 | ) 416 | show_elements <- isTRUE(show_elements) 417 | 418 | if (!show_elements) { 419 | fmt_count <- "%d" 420 | fmt_percentage <- sprintf("%%.%df%%%%", digits) 421 | fmt_both <- sprintf("%%d\n(%%.%df%%%%)", digits) 422 | 423 | if (comma_sep) { 424 | fmt_count <- "%s" 425 | fmt_percentage <- sprintf("%%.%df%%%%", digits) 426 | fmt_both <- sprintf("%%s\n(%%.%df%%%%)", digits) 427 | } 428 | 429 | total_count <- sum(df_text$n) 430 | 431 | df_text <- df_text %>% mutate(text = dplyr::case_when( 432 | show_counts && show_percentage ~ { 433 | if (comma_sep) { 434 | sprintf(fmt_both, scales::label_comma()(n), 100 * n / total_count) 435 | } else { 436 | sprintf(fmt_both, n, 100 * n / total_count) 437 | } 438 | }, 439 | show_counts ~ { 440 | if (comma_sep) { 441 | sprintf(fmt_count, scales::label_comma()(n)) 442 | } else { 443 | sprintf(fmt_count, n) 444 | } 445 | }, 446 | show_percentage ~ sprintf(fmt_percentage, 100 * n / total_count), 447 | TRUE ~ "" 448 | )) 449 | } 450 | if ((show_outside == "none") || (show_outside == "auto" && df_text$n[[nrow(df_text)]] == 0)) { 451 | if (df_text$n[[nrow(df_text)]] > 0) 452 | warning("Although not display in plot, outside elements are still count in percentages.") 453 | df_text <- df_text[-nrow(df_text), ] 454 | } 455 | 456 | list( 457 | shapes = df_shape, 458 | texts = df_text, 459 | labels = df_label, 460 | segs = df_seg, 461 | elements = df_element 462 | ) 463 | } 464 | -------------------------------------------------------------------------------- /R/venn_geometry.R: -------------------------------------------------------------------------------- 1 | library(tibble) 2 | library(dplyr) 3 | library(ggplot2) 4 | 5 | #==========================================================# 6 | 7 | min_set_num <- 2 8 | max_set_num <- 5 9 | 10 | default_color_list <- c( 11 | "blue", "yellow", "green", "red", 12 | "purple", "orange", "pink", "brown" 13 | ) 14 | 15 | #==========================================================# 16 | 17 | gen_circle <- function( 18 | group, x_offset = 0, y_offset = 0, radius = 1, 19 | radius_b = radius, theta_offset = 0, length.out = 100 20 | ) { 21 | d <- tibble( 22 | group = group, 23 | theta = seq(0, 2 * pi, length.out = length.out) 24 | ) 25 | d <- d %>% 26 | mutate( 27 | x_raw = radius * cos(theta), 28 | y_raw = radius_b * sin(theta), 29 | x = x_offset + x_raw * cos(theta_offset) - y_raw * sin(theta_offset), 30 | y = y_offset + x_raw * sin(theta_offset) + y_raw * cos(theta_offset) 31 | ) 32 | d 33 | } 34 | 35 | gen_circle_list <- function( 36 | n, 37 | center_radius = 1, 38 | ellipse_a = 1.5, 39 | ellipse_b = 1, 40 | start_angle = pi / 2 + pi / 12, 41 | rotation_offset = -pi / 6 42 | ) { 43 | d <- list() 44 | for (i in seq_len(n)) { 45 | theta <- start_angle + 2 * pi * (i - 1) / n 46 | x <- center_radius * cos(theta) 47 | y <- center_radius * sin(theta) 48 | c <- gen_circle(i, x, y, ellipse_a, ellipse_b, theta + rotation_offset) 49 | d[[i]] <- c 50 | } 51 | d <- do.call(rbind, d) 52 | d 53 | } 54 | 55 | gen_label_pos_list <- function(name_list, radius = 2.6, start_angle = pi / 2) { 56 | n <- length(name_list) 57 | idx <- seq_len(n) 58 | d <- data.frame(name = name_list) 59 | d$x <- radius * cos(start_angle + (idx - 1) * 2 * pi / n) 60 | d$y <- radius * sin(start_angle + (idx - 1) * 2 * pi / n) 61 | d$hjust <- 0.5 62 | d$vjust <- 0.5 63 | d 64 | } 65 | 66 | #==========================================================# 67 | 68 | gen_circle_2 <- function(scale_info) { 69 | if (is.null(scale_info)) { 70 | # Default values when auto_scale is FALSE 71 | a_radius <- 1 72 | b_radius <- 1 73 | overlap_size <- 1 / 3 74 | } else { 75 | a_radius <- scale_info["a_radius"] 76 | b_radius <- scale_info["b_radius"] 77 | overlap_size <- scale_info["overlap_size"] 78 | } 79 | x_dist <- (a_radius + b_radius - overlap_size * 2) / 2 80 | rbind(gen_circle(1L, -x_dist, 0, a_radius), 81 | gen_circle(2L, x_dist, 0, b_radius)) 82 | } 83 | 84 | gen_text_pos_2 <- function(scale_info, min_overlap_for_text = 0.2) { 85 | df <- tribble( 86 | ~name, ~x, ~y, ~hjust, ~vjust, 87 | "A", -0.8, 0, 0.5, 0.5, 88 | "B", 0.8, 0, 0.5, 0.5, 89 | "AB", 0, 0, 0.5, 0.5, 90 | "-", 0, -1.2, 0.5, 0.5 91 | ) 92 | if (!is.null(scale_info) && scale_info["auto_scale"]) { 93 | a_radius <- scale_info["a_radius"] 94 | b_radius <- scale_info["b_radius"] 95 | overlap_size <- scale_info["overlap_size"] 96 | x_dist <- (a_radius + b_radius - overlap_size * 2) / 2 97 | if (overlap_size <= 0) { 98 | df$x[[1]] <- -x_dist 99 | df$x[[2]] <- x_dist 100 | df <- df %>% filter(name != "-") 101 | } else { 102 | if (overlap_size < min_overlap_for_text) { 103 | df$x[[1]] <- -x_dist - overlap_size 104 | df$x[[2]] <- x_dist + overlap_size 105 | if (a_radius < min_overlap_for_text) { 106 | df$x[[3]] <- -x_dist + (a_radius - overlap_size) / 2 107 | df$y[[3]] <- -1.5 * a_radius 108 | } else if (b_radius < min_overlap_for_text) { 109 | df$x[[3]] <- x_dist - (a_radius - overlap_size) / 2 110 | df$y[[3]] <- -1.5 * b_radius 111 | } else { 112 | df$x[[3]] <- -x_dist + a_radius - overlap_size 113 | df$y[[3]] <- -1.2 114 | } 115 | df$x[[4]] <- -x_dist - a_radius 116 | df$y[[4]] <- -1.6 117 | df$hjust[[4]] <- 0 118 | } else { 119 | df$x[[1]] <- -x_dist - overlap_size 120 | df$x[[2]] <- x_dist + overlap_size 121 | df$x[[3]] <- -x_dist + a_radius - overlap_size 122 | } 123 | # Don't filter out individual sets even if radius <= overlap_size 124 | # This ensures that sets with elements are always displayed 125 | # if (a_radius <= overlap_size) { 126 | # df <- df %>% filter(name != "A") 127 | # } else if (b_radius <= overlap_size) { 128 | # df <- df %>% filter(name != "B") 129 | # } 130 | } 131 | } 132 | df 133 | } 134 | 135 | gen_seg_pos_2 <- function(scale_info, min_overlap_for_text = 0.2) { 136 | df <- tibble(x = 0, y = 0, xend = 0, yend = 0)[-1, ] 137 | if (!is.null(scale_info)) { 138 | a_radius <- scale_info["a_radius"] 139 | b_radius <- scale_info["b_radius"] 140 | overlap_size <- scale_info["overlap_size"] 141 | if (overlap_size > 0 && scale_info["auto_scale"]) { 142 | x_dist <- (a_radius + b_radius - overlap_size * 2) / 2 143 | if (scale_info["overlap_size"] < min_overlap_for_text) { 144 | x_pos <- -x_dist + a_radius - overlap_size 145 | if (a_radius < min_overlap_for_text) { 146 | x2_pos <- -x_dist + 1.2 * (a_radius - overlap_size) / 2 147 | df <- tibble(x = x_pos, y = 0, xend = x2_pos, yend = -1.2 * a_radius) 148 | } else if (b_radius < min_overlap_for_text) { 149 | x2_pos <- x_dist - 1.2 * (a_radius - overlap_size) / 2 150 | df <- tibble(x = x_pos, y = 0, xend = x2_pos, yend = -1.2 * a_radius) 151 | } else { 152 | df <- tibble(x = x_pos, y = 0, xend = x_pos, yend = -1) 153 | } 154 | } 155 | } 156 | } 157 | return(df) 158 | } 159 | 160 | gen_label_pos_2 <- function(scale_info) { 161 | df <- tribble( 162 | ~name, ~x, ~y, ~hjust, ~vjust, 163 | "A", -0.8, 1.2, 0.5, 0, 164 | "B", 0.8, 1.2, 0.5, 0 165 | ) 166 | if (!is.null(scale_info) && scale_info["auto_scale"]) { 167 | } 168 | df 169 | } 170 | 171 | #==========================================================# 172 | 173 | gen_circle_3 <- function(scale_info) { 174 | rbind( 175 | gen_circle(1L, -2/3, (sqrt(3) + 2) / 6, 1), 176 | gen_circle(2L, 2/3,(sqrt(3) + 2) / 6, 1), 177 | gen_circle(3L, 0, -(sqrt(3) + 2) / 6, 1) 178 | ) 179 | } 180 | 181 | gen_text_pos_3 <- function(scale_info, min_overlap_for_text = 0.2) { 182 | tribble( 183 | ~name, ~x, ~y, ~hjust, ~vjust, 184 | "A", -0.8, 0.62, 0.5, 0.5, 185 | "B", 0.8, 0.62, 0.5, 0.5, 186 | "C", 0, -0.62, 0.5, 0.5, 187 | "AB", 0, 0.8, 0.5, 0.5, 188 | "AC", -0.5, 0, 0.5, 0.5, 189 | "BC", 0.5, 0, 0.5, 0.5, 190 | "ABC", 0, 0.2, 0.5, 0.5, 191 | "-", 1.2, -0.8, 0, 0.5 192 | ) 193 | } 194 | 195 | gen_label_pos_3 <- function(scale_info) { 196 | tribble( 197 | ~name, ~x, ~y, ~hjust, ~vjust, 198 | "A", -0.8, 1.8, 0.5, 0, 199 | "B", 0.8, 1.8, 0.5, 0, 200 | "C", 0, -1.8, 0.5, 1 201 | ) 202 | } 203 | 204 | #==========================================================# 205 | 206 | gen_circle_4 <- function(scale_info) { 207 | rbind( 208 | gen_circle(1L, -.7, -1/2, .75, 1.5, pi/4), 209 | gen_circle(2L, -.72+2/3, -1/6, .75, 1.5, pi/4), 210 | gen_circle(3L, .72-2/3, -1/6, .75, 1.5, -pi/4), 211 | gen_circle(4L, .7, -1/2, .75, 1.5, -pi/4) 212 | ) 213 | } 214 | 215 | gen_text_pos_4 <- function(scale_info, min_overlap_for_text = 0.2) { 216 | tribble( 217 | ~name, ~x, ~y, ~hjust, ~vjust, 218 | "A", -1.5, 0, 0.5, 0.5, 219 | "B", -0.6, 0.7, 0.5, 0.5, 220 | "C", 0.6, 0.7, 0.5, 0.5, 221 | "D", 1.5, 0, 0.5, 0.5, 222 | "AB", -0.9, 0.3, 0.5, 0.5, 223 | "BC", 0, 0.4, 0.5, 0.5, 224 | "CD", 0.9, 0.3, 0.5, 0.5, 225 | "AC", -0.8, -0.9, 0.5, 0.5, 226 | "BD", 0.8, -0.9, 0.5, 0.5, 227 | "AD", 0, -1.4, 0.5, 0.5, 228 | "ABC", -0.5, -0.2, 0.5, 0.5, 229 | "BCD", 0.5, -0.2, 0.5, 0.5, 230 | "ACD", -0.3, -1.1, 0.5, 0.5, 231 | "ABD", 0.3, -1.1, 0.5, 0.5, 232 | "ABCD", 0, -0.7, 0.5, 0.5, 233 | "-", 0, -1.9, 0.5, 0.5 234 | ) 235 | } 236 | 237 | gen_label_pos_4 <- function(scale_info) { 238 | tribble( 239 | ~name, ~x, ~y, ~hjust, ~vjust, 240 | "A", -1.5, -1.3, 1, 1, 241 | "B", -0.8, 1.2, 0.5, 0, 242 | "C", 0.8, 1.2, 0.5, 0, 243 | "D", 1.5, -1.3, 0, 1 244 | ) 245 | } 246 | 247 | #==========================================================# 248 | 249 | try_plot <- function( 250 | n, 251 | center_radius = 1, 252 | ellipse_a = 1.5, 253 | ellipse_b = 1, 254 | start_angle = pi / 2, 255 | rotation_offset = -pi / 6 256 | ) { 257 | d <- gen_circle_list( 258 | n, center_radius, ellipse_a, ellipse_b, 259 | start_angle, rotation_offset 260 | ) 261 | d$group <- LETTERS[d$group] 262 | g <- ggplot(d) + 263 | geom_polygon( 264 | aes(x = x, y = y, group = group, fill = group), 265 | alpha = 0.5 266 | ) + 267 | scale_fill_manual( 268 | values = default_color_list[(seq_len(n) %% length(default_color_list)) + 1] 269 | ) + 270 | geom_polygon( 271 | aes(x = x, y = y, group = group), 272 | color = "black", 273 | linewidth = 0.5, 274 | alpha = 0 275 | ) + 276 | coord_fixed() + 277 | theme_void() 278 | print(g) 279 | d 280 | } 281 | 282 | #try_plot(2, ellipse_b = 1.5, start_angle = pi) 283 | #try_plot(2, ellipse_b = 1.5) 284 | # 285 | #try_plot(3, ellipse_b = 1.5, start_angle = pi / 2) 286 | #try_plot(3, ellipse_b = 1.5, start_angle = pi * 2 / 3) 287 | #try_plot(3, ellipse_b = 1.5, start_angle = pi * 5 / 6) 288 | #try_plot(3, ellipse_b = 1.5, start_angle = pi) 289 | # 290 | #try_plot(4, ellipse_b = 1.5, rotation_offset = 0) 291 | #try_plot(4, ellipse_b = 1.5, start_angle = pi * 3 / 4) 292 | #try_plot(4, ellipse_b = 1) 293 | # 294 | #try_plot(5, ellipse_b = 1.5, rotation_offset = 0) 295 | #try_plot(5, ellipse_b = 1) 296 | # 297 | #try_plot(6, ellipse_b = 1.5, rotation_offset = 0) 298 | #try_plot(6, ellipse_b = 1) 299 | # 300 | #try_plot(7, ellipse_b = 1.5, rotation_offset = 0) 301 | #try_plot(7, ellipse_b = 1) 302 | # 303 | #try_plot(8, ellipse_b = 1.5, rotation_offset = 0) 304 | #try_plot(8, ellipse_b = 1, rotation_offset = -pi / 6) 305 | 306 | #==========================================================# 307 | 308 | gen_circle_5 <- function(scale_info) { 309 | gen_circle_list(5, 1, 3.1, 1.5, pi * 0.45, pi * 0.1) 310 | } 311 | 312 | gen_text_pos_5 <- function(scale_info, min_overlap_for_text = 0.2) { 313 | rbind( 314 | data.frame( 315 | name = "ABCDE", x = 0, y = 0, hjust = 0.5, vjust = 0.5 316 | ), 317 | gen_label_pos_list( 318 | c("BCDE", "ACDE", "ABDE", "ABCE", "ABCD"), 319 | radius = 1.42, start_angle = pi * 1.12 320 | ), 321 | gen_label_pos_list( 322 | c("CDE", "ADE", "ABE", "ABC", "BCD"), 323 | radius = 1.55, start_angle = pi * 1.33 324 | ), 325 | gen_label_pos_list( 326 | c("BCE", "ACD", "BDE", "ACE", "ABD"), 327 | radius = 1.88, start_angle = pi * 1.13 328 | ), 329 | gen_label_pos_list( 330 | c("AC", "BD", "CE", "AD", "BE"), 331 | radius = 1.98, start_angle = pi * 0.44 332 | ), 333 | gen_label_pos_list( 334 | c("AB", "BC", "CD", "DE", "AE"), 335 | radius = 2.05, start_angle = pi * 0.68 336 | ), 337 | gen_label_pos_list( 338 | c("A", "B", "C", "D", "E"), 339 | radius = 3, start_angle = pi * 0.52 340 | ), 341 | data.frame( 342 | name = "-", x = 0, y = -4, hjust = 0.5, vjust = 0.5 343 | ) 344 | ) 345 | } 346 | 347 | gen_label_pos_5 <- function(scale_info) { 348 | gen_label_pos_list(LETTERS[seq_len(5)], radius = 4.5, start_angle = pi / 2) 349 | } 350 | 351 | #==========================================================# 352 | 353 | gen_circle_6 <- function(scale_info) { 354 | gen_circle_list(6) 355 | } 356 | 357 | gen_text_pos_6 <- function(scale_info, min_overlap_for_text = 0.2) { 358 | tribble( 359 | ~name, ~x, ~y, ~hjust, ~vjust, 360 | "A", -0.8, 0.62, 0.5, 0.5, 361 | "B", 0.8, 0.62, 0.5, 0.5, 362 | "C", 0, -0.62, 0.5, 0.5, 363 | "D", 0.8, -0.62, 0.5, 0.5, 364 | "E", -0.8, -0.62, 0.5, 0.5, 365 | "AB", 0, 0.8, 0.5, 0.5, 366 | "AC", -0.5, 0, 0.5, 0.5, 367 | "AD", 0.5, 0, 0.5, 0.5, 368 | "AE", -0.5, 0, 0.5, 0.5, 369 | "BC", 0, 0.4, 0.5, 0.5, 370 | "BD", 0.5, 0.4, 0.5, 0.5, 371 | "BE", -0.5, 0.4, 0.5, 0.5, 372 | "CD", 0.5, -0.4, 0.5, 0.5, 373 | "CE", -0.5, -0.4, 0.5, 0.5, 374 | "DE", 0, -0.4, 0.5, 0.5, 375 | "ABC", -0.5, -0.2, 0.5, 0.5, 376 | "ABD", 0.5, -0.2, 0.5, 0.5, 377 | "ABE", -0.5, -0.2, 0.5, 0.5, 378 | "ACD", 0.5, -0.2, 0.5, 0.5, 379 | "ACE", -0.5, -0.2, 0.5, 0.5, 380 | "ADE", 0.5, -0.2, 0.5, 0.5, 381 | "BCD", 0.5, -0.2, 0.5, 0.5, 382 | "BCE", -0.5, -0.2, 0.5, 0.5, 383 | "BDE", 0.5, -0.2, 0.5, 0.5, 384 | "CDE", -0.5, -0.2, 0.5, 0.5, 385 | "ABCDE", 0, -0.2, 0.5, 0.5, 386 | "-", 0, -1.9, 0.5, 0.5 387 | ) 388 | } 389 | 390 | gen_label_pos_6 <- function(scale_info) { 391 | gen_label_pos_list(LETTERS[seq_len(6)]) 392 | } 393 | 394 | #==========================================================# 395 | 396 | gen_circle_7 <- function(scale_info) { 397 | gen_circle_list(7) 398 | } 399 | 400 | gen_text_pos_7 <- function(scale_info, min_overlap_for_text = 0.2) { 401 | tribble( 402 | ~name, ~x, ~y, ~hjust, ~vjust, 403 | "A", -0.8, 0.62, 0.5, 0.5, 404 | "B", 0.8, 0.62, 0.5, 0.5, 405 | "C", 0, -0.62, 0.5, 0.5, 406 | "D", 0.8, -0.62, 0.5, 0.5, 407 | "E", -0.8, -0.62, 0.5, 0.5, 408 | "AB", 0, 0.8, 0.5, 0.5, 409 | "AC", -0.5, 0, 0.5, 0.5, 410 | "AD", 0.5, 0, 0.5, 0.5, 411 | "AE", -0.5, 0, 0.5, 0.5, 412 | "BC", 0, 0.4, 0.5, 0.5, 413 | "BD", 0.5, 0.4, 0.5, 0.5, 414 | "BE", -0.5, 0.4, 0.5, 0.5, 415 | "CD", 0.5, -0.4, 0.5, 0.5, 416 | "CE", -0.5, -0.4, 0.5, 0.5, 417 | "DE", 0, -0.4, 0.5, 0.5, 418 | "ABC", -0.5, -0.2, 0.5, 0.5, 419 | "ABD", 0.5, -0.2, 0.5, 0.5, 420 | "ABE", -0.5, -0.2, 0.5, 0.5, 421 | "ACD", 0.5, -0.2, 0.5, 0.5, 422 | "ACE", -0.5, -0.2, 0.5, 0.5, 423 | "ADE", 0.5, -0.2, 0.5, 0.5, 424 | "BCD", 0.5, -0.2, 0.5, 0.5, 425 | "BCE", -0.5, -0.2, 0.5, 0.5, 426 | "BDE", 0.5, -0.2, 0.5, 0.5, 427 | "CDE", -0.5, -0.2, 0.5, 0.5, 428 | "ABCDE", 0, -0.2, 0.5, 0.5, 429 | "-", 0, -1.9, 0.5, 0.5 430 | ) 431 | } 432 | 433 | gen_label_pos_7 <- function(scale_info) { 434 | gen_label_pos_list(LETTERS[seq_len(7)]) 435 | } 436 | 437 | #==========================================================# 438 | 439 | gen_circle_8 <- function(scale_info) { 440 | gen_circle_list(8) 441 | } 442 | 443 | gen_text_pos_8 <- function(scale_info, min_overlap_for_text = 0.2) { 444 | tribble( 445 | ~name, ~x, ~y, ~hjust, ~vjust, 446 | "A", -0.8, 0.62, 0.5, 0.5, 447 | "B", 0.8, 0.62, 0.5, 0.5, 448 | "C", 0, -0.62, 0.5, 0.5, 449 | "D", 0.8, -0.62, 0.5, 0.5, 450 | "E", -0.8, -0.62, 0.5, 0.5, 451 | "AB", 0, 0.8, 0.5, 0.5, 452 | "AC", -0.5, 0, 0.5, 0.5, 453 | "AD", 0.5, 0, 0.5, 0.5, 454 | "AE", -0.5, 0, 0.5, 0.5, 455 | "BC", 0, 0.4, 0.5, 0.5, 456 | "BD", 0.5, 0.4, 0.5, 0.5, 457 | "BE", -0.5, 0.4, 0.5, 0.5, 458 | "CD", 0.5, -0.4, 0.5, 0.5, 459 | "CE", -0.5, -0.4, 0.5, 0.5, 460 | "DE", 0, -0.4, 0.5, 0.5, 461 | "ABC", -0.5, -0.2, 0.5, 0.5, 462 | "ABD", 0.5, -0.2, 0.5, 0.5, 463 | "ABE", -0.5, -0.2, 0.5, 0.5, 464 | "ACD", 0.5, -0.2, 0.5, 0.5, 465 | "ACE", -0.5, -0.2, 0.5, 0.5, 466 | "ADE", 0.5, -0.2, 0.5, 0.5, 467 | "BCD", 0.5, -0.2, 0.5, 0.5, 468 | "BCE", -0.5, -0.2, 0.5, 0.5, 469 | "BDE", 0.5, -0.2, 0.5, 0.5, 470 | "CDE", -0.5, -0.2, 0.5, 0.5, 471 | "ABCDE", 0, -0.2, 0.5, 0.5, 472 | "-", 0, -1.9, 0.5, 0.5 473 | ) 474 | } 475 | 476 | gen_label_pos_8 <- function(scale_info) { 477 | gen_label_pos_list(LETTERS[seq_len(8)]) 478 | } 479 | 480 | #==========================================================# 481 | --------------------------------------------------------------------------------