├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ └── pkgdown.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── R ├── cohort_count.R ├── cohort_define.R ├── cohort_label.R ├── cohort_pull.R ├── cohort_select.R ├── cohort_start.R ├── consort_arrow_add.R ├── consort_box_add.R ├── consort_line_add.R ├── consort_start.R ├── create_consort_data.R ├── data.R ├── geom_consort.R ├── geom_consort_arrow.R ├── geom_consort_box.R ├── geom_consort_line.R ├── ggplot-consort.R ├── imports.R ├── utils-cohort.R ├── utils-pipe.R └── utils.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── data └── trial_data.rda ├── dev ├── ggconsort-hex.R ├── ggconsort-hex.png ├── ggconsort-hex.svg ├── ggconsort.fig └── sim-data.R ├── ggconsort.Rproj ├── man ├── cohort_count.Rd ├── cohort_define.Rd ├── cohort_label.Rd ├── cohort_pull.Rd ├── cohort_select.Rd ├── cohort_start.Rd ├── consort_start.Rd ├── figures │ ├── README-example-consort-1.png │ └── logo.png ├── pipe.Rd └── trial_data.Rd └── pkgdown ├── extra.css └── favicon ├── apple-touch-icon-120x120.png ├── apple-touch-icon-152x152.png ├── apple-touch-icon-180x180.png ├── apple-touch-icon-60x60.png ├── apple-touch-icon-76x76.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png └── favicon.ico /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | ^dev/ggconsort-hex\.R$ 6 | ^dev/ggconsort-hex\.png$ 7 | ^dev/ggconsort-hex\.svg$ 8 | ^dev/ggconsort\.fig$ 9 | ^dev/sim-data\.R$ 10 | ^_pkgdown\.yml$ 11 | ^docs$ 12 | ^pkgdown$ 13 | ^\.github$ 14 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/master/examples 2 | # 3 | # For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. 4 | # https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions 5 | on: 6 | push: 7 | branches: [main, master] 8 | tags: 9 | -'*' 10 | 11 | name: pkgdown 12 | 13 | jobs: 14 | pkgdown: 15 | runs-on: ubuntu-18.04 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: r-lib/actions/setup-pandoc@v1 21 | - uses: r-lib/actions/setup-r@v1 22 | with: 23 | use-public-rspm: true 24 | - uses: r-lib/actions/setup-r-dependencies@v1 25 | with: 26 | extra-packages: pkgdown 27 | needs: website 28 | 29 | - name: Deploy package 30 | run: | 31 | git config --local user.name "$GITHUB_ACTOR" 32 | git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" 33 | Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .Rdata 6 | .httr-oauth 7 | .DS_Store 8 | docs 9 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: ggconsort 2 | Type: Package 3 | Title: Create CONSORT Diagrams 4 | Version: 0.1.0 5 | Authors@R: 6 | c(person(given = "Travis", 7 | family = "Gerke", 8 | role = c("aut", "cre"), 9 | email = "tgerke@mail.harvard.edu", 10 | comment = c(ORCID = "0000-0002-9500-8907")), 11 | person(given = "Garrick", 12 | family = "Aden-Buie", 13 | role = "aut", 14 | email = "garrick@adenbuie.com", 15 | comment = c(ORCID = "0000-0002-7111-0077"))) 16 | Maintainer: Travis Gerke 17 | Description: This package provides convenience functions for constructing CONSORT diagrams with ggplot2. 18 | License: MIT + file LICENSE 19 | Encoding: UTF-8 20 | LazyData: true 21 | Imports: 22 | dplyr, 23 | ggplot2, 24 | ggtext, 25 | glue, 26 | magrittr, 27 | purrr, 28 | rlang, 29 | tidyselect 30 | RoxygenNote: 7.1.1 31 | Suggests: 32 | palmerpenguins 33 | Depends: 34 | R (>= 2.10) 35 | URL: https://tgerke.github.io/ggconsort 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2021 2 | COPYRIGHT HOLDER: Travis Gerke, Garrick Aden-Buie 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2021 Travis Gerke, Garrick Aden-Buie 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 | S3method(ggplot,ggconsort) 4 | S3method(ggplot,ggconsort_cohort) 5 | S3method(plot,ggconsort) 6 | S3method(print,ggconsort) 7 | S3method(print,ggconsort_cohort) 8 | S3method(summary,ggconsort_cohort) 9 | export("%>%") 10 | export(cohort_count) 11 | export(cohort_count_adorn) 12 | export(cohort_count_int) 13 | export(cohort_define) 14 | export(cohort_label) 15 | export(cohort_pull) 16 | export(cohort_select) 17 | export(cohort_start) 18 | export(consort_arrow_add) 19 | export(consort_box_add) 20 | export(consort_line_add) 21 | export(create_consort_data) 22 | export(geom_consort) 23 | export(geom_consort_arrow) 24 | export(geom_consort_box) 25 | export(geom_consort_line) 26 | export(theme_consort) 27 | importFrom(ggplot2,ggplot) 28 | importFrom(magrittr,"%>%") 29 | importFrom(rlang,"%||%") 30 | importFrom(rlang,.data) 31 | -------------------------------------------------------------------------------- /R/cohort_count.R: -------------------------------------------------------------------------------- 1 | #' Count the number of rows in each ggconsort cohort 2 | #' 3 | #' @param .data A \code{ggconsort_cohort} object 4 | #' @param ... Cohorts to include in the output, can be quoted or unquoted 5 | #' cohort names, or \pkg{tidyselect} helpers such as 6 | #' [tidyselect::starts_with()]. 7 | #' 8 | #' @return A \code{tibble} with cohort name, row number total, and label. 9 | #' 10 | #' @describeIn cohort_count Returns a tibble with cohort name, row number total 11 | #' and label. 12 | #' @export 13 | #' 14 | 15 | cohort_count <- function(.data, ...) { 16 | assert_cohort(.data) 17 | 18 | cohort <- tidyselect::eval_select( 19 | rlang::expr(c(...)), 20 | rlang::set_names(names(.data$data)) 21 | ) 22 | 23 | labels <- if (length(.data$labels)) { 24 | list( 25 | cohort = names(.data$labels), 26 | label = unname(unlist(.data$labels)) 27 | ) %>% 28 | dplyr::as_tibble() 29 | } 30 | 31 | counts <- 32 | lapply(.data$data, function(x) dplyr::tibble(count = nrow(x))) %>% 33 | dplyr::bind_rows(.id = "cohort") 34 | 35 | if (rlang::has_length(cohort)) { 36 | counts <- counts %>% 37 | dplyr::slice(!!cohort) 38 | } 39 | 40 | if (is.null(labels)) { 41 | return(counts) 42 | } 43 | 44 | dplyr::left_join(counts, labels, by = "cohort") 45 | } 46 | 47 | #' @describeIn cohort_count Returns a named vector with cohort counts. 48 | #' 49 | #' @export 50 | cohort_count_int <- function(.data, ...) { 51 | counts <- cohort_count(.data, ...) 52 | 53 | rlang::set_names(counts$count, counts$cohort) 54 | } 55 | 56 | default_label_count <- function(...) { 57 | glue::glue("{label} (n = {count})", ..., .envir = parent.frame()) 58 | } 59 | 60 | #' @describeIn cohort_count Returns a cohort count in "(n = )" or 61 | #' other custom format 62 | #' 63 | #' @param .label_fn An optional custom function for formatting cohort counts 64 | #' 65 | #' @export 66 | #' @examples 67 | #' cohorts <- trial_data %>% 68 | #' cohort_start("Assessed for eligibility") %>% 69 | #' cohort_define( 70 | #' consented = .full %>% dplyr::filter(declined != 1), 71 | #' consented_chemonaive = consented %>% dplyr::filter(prior_chemo != 1) 72 | #' ) %>% 73 | #' cohort_label( 74 | #' consented = "Consented", 75 | #' consented_chemonaive = "Chemotherapy naive" 76 | #' ) 77 | #' 78 | #' cohorts %>% 79 | #' cohort_count() 80 | #' 81 | #' cohorts %>% 82 | #' cohort_count_adorn() 83 | #' 84 | #' cohorts %>% 85 | #' cohort_count_adorn( 86 | #' starts_with("consented"), 87 | #' .label_fn = function(cohort, label, count, ...) { 88 | #' glue::glue("{count} {label} ({cohort})") 89 | #' } 90 | #' ) 91 | cohort_count_adorn <- function(.data, ..., .label_fn = NULL) { 92 | counts <- cohort_count(.data, ...) 93 | counts$label <- counts$label %||% "" 94 | # reorder so that `.x` is label and `.y` is count 95 | counts <- counts %>% 96 | dplyr::select(.data$label, .data$count, dplyr::everything()) 97 | 98 | .label_fn <- .label_fn %||% default_label_count 99 | purrr::pmap_chr(counts, .label_fn) 100 | } 101 | -------------------------------------------------------------------------------- /R/cohort_define.R: -------------------------------------------------------------------------------- 1 | #' Define ggconsort cohorts 2 | #' 3 | #' Following a call to \code{cohort_start}, use \code{cohort_define} 4 | #' to construct cohorts from the full source data which are appended 5 | #' to the \code{ggconsort_cohort} object. 6 | #' 7 | #' @param .data A \code{ggconsort_cohort} object 8 | #' @param ... A series of named expressions which define the cohorts 9 | #' 10 | #' @return The modified \code{ggconsort_cohort} object which now includes 11 | #' additional \code{$data} items according to provided cohort definitions 12 | #' @export 13 | #' 14 | ### FIXME: to add @examples 15 | cohort_define <- function(.data, ...) { 16 | assert_cohort(.data) 17 | 18 | exprs <- rlang::enexprs(...) 19 | assert_named(exprs, "...") 20 | 21 | for (cohort_name in names(exprs)) { 22 | cohort <- rlang::eval_tidy(exprs[[cohort_name]], data = .data$data) 23 | 24 | .data$data[[cohort_name]] <- cohort %>% dplyr::ungroup() 25 | 26 | if (dplyr::is.grouped_df(cohort)) { 27 | # if the cohort is grouped, we add new cohorts for each group level, 28 | # separated by `.` and prefixed with the `cohort_name` 29 | groups <- cohort %>% dplyr::group_keys() 30 | groups$.key <- apply(groups, 1, function(x) paste(to_snake_case(x), collapse = ".")) 31 | groups$.key <- paste0(groups$cohort_name, ".", groups$.key) 32 | 33 | cohort <- 34 | cohort %>% 35 | dplyr::left_join(groups, by = names(groups)[-length(groups)]) %>% 36 | dplyr::group_by(.data$.key) %>% 37 | dplyr::group_nest() 38 | 39 | for (i in seq_len(nrow(cohort))) { 40 | .data$data[[cohort$.key[[i]]]] <- cohort$data[[i]] 41 | } 42 | } 43 | } 44 | 45 | .data 46 | } 47 | -------------------------------------------------------------------------------- /R/cohort_label.R: -------------------------------------------------------------------------------- 1 | #' Add labels to ggconsort cohorts 2 | #' 3 | #' @param .data A \code{ggconsort_cohort} object 4 | #' @param ... A series of named expressions which provide labels 5 | #' corresponding to named cohorts in the \code{ggconsort_cohort} object 6 | #' 7 | #' @return The modified \code{ggconsort_cohort} object which now includes 8 | #' additional \code{$labels} items according to provided label definitions 9 | #' @export 10 | #' 11 | ### FIXME: to add @examples 12 | cohort_label <- function(.data, ...) { 13 | assert_cohort(.data) 14 | 15 | labels <- list(...) 16 | assert_named(labels, "...") 17 | # FIXME: check that all labels are length-1 strings, too 18 | 19 | # check that label names match cohort names 20 | unexpected <- setdiff(names(labels), names(.data$data)) 21 | if (length(unexpected)) { 22 | msg <- sprintf( 23 | "Unknown cohort %s: %s", 24 | ngettext(length(unexpected), "name", "names"), 25 | d_quote(unexpected, collapse = ", ") 26 | ) 27 | stop(msg) 28 | } 29 | 30 | .data$labels <- modify_list(.data$labels, labels) 31 | 32 | .data 33 | } 34 | -------------------------------------------------------------------------------- /R/cohort_pull.R: -------------------------------------------------------------------------------- 1 | #' Extract a cohort data frame 2 | #' 3 | #' Pull a single tibble from a \code{ggconsort_cohort} object, 4 | #' often for downstream analysis or deeper inspection. 5 | #' 6 | #' @param .data A \code{ggconsort_cohort} object 7 | #' 8 | #' @param ... The cohort to pull as an unquoted expression 9 | #' 10 | #' @return A tibble for a single cohort 11 | #' 12 | #' @export 13 | #' @examples 14 | #' cohorts <- trial_data %>% 15 | #' cohort_start("Assessed for eligibility") %>% 16 | #' cohort_define( 17 | #' consented = .full %>% dplyr::filter(declined != 1), 18 | #' consented_chemonaive = consented %>% dplyr::filter(prior_chemo != 1) 19 | #' ) 20 | #' 21 | #' cohorts %>% cohort_pull(consented_chemonaive) 22 | 23 | cohort_pull <- function(.data, ...) { 24 | cohort <- tidyselect::eval_select( 25 | rlang::expr(c(...)), 26 | rlang::set_names(names(.data$data)) 27 | ) 28 | 29 | if (rlang::has_length(cohort, n = 1)) { 30 | return(.data$data[[cohort]]) 31 | } 32 | 33 | .data$data[cohort] 34 | } 35 | -------------------------------------------------------------------------------- /R/cohort_select.R: -------------------------------------------------------------------------------- 1 | #' Subset ggconsort cohort objects 2 | #' 3 | #' Select a subset of cohorts from a \code{ggconsort_cohort} 4 | #' object 5 | #' 6 | #' @param .data A \code{ggconsort_cohort} object 7 | #' 8 | #' @param ... The cohort(s) to pull as an unquoted or 9 | #' \code{tidyselect} style expression 10 | #' 11 | #' @return A \code{ggconsort_cohort} object 12 | #' 13 | #' @export 14 | #' @examples 15 | #' cohorts <- 16 | #' trial_data %>% 17 | #' cohort_start("Assessed for eligibility") %>% 18 | #' cohort_define( 19 | #' consented = .full %>% dplyr::filter(declined != 1), 20 | #' treatment_a = consented %>% dplyr::filter(treatment == "Drug A"), 21 | #' treatment_b = consented %>% dplyr::filter(treatment == "Drug B") 22 | #' ) %>% 23 | #' cohort_label( 24 | #' consented = "Consented", 25 | #' treatment_a = "Allocated to arm A", 26 | #' treatment_b = "Allocated to arm B" 27 | #' ) 28 | #' 29 | #' cohorts %>% cohort_select(consented) 30 | #' 31 | #' cohorts %>% 32 | #' cohort_select(starts_with("treatment_")) 33 | #' 34 | cohort_select <- function(.data, ...) { 35 | cohort <- tidyselect::eval_select( 36 | rlang::expr(c(".full", ...)), 37 | rlang::set_names(names(.data$data)) 38 | ) 39 | 40 | if (!rlang::has_length(cohort)) { 41 | return(.data) 42 | } 43 | 44 | .data$data <- .data$data[cohort] 45 | labelled_cohorts <- intersect(names(cohort), names(.data$labels)) 46 | .data$labels <- .data$labels[labelled_cohorts] 47 | .data 48 | } 49 | -------------------------------------------------------------------------------- /R/cohort_start.R: -------------------------------------------------------------------------------- 1 | #' Initialize a new ggconsort cohort 2 | #' 3 | #' Creates an object with an optional label which stores 4 | #' the originating source data for downstream ggconsort cohorts 5 | #' 6 | #' @param .data A data frame or tibble 7 | #' @param label A character string to describe the set of cohorts 8 | #' 9 | #' @return Returns a \code{ggconsort_cohort} object with 10 | #' \code{$data} and \code{$labels} items 11 | #' @export 12 | #' 13 | ### FIXME: to add @examples 14 | cohort_start <- function(.data, label = NULL) { 15 | stopifnot(is.data.frame(.data)) 16 | x <- structure( 17 | list( 18 | data = list(.full = .data), 19 | labels = if (!is.null(label)) list(.full = label) else list() 20 | ), 21 | class = "ggconsort_cohort" 22 | ) 23 | x 24 | } 25 | 26 | #' @export 27 | print.ggconsort_cohort <- function(x, ...) { 28 | counts <- cohort_count(x) 29 | count_full <- dplyr::filter(counts, .data$cohort == ".full")$count 30 | n_cohorts <- nrow(counts) - 1 31 | 32 | desc_obs <- ngettext( 33 | count_full, 34 | "A ggconsort cohort of %d observation", 35 | "A ggconsort cohort of %d observations" 36 | ) 37 | desc_cohorts <- ngettext(n_cohorts, "with %d cohort", "with %d cohorts") 38 | description <- sprintf(paste(desc_obs, desc_cohorts), count_full, n_cohorts) 39 | description <- paste0(description, if (n_cohorts == 0) ".\n" else ":") 40 | 41 | cat(description) 42 | if (n_cohorts < 1) { 43 | return() 44 | } 45 | 46 | for (i in seq_len(min(n_cohorts, 8)) + 1) { 47 | # first cohort is the ".full" cohort 48 | cat("\n - ", counts$cohort[[i]], " (", counts$count[[i]], ")", sep = "") 49 | } 50 | 51 | if (n_cohorts > 8) { 52 | cat("\n ...and", n_cohorts - 8, "more.") 53 | } 54 | 55 | invisible(x) 56 | } 57 | 58 | #' @export 59 | summary.ggconsort_cohort <- function(object, ...) { 60 | cohort_count(object) 61 | } 62 | -------------------------------------------------------------------------------- /R/consort_arrow_add.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | consort_arrow_add <- function( 4 | .data, start = NA, start_side = NA, end = NA, end_side = NA, 5 | start_x = NA, start_y = NA, end_x = NA, end_y = NA 6 | ) { 7 | .data <- consort_start(.data) 8 | 9 | .data$consort <- dplyr::bind_rows( 10 | .data$consort, 11 | dplyr::tibble( 12 | name = NA, box_x = NA, box_y = NA, label = NA, 13 | type = "arrow", 14 | start = start, start_side = start_side, end = end, end_side = end_side, 15 | start_x = start_x, start_y = start_y, end_x = end_x, end_y = end_y 16 | ) 17 | ) 18 | 19 | .data 20 | } 21 | -------------------------------------------------------------------------------- /R/consort_box_add.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | consort_box_add <- function(.data, name, x, y, label) { 4 | .data <- consort_start(.data) 5 | 6 | .data$consort <- dplyr::bind_rows( 7 | .data$consort, 8 | dplyr::tibble( 9 | name = name, box_x = x, box_y = y, label = label, 10 | type = "box", 11 | start = NA, start_side = NA, end = NA, end_side = NA, 12 | start_x = NA, start_y = NA, end_x = NA, end_y = NA 13 | ) 14 | ) 15 | 16 | .data 17 | } 18 | -------------------------------------------------------------------------------- /R/consort_line_add.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | consort_line_add <- function( 4 | .data, start = NA, start_side = NA, end = NA, end_side = NA, 5 | start_x = NA, start_y = NA, end_x = NA, end_y = NA 6 | ) { 7 | .data <- consort_start(.data) 8 | 9 | .data$consort <- dplyr::bind_rows( 10 | .data$consort, 11 | dplyr::tibble( 12 | name = NA, box_x = NA, box_y = NA, label = NA, 13 | type = "line", 14 | start = start, start_side = start_side, end = end, end_side = end_side, 15 | start_x = start_x, start_y = start_y, end_x = end_x, end_y = end_y 16 | ) 17 | ) 18 | 19 | .data 20 | } 21 | -------------------------------------------------------------------------------- /R/consort_start.R: -------------------------------------------------------------------------------- 1 | #' Initialize a ggconsort object for plotting. Used internally. 2 | #' @param .data A \code{ggconsort_cohort} object 3 | #' @keywords Internal 4 | 5 | consort_start <- function(.data) { 6 | if (inherits(.data, "ggconsort")) { 7 | return(.data) 8 | } 9 | 10 | assert_cohort(.data) 11 | 12 | .data[['consort']] <- list() 13 | class(.data) <- c("ggconsort", class(.data)) 14 | 15 | .data 16 | } 17 | -------------------------------------------------------------------------------- /R/create_consort_data.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | # combines tibbles like consort_boxes and consort_arrows into a 4 | # usable form for ggconsort geoms 5 | # consort_boxes and consort_arrows are expected to have certain formats 6 | # FIXME: to describe and check for those formats 7 | 8 | create_consort_data <- function(.data, ...) { 9 | # FIXME: account for 2+ arrows coming in by summarizing those rows 10 | # FIXME: need to assert that we've got a cohort with consort data 11 | 12 | consort_boxes <- .data$consort %>% 13 | dplyr::filter(type == "box") %>% 14 | dplyr::select(name, box_x, box_y, label) 15 | 16 | consort_arrows <- .data$consort %>% 17 | dplyr::filter(type == "arrow") %>% 18 | dplyr::select( 19 | start, start_side, end, end_side, 20 | start_x, start_y, end_x, end_y 21 | ) 22 | # if no arrows are set up, allow the joins so the box prints 23 | if (nrow(consort_arrows) == 0) { 24 | consort_arrows <- bind_rows( 25 | consort_arrows, 26 | tibble( 27 | start = NA, start_side = NA, end = NA, end_side = NA, 28 | start_x = NA, start_y = NA, end_x = NA, end_y = NA 29 | ) 30 | ) 31 | } 32 | 33 | consort_lines <- .data$consort %>% 34 | dplyr::filter(type == "line") %>% 35 | dplyr::select( 36 | start, start_side, end, end_side, 37 | start_x, start_y, end_x, end_y 38 | ) 39 | # if no lines are set up, allow the joins so the box prints 40 | if (nrow(consort_lines) == 0) { 41 | consort_lines <- bind_rows( 42 | consort_lines, 43 | tibble( 44 | start = NA, start_side = NA, end = NA, end_side = NA, 45 | start_x = NA, start_y = NA, end_x = NA, end_y = NA 46 | ) 47 | ) 48 | } 49 | 50 | boxes <- dplyr::left_join( 51 | consort_boxes, 52 | consort_arrows %>% dplyr::select(end, end_side), 53 | by = c("name" = "end") 54 | ) %>% 55 | dplyr::mutate( 56 | vjust = dplyr::if_else( 57 | end_side == "top", 1, .5, missing = .5 58 | ), 59 | hjust = dplyr::case_when( 60 | end_side == "left" ~ 0, 61 | end_side == "right" ~ 1, 62 | TRUE ~ .5 63 | ), 64 | type = "box" 65 | ) %>% 66 | dplyr::rename( 67 | arrow_in = end_side 68 | ) 69 | 70 | arrows <- dplyr::left_join( 71 | consort_arrows, 72 | consort_boxes %>% 73 | dplyr::select(-label) %>% 74 | dplyr::rename(x = box_x, y = box_y), 75 | by = c("start" = "name") 76 | ) %>% 77 | dplyr::left_join( 78 | consort_boxes %>% 79 | dplyr::select(-label) %>% 80 | dplyr::rename(xend = box_x, yend = box_y), 81 | by = c("end" = "name") 82 | ) %>% 83 | dplyr::mutate( 84 | x = dplyr::if_else(!is.na(start_x), as.numeric(start_x), x), 85 | y = dplyr::if_else(!is.na(start_y), as.numeric(start_y), y), 86 | xend = dplyr::if_else(!is.na(end_x), as.numeric(end_x), xend), 87 | yend = dplyr::if_else(!is.na(end_y), as.numeric(end_y), yend), 88 | type = "arrow" 89 | ) %>% 90 | dplyr::select(-dplyr::starts_with("start_"), -dplyr::starts_with("end_")) 91 | 92 | lines <- dplyr::left_join( 93 | consort_lines, 94 | consort_boxes %>% 95 | dplyr::select(-label) %>% 96 | dplyr::rename(x = box_x, y = box_y), 97 | by = c("start" = "name") 98 | ) %>% 99 | dplyr::left_join( 100 | consort_boxes %>% 101 | dplyr::select(-label) %>% 102 | dplyr::rename(xend = box_x, yend = box_y), 103 | by = c("end" = "name") 104 | ) %>% 105 | dplyr::mutate( 106 | x = dplyr::if_else(is.na(start_x), x, as.numeric(start_x)), 107 | y = dplyr::if_else(is.na(start_y), y, as.numeric(start_y)), 108 | xend = dplyr::if_else(is.na(end_x), xend, as.numeric(end_x)), 109 | yend = dplyr::if_else(is.na(end_y), yend, as.numeric(end_y)), 110 | type = "line" 111 | ) %>% 112 | dplyr::select(-dplyr::starts_with("start_"), -dplyr::starts_with("end_")) 113 | 114 | dplyr::full_join( 115 | boxes, 116 | bind_rows(arrows, lines), 117 | by = "type" 118 | ) %>% 119 | dplyr::filter( 120 | rowSums(is.na(.)) != (ncol(.) - 1) 121 | ) 122 | } 123 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' Screened and randomized patients 2 | #' 3 | #' A simulated dataset containing 3 trial exclusion variables and 4 | #' a treatment allocation variable. 5 | #' 6 | #' @format A tibble with 1200 rows and 5 variables: 7 | #' \describe{ 8 | #' \item{id}{patient ID} 9 | #' \item{declined}{indicator for declining to participate} 10 | #' \item{prior_chemo}{indicator for prior chemotherapy, a trial exclusion} 11 | #' \item{bone_mets}{indicator for bone metastases, a trial exclusion} 12 | #' \item{treatment}{treatment assignment} 13 | #' } 14 | #' @source dev/sim-data.R 15 | "trial_data" 16 | -------------------------------------------------------------------------------- /R/geom_consort.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | geom_consort <- function(...) { 4 | list( 5 | geom_consort_arrow(...), 6 | geom_consort_box(...) 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /R/geom_consort_arrow.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | # sets some reasonable size and style defaults for edges 4 | 5 | geom_consort_arrow <- function(x, xend, y, yend, data = NULL, ...) { 6 | list( 7 | ggplot2::geom_segment( 8 | ggplot2::aes(x = .data$x, xend = .data$xend, y = .data$y, yend = .data$yend), 9 | data = function(d) dplyr::filter(d, .data$type == "arrow"), 10 | size = 0.15, linejoin = "mitre", lineend = "butt", 11 | arrow = ggplot2::arrow(length = ggplot2::unit(2, "mm"), type = "closed") 12 | ), 13 | ggplot2::geom_segment( 14 | ggplot2::aes(x = .data$x, xend = .data$xend, y = .data$y, yend = .data$yend), 15 | data = function(d) dplyr::filter(d, .data$type == "line"), 16 | size = 0.15 17 | ) 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /R/geom_consort_box.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | # creates a box with squared edges, with label args 4 | # that get passed to geom_richtext 5 | 6 | # note that can take vectors like arrow_in = c("top", "left") 7 | 8 | geom_consort_box <- function( 9 | x, y, label, data = NULL, 10 | label_color = "black", label_size = "8pt", label_height = 1, 11 | ... 12 | ) { 13 | 14 | ggtext::geom_richtext( 15 | ggplot2::aes( 16 | x = .data$box_x, y = .data$box_y, label = .data$label, 17 | lineheight = label_height, 18 | vjust = .data$vjust, hjust = .data$hjust, ... 19 | ), 20 | data = function(d) dplyr::filter(d, .data$type == "box"), 21 | label.r = ggplot2::unit(0, units = "npc") 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /R/geom_consort_line.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | 3 | # Sometimes lines without arrows are required, and 4 | # this function allows us to keep styling consistent 5 | 6 | geom_consort_line <- function(x, xend, y, yend, ...) { 7 | ggplot2::geom_segment( 8 | ggplot2::aes(x = x, xend = xend, y= y, yend = yend), 9 | size = 0.15, linejoin = "mitre", lineend = "butt" 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /R/ggplot-consort.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | ggplot.ggconsort_cohort <- function(data = NULL, ...) { 3 | stop("make a consort") 4 | } 5 | 6 | #' @importFrom ggplot2 ggplot 7 | #' @export 8 | ggplot.ggconsort <- function( 9 | data = NULL, 10 | mapping = ggplot2::aes(), 11 | ..., 12 | environment = parent.frame() 13 | ) { 14 | data %>% 15 | create_consort_data() %>% 16 | ggplot2::ggplot() + 17 | ggplot2::coord_cartesian(clip = "off") 18 | } 19 | 20 | #' @export 21 | theme_consort <- function(margin_h = 0, margin_v = 0, margin_unit = "line") { 22 | ggplot2::theme_void() + 23 | ggplot2::theme( 24 | plot.margin = ggplot2::margin( 25 | margin_v, margin_h, margin_v, margin_h, 26 | unit = margin_unit 27 | ) 28 | ) 29 | } 30 | 31 | #' @export 32 | plot.ggconsort <- function(x, y = NULL, ..., margin_h = 0, margin_v = 0) { 33 | ggplot(x) + 34 | geom_consort() + 35 | theme_consort() 36 | } 37 | 38 | #' @export 39 | print.ggconsort <- function(x, ...) { 40 | print(plot.ggconsort(x, ...)) 41 | } 42 | -------------------------------------------------------------------------------- /R/imports.R: -------------------------------------------------------------------------------- 1 | #' @importFrom rlang %||% .data 2 | NULL 3 | 4 | -------------------------------------------------------------------------------- /R/utils-cohort.R: -------------------------------------------------------------------------------- 1 | is_cohort <- function(x) { 2 | inherits(x, "ggconsort_cohort") 3 | } 4 | 5 | assert_cohort <- function(x, arg_name = NULL) { 6 | arg_name <- arg_name %||% rlang::quo_name(rlang::enquo(x)) 7 | if (!is_cohort(x)) { 8 | stop("`", arg_name, "` must be a ggconsort cohort created with `cohort_start()`.") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /R/utils-pipe.R: -------------------------------------------------------------------------------- 1 | #' Pipe operator 2 | #' 3 | #' See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. 4 | #' 5 | #' @name %>% 6 | #' @rdname pipe 7 | #' @keywords internal 8 | #' @export 9 | #' @importFrom magrittr %>% 10 | #' @usage lhs \%>\% rhs 11 | #' @param lhs A value or the magrittr placeholder. 12 | #' @param rhs A function call using the magrittr semantics. 13 | #' @return The result of calling `rhs(lhs)`. 14 | NULL 15 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | modify_list <- function(old, new) { 2 | for (n in names(new)) { 3 | old[[n]] <- new[[n]] 4 | } 5 | old 6 | } 7 | 8 | d_quote <- function(x, collapse = NULL, sep = " ") { 9 | # backwards compatible dQuote() for R < 3.6 10 | opts <- options("useFancyQuotes" = 2) 11 | on.exit(options(opts)) 12 | paste(dQuote(x), sep = sep, collapse = collapse) 13 | } 14 | 15 | assert_named <- function(x, arg_name = NULL) { 16 | arg_name <- arg_name %||% rlang::quo_name(rlang::enquo(x)) 17 | if (is.null(names(x)) || !all(nzchar(names(x)))) { 18 | stop("All items in `", arg_name, "` must be named.") 19 | } 20 | } 21 | 22 | to_snake_case <- function(x) { 23 | x <- strsplit(x, "[^[:alnum:]]") 24 | vapply(x, paste, collapse = "_", character(1)) 25 | } 26 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%", 13 | fig.retina = 3 14 | ) 15 | ``` 16 | 17 | # ggconsort 18 | 19 | 20 | 21 | 22 | ## Overview 23 | 24 | The goal of ggconsort is to provide convenience functions for creating [CONSORT diagrams](http://www.consort-statement.org/consort-statement/flow-diagram) with ggplot2. ggconsort segments CONSORT creation into two stages: (1) counting and annotation at the time of data wrangling, and (2) diagram layout and aesthetic design. With the introduction of a `ggconsort_cohort` class, stage (1) can be accomplished within dplyr chains. Specifically, the following functions are implemented inside a dplyr chain to define a `ggconsort_cohort`: 25 | 26 | * `cohort_start()` initializes a `ggconsort_cohort` object which contains a labeled copy of the source data 27 | * `cohort_define()` constructs cohorts that are variations of the source data or other cohorts 28 | * `cohort_label()` adds labels to each named cohort within the `ggconsort_cohort` object 29 | 30 | Stage 2 makes use of three ggconsort `consort_` verbs which equip the `ggconsort_cohort` object with `ggconsort` properties. The `ggconsort` object can be viewed with ggplot via `geom_consort() + theme_consort()`. `plot` and `print` methods are also available for the `ggconsort` object for visually iterative development. 31 | 32 | * `consort_box_add()` adds a text box to the CONSORT diagram 33 | * `consort_arrow_add()` adds an arrow to the CONSORT diagram 34 | * `consort_line_add()` adds a line (without an arrow head) the CONSORT diagram 35 | 36 | ## Installation 37 | 38 | You can install the released version of ggconsort from [GitHub](https://github.com/tgerke/ggconsort) with: 39 | 40 | ``` r 41 | # install.packages("devtools") 42 | devtools::install_github("tgerke/ggconsort") 43 | ``` 44 | 45 | ## Usage 46 | 47 | To demonstrate usage, we use a simulated dataset within ggconsort called `trial_data`, which contains 1200 patients who were approached to participate in a randomized trial comparing Drug A to Drug B. 48 | 49 | ```{r trial_data} 50 | library(ggconsort) 51 | 52 | head(trial_data) 53 | ``` 54 | 55 | Of the 1200 approached patients, only a subset were ultimately randomized: some declined to participate or were ineligible (due to prior chemotherapy or bone metastasis). We will use ggconsort verbs and geoms to count the patient flow and represent the process in a CONSORT diagram. 56 | 57 | We first define the `ggconsort_cohort` object (`study_cohorts`) in the following dplyr chain. 58 | 59 | ```{r example-cohort, message=FALSE} 60 | library(dplyr) 61 | 62 | study_cohorts <- 63 | trial_data %>% 64 | cohort_start("Assessed for eligibility") %>% 65 | # Define cohorts using named expressions -------------------- 66 | # Notice that you can use previously defined cohorts in subsequent steps 67 | cohort_define( 68 | consented = .full %>% filter(declined != 1), 69 | consented_chemonaive = consented %>% filter(prior_chemo != 1), 70 | randomized = consented_chemonaive %>% filter(bone_mets != 1), 71 | treatment_a = randomized %>% filter(treatment == "Drug A"), 72 | treatment_b = randomized %>% filter(treatment == "Drug B"), 73 | # anti_join is useful for counting exclusions ------------- 74 | excluded = anti_join(.full, randomized, by = "id"), 75 | excluded_declined = anti_join(.full, consented, by = "id"), 76 | excluded_chemo = anti_join(consented, consented_chemonaive, by = "id"), 77 | excluded_mets = anti_join(consented_chemonaive, randomized, by = "id") 78 | ) %>% 79 | # Provide text labels for cohorts --------------------------- 80 | cohort_label( 81 | consented = "Consented", 82 | consented_chemonaive = "Chemotherapy naive", 83 | randomized = "Randomized", 84 | treatment_a = "Allocated to arm A", 85 | treatment_b = "Allocated to arm B", 86 | excluded = "Excluded", 87 | excluded_declined = "Declined to participate", 88 | excluded_chemo = "Prior chemotherapy", 89 | excluded_mets = "Bone metastasis" 90 | ) 91 | ``` 92 | 93 | We can have a look at the `study_cohorts` object with its print and summary methods: 94 | 95 | ```{r print-summary} 96 | study_cohorts 97 | 98 | summary(study_cohorts) 99 | ``` 100 | 101 | Next, we add CONSORT "boxes" and "arrows" with appropriate ggconsort verbs, and plot with the CONSORT diagram `ggplot`. Note the use of `cohort_count_adorn()`, which is a convenience function that glues cohort counts to their labels. 102 | 103 | ```{r example-consort} 104 | library(ggplot2) 105 | 106 | study_consort <- study_cohorts %>% 107 | consort_box_add( 108 | "full", 0, 50, cohort_count_adorn(study_cohorts, .full) 109 | ) %>% 110 | consort_box_add( 111 | "exclusions", 20, 40, glue::glue( 112 | '{cohort_count_adorn(study_cohorts, excluded)}
113 | • {cohort_count_adorn(study_cohorts, excluded_declined)}
114 | • {cohort_count_adorn(study_cohorts, excluded_chemo)}
115 | • {cohort_count_adorn(study_cohorts, excluded_mets)} 116 | ') 117 | ) %>% 118 | consort_box_add( 119 | "randomized", 0, 30, cohort_count_adorn(study_cohorts, randomized) 120 | ) %>% 121 | consort_box_add( 122 | "arm_a", -30, 10, cohort_count_adorn(study_cohorts, treatment_a) 123 | ) %>% 124 | consort_box_add( 125 | "arm_b", 30, 10, cohort_count_adorn(study_cohorts, treatment_b) 126 | ) %>% 127 | consort_arrow_add( 128 | end = "exclusions", end_side = "left", start_x = 0, start_y = 40 129 | ) %>% 130 | consort_arrow_add( 131 | "full", "bottom", "randomized", "top" 132 | ) %>% 133 | consort_arrow_add( 134 | start_x = 0, start_y = 30, end_x = 0, end_y = 20, 135 | ) %>% 136 | consort_line_add( 137 | start_x = -30, start_y = 20, end_x = 30, end_y = 20, 138 | ) %>% 139 | consort_arrow_add( 140 | end = "arm_a", end_side = "top", start_x = -30, start_y = 20 141 | ) %>% 142 | consort_arrow_add( 143 | end = "arm_b", end_side = "top", start_x = 30, start_y = 20 144 | ) 145 | 146 | study_consort %>% 147 | ggplot() + 148 | geom_consort() + 149 | theme_consort(margin_h = 8, margin_v = 1) + 150 | # you can include other ggplot geoms, as needed ------------- 151 | ggtext::geom_richtext( 152 | aes(x = 0, y = 10, label = "Allocation"), 153 | fill = "#9bc0fc" 154 | ) 155 | ``` 156 | 157 | At this point, we are ready for analysis. The following retrieves the desired data frame of randomized subjects: 158 | 159 | ```{r pull-data} 160 | study_cohorts %>% 161 | cohort_pull(randomized) 162 | ``` 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # ggconsort 5 | 6 | 7 | 8 | 9 | ## Overview 10 | 11 | The goal of ggconsort is to provide convenience functions for creating 12 | [CONSORT 13 | diagrams](http://www.consort-statement.org/consort-statement/flow-diagram) 14 | with ggplot2. ggconsort segments CONSORT creation into two stages: (1) 15 | counting and annotation at the time of data wrangling, and (2) diagram 16 | layout and aesthetic design. With the introduction of a 17 | `ggconsort_cohort` class, stage (1) can be accomplished within dplyr 18 | chains. Specifically, the following functions are implemented inside a 19 | dplyr chain to define a `ggconsort_cohort`: 20 | 21 | - `cohort_start()` initializes a `ggconsort_cohort` object which 22 | contains a labeled copy of the source data 23 | - `cohort_define()` constructs cohorts that are variations of the 24 | source data or other cohorts 25 | - `cohort_label()` adds labels to each named cohort within the 26 | `ggconsort_cohort` object 27 | 28 | Stage 2 makes use of three ggconsort `consort_` verbs which equip the 29 | `ggconsort_cohort` object with `ggconsort` properties. The `ggconsort` 30 | object can be viewed with ggplot via `geom_consort() + theme_consort()`. 31 | `plot` and `print` methods are also available for the `ggconsort` object 32 | for visually iterative development. 33 | 34 | - `consort_box_add()` adds a text box to the CONSORT diagram 35 | - `consort_arrow_add()` adds an arrow to the CONSORT diagram 36 | - `consort_line_add()` adds a line (without an arrow head) the CONSORT 37 | diagram 38 | 39 | ## Installation 40 | 41 | You can install the released version of ggconsort from 42 | [GitHub](https://github.com/tgerke/ggconsort) with: 43 | 44 | ``` r 45 | # install.packages("devtools") 46 | devtools::install_github("tgerke/ggconsort") 47 | ``` 48 | 49 | ## Usage 50 | 51 | To demonstrate usage, we use a simulated dataset within ggconsort called 52 | `trial_data`, which contains 1200 patients who were approached to 53 | participate in a randomized trial comparing Drug A to Drug B. 54 | 55 | ``` r 56 | library(ggconsort) 57 | 58 | head(trial_data) 59 | #> # A tibble: 6 x 5 60 | #> id declined prior_chemo bone_mets treatment 61 | #> 62 | #> 1 65464 0 0 0 Drug A 63 | #> 2 48228 0 0 0 Drug B 64 | #> 3 92586 0 0 0 Drug A 65 | #> 4 70176 0 0 0 Drug B 66 | #> 5 89052 0 0 0 Drug A 67 | #> 6 97333 0 0 0 Drug B 68 | ``` 69 | 70 | Of the 1200 approached patients, only a subset were ultimately 71 | randomized: some declined to participate or were ineligible (due to 72 | prior chemotherapy or bone metastasis). We will use ggconsort verbs and 73 | geoms to count the patient flow and represent the process in a CONSORT 74 | diagram. 75 | 76 | We first define the `ggconsort_cohort` object (`study_cohorts`) in the 77 | following dplyr chain. 78 | 79 | ``` r 80 | library(dplyr) 81 | 82 | study_cohorts <- 83 | trial_data %>% 84 | cohort_start("Assessed for eligibility") %>% 85 | # Define cohorts using named expressions -------------------- 86 | # Notice that you can use previously defined cohorts in subsequent steps 87 | cohort_define( 88 | consented = .full %>% filter(declined != 1), 89 | consented_chemonaive = consented %>% filter(prior_chemo != 1), 90 | randomized = consented_chemonaive %>% filter(bone_mets != 1), 91 | treatment_a = randomized %>% filter(treatment == "Drug A"), 92 | treatment_b = randomized %>% filter(treatment == "Drug B"), 93 | # anti_join is useful for counting exclusions ------------- 94 | excluded = anti_join(.full, randomized, by = "id"), 95 | excluded_declined = anti_join(.full, consented, by = "id"), 96 | excluded_chemo = anti_join(consented, consented_chemonaive, by = "id"), 97 | excluded_mets = anti_join(consented_chemonaive, randomized, by = "id") 98 | ) %>% 99 | # Provide text labels for cohorts --------------------------- 100 | cohort_label( 101 | consented = "Consented", 102 | consented_chemonaive = "Chemotherapy naive", 103 | randomized = "Randomized", 104 | treatment_a = "Allocated to arm A", 105 | treatment_b = "Allocated to arm B", 106 | excluded = "Excluded", 107 | excluded_declined = "Declined to participate", 108 | excluded_chemo = "Prior chemotherapy", 109 | excluded_mets = "Bone metastasis" 110 | ) 111 | ``` 112 | 113 | We can have a look at the `study_cohorts` object with its print and 114 | summary methods: 115 | 116 | ``` r 117 | study_cohorts 118 | #> A ggconsort cohort of 1200 observations with 9 cohorts: 119 | #> - consented (1141) 120 | #> - consented_chemonaive (1028) 121 | #> - randomized (938) 122 | #> - treatment_a (469) 123 | #> - treatment_b (469) 124 | #> - excluded (262) 125 | #> - excluded_declined (59) 126 | #> - excluded_chemo (113) 127 | #> ...and 1 more. 128 | 129 | summary(study_cohorts) 130 | #> # A tibble: 10 x 3 131 | #> cohort count label 132 | #> 133 | #> 1 .full 1200 Assessed for eligibility 134 | #> 2 consented 1141 Consented 135 | #> 3 consented_chemonaive 1028 Chemotherapy naive 136 | #> 4 randomized 938 Randomized 137 | #> 5 treatment_a 469 Allocated to arm A 138 | #> 6 treatment_b 469 Allocated to arm B 139 | #> 7 excluded 262 Excluded 140 | #> 8 excluded_declined 59 Declined to participate 141 | #> 9 excluded_chemo 113 Prior chemotherapy 142 | #> 10 excluded_mets 90 Bone metastasis 143 | ``` 144 | 145 | Next, we add CONSORT “boxes” and “arrows” with appropriate ggconsort 146 | verbs, and plot with the CONSORT diagram `ggplot`. Note the use of 147 | `cohort_count_adorn()`, which is a convenience function that glues 148 | cohort counts to their labels. 149 | 150 | ``` r 151 | library(ggplot2) 152 | 153 | study_consort <- study_cohorts %>% 154 | consort_box_add( 155 | "full", 0, 50, cohort_count_adorn(study_cohorts, .full) 156 | ) %>% 157 | consort_box_add( 158 | "exclusions", 20, 40, glue::glue( 159 | '{cohort_count_adorn(study_cohorts, excluded)}
160 | • {cohort_count_adorn(study_cohorts, excluded_declined)}
161 | • {cohort_count_adorn(study_cohorts, excluded_chemo)}
162 | • {cohort_count_adorn(study_cohorts, excluded_mets)} 163 | ') 164 | ) %>% 165 | consort_box_add( 166 | "randomized", 0, 30, cohort_count_adorn(study_cohorts, randomized) 167 | ) %>% 168 | consort_box_add( 169 | "arm_a", -30, 10, cohort_count_adorn(study_cohorts, treatment_a) 170 | ) %>% 171 | consort_box_add( 172 | "arm_b", 30, 10, cohort_count_adorn(study_cohorts, treatment_b) 173 | ) %>% 174 | consort_arrow_add( 175 | end = "exclusions", end_side = "left", start_x = 0, start_y = 40 176 | ) %>% 177 | consort_arrow_add( 178 | "full", "bottom", "randomized", "top" 179 | ) %>% 180 | consort_arrow_add( 181 | start_x = 0, start_y = 30, end_x = 0, end_y = 20, 182 | ) %>% 183 | consort_line_add( 184 | start_x = -30, start_y = 20, end_x = 30, end_y = 20, 185 | ) %>% 186 | consort_arrow_add( 187 | end = "arm_a", end_side = "top", start_x = -30, start_y = 20 188 | ) %>% 189 | consort_arrow_add( 190 | end = "arm_b", end_side = "top", start_x = 30, start_y = 20 191 | ) 192 | 193 | study_consort %>% 194 | ggplot() + 195 | geom_consort() + 196 | theme_consort(margin_h = 8, margin_v = 1) + 197 | # you can include other ggplot geoms, as needed ------------- 198 | ggtext::geom_richtext( 199 | aes(x = 0, y = 10, label = "Allocation"), 200 | fill = "#9bc0fc" 201 | ) 202 | ``` 203 | 204 | 205 | 206 | At this point, we are ready for analysis. The following retrieves the 207 | desired data frame of randomized subjects: 208 | 209 | ``` r 210 | study_cohorts %>% 211 | cohort_pull(randomized) 212 | #> # A tibble: 938 x 5 213 | #> id declined prior_chemo bone_mets treatment 214 | #> 215 | #> 1 65464 0 0 0 Drug A 216 | #> 2 48228 0 0 0 Drug B 217 | #> 3 92586 0 0 0 Drug A 218 | #> 4 70176 0 0 0 Drug B 219 | #> 5 89052 0 0 0 Drug A 220 | #> 6 97333 0 0 0 Drug B 221 | #> 7 80724 0 0 0 Drug A 222 | #> 8 65186 0 0 0 Drug B 223 | #> 9 48837 0 0 0 Drug A 224 | #> 10 99005 0 0 0 Drug B 225 | #> # … with 928 more rows 226 | ``` 227 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://tgerke.github.io/ggconsort 2 | 3 | template: 4 | params: 5 | bootswatch: simplex 6 | 7 | authors: 8 | Travis Gerke: 9 | href: "https://travisgerke.com/" 10 | Garrick Aden-Buie: 11 | href: "https://www.garrickadenbuie.com/" 12 | 13 | navbar: 14 | type: default 15 | right: 16 | - icon: fa-github fa-lg 17 | href: https://github.com/tgerke/ggconsort 18 | -------------------------------------------------------------------------------- /data/trial_data.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/data/trial_data.rda -------------------------------------------------------------------------------- /dev/ggconsort-hex.R: -------------------------------------------------------------------------------- 1 | #### 2 | # THIS HEXSTICKER CODE WAS ONLY USED TO GENERATE A TEMPLATE 3 | # THE ULTIMATE STICKER WAS MADE IN FIGMA, AND LOOSELY RESEMBLES 4 | # THE ONE GENERATED FROM THIS SCRIPT 5 | #### 6 | 7 | library(dplyr) 8 | library(ggplot2) 9 | library(ggtext) 10 | 11 | ggplot(data = NULL) + 12 | geom_segment( 13 | aes(x = 0, xend = 0, y = 30, yend = 20), 14 | size = 0.75, linejoin = "mitre", lineend = "butt", 15 | arrow = arrow(length = unit(2, "mm"), type = "closed") 16 | ) + 17 | geom_segment( 18 | aes(x = 0, xend = 15, y = 25, yend = 25), 19 | size = 0.75, linejoin = "mitre", lineend = "butt", 20 | arrow = arrow(length = unit(2, "mm"), type = "closed") 21 | ) + 22 | geom_segment( 23 | aes(x = 0, xend = 0, y = 20, yend = 15), 24 | size = 0.75, linejoin = "mitre", lineend = "butt" 25 | ) + 26 | geom_segment( 27 | aes(x = -15, xend = 15, y = 15, yend = 15), 28 | size = 0.75, linejoin = "mitre", lineend = "butt" 29 | ) + 30 | geom_segment( 31 | aes(x = -15, xend = -15, y = 15, yend = 10), 32 | size = 0.75, linejoin = "mitre", lineend = "butt", 33 | arrow = arrow(length = unit(2, "mm"), type = "closed") 34 | ) + 35 | geom_segment( 36 | aes(x = 15, xend = 15, y = 15, yend = 10), 37 | size = 0.75, linejoin = "mitre", lineend = "butt", 38 | arrow = arrow(length = unit(2, "mm"), type = "closed") 39 | ) + 40 | geom_segment( 41 | aes(x = -15, xend = -15, y = 10, yend = 3), 42 | size = 0.75, linejoin = "mitre", lineend = "butt", 43 | arrow = arrow(length = unit(2, "mm"), type = "closed") 44 | ) + 45 | geom_segment( 46 | aes(x = 15, xend = 15, y = 10, yend = 3), 47 | size = 0.75, linejoin = "mitre", lineend = "butt", 48 | arrow = arrow(length = unit(2, "mm"), type = "closed") 49 | ) + 50 | geom_segment( 51 | aes(x = -15, xend = -15, y = 3, yend = -4), 52 | size = 0.75, linejoin = "mitre", lineend = "butt", 53 | arrow = arrow(length = unit(2, "mm"), type = "closed") 54 | ) + 55 | geom_segment( 56 | aes(x = 15, xend = 15, y = 3, yend = -4), 57 | size = 0.75, linejoin = "mitre", lineend = "butt", 58 | arrow = arrow(length = unit(2, "mm"), type = "closed") 59 | ) + 60 | geom_textbox( 61 | aes(x = 0, y = 30, label = "", vjust = 1), 62 | fill = "#794e58", 63 | box.r = unit(0, units = "npc"), 64 | width = unit(5, "line") 65 | ) + 66 | geom_textbox( 67 | aes(x = 0, y = 20, label = "", vjust = 1), 68 | fill = "#794e58", 69 | box.r = unit(0, units = "npc"), 70 | width = unit(3, "line") 71 | ) + 72 | geom_textbox( 73 | aes(x = 15, y = 25, label = "", hjust = 0), 74 | fill = "#794e58", 75 | box.r = unit(0, units = "npc"), 76 | width = unit(3, "line"), 77 | height = unit(2, "line") 78 | ) + 79 | geom_textbox( 80 | aes(x = -15, y = 10, label = "", vjust = 1), 81 | fill = "#794e58", 82 | box.r = unit(0, units = "npc"), 83 | width = unit(3.5, "line") 84 | ) + 85 | geom_textbox( 86 | aes(x = 15, y = 10, label = "", vjust = 1), 87 | fill = "#794e58", 88 | box.r = unit(0, units = "npc"), 89 | width = unit(3.5, "line") 90 | ) + 91 | geom_textbox( 92 | aes(x = -15, y = 3, label = "", vjust = 1), 93 | fill = "#794e58", 94 | box.r = unit(0, units = "npc"), 95 | width = unit(3.5, "line") 96 | ) + 97 | geom_textbox( 98 | aes(x = 15, y = 3, label = "", vjust = 1), 99 | fill = "#794e58", 100 | box.r = unit(0, units = "npc"), 101 | width = unit(3.5, "line") 102 | ) + 103 | geom_textbox( 104 | aes(x = -15, y = -4, label = "", vjust = 1), 105 | fill = "#794e58", 106 | box.r = unit(0, units = "npc"), 107 | width = unit(3.5, "line") 108 | ) + 109 | geom_textbox( 110 | aes(x = 15, y = -4, label = "", vjust = 1), 111 | fill = "#794e58", 112 | box.r = unit(0, units = "npc"), 113 | width = unit(3.5, "line") 114 | ) + 115 | xlim(-40, 50) + 116 | ylim(-10, 30) + 117 | theme_void() -> p 118 | 119 | library(hexSticker) 120 | library(showtext) 121 | 122 | font_add_google("Noto Sans KR", regular.wt = 500) 123 | showtext_auto() 124 | 125 | sticker( 126 | p, 127 | package="ggconsort", 128 | p_x = 1, 129 | p_y = 1.5, 130 | p_size=20, 131 | p_family = "Noto Sans KR", 132 | p_color = "#4e796f", 133 | s_x=1.03, 134 | s_y=.8, 135 | s_width=1, 136 | s_height=1, 137 | h_fill = "#ffffff", 138 | h_color = "#4e796f", 139 | dpi = 900 140 | ) -> s 141 | 142 | ggsave( 143 | filename= here::here("dev/ggconsort-hex.png"), 144 | plot = s, 145 | width = 9, 146 | height = 9 147 | ) 148 | 149 | ggsave( 150 | filename= here::here("dev/ggconsort-hex.svg"), 151 | plot = s, 152 | width = 9, 153 | height = 9 154 | ) 155 | 156 | -------------------------------------------------------------------------------- /dev/ggconsort-hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/dev/ggconsort-hex.png -------------------------------------------------------------------------------- /dev/ggconsort-hex.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /dev/ggconsort.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/dev/ggconsort.fig -------------------------------------------------------------------------------- /dev/sim-data.R: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | 3 | set.seed(8675309) 4 | 5 | n <- 1200 6 | 7 | screen_data <- tibble( 8 | id = sample(10000:99999, size = n, replace = FALSE), 9 | declined = sample(0:1, n, replace = TRUE, prob = c(.95, .05)), 10 | prior_chemo = sample(0:1, n, replace = TRUE, prob = c(.90, .10)), 11 | bone_mets = sample(0:1, n, replace = TRUE, prob = c(.90, .10)), 12 | ) %>% 13 | mutate( 14 | excluded = if_else(declined | prior_chemo | bone_mets, 1, 0) 15 | ) 16 | 17 | randomized <- screen_data %>% 18 | filter(excluded == 0) %>% 19 | mutate( 20 | treatment = if_else(row_number() %% 2 == 1, "Drug A", "Drug B") 21 | ) 22 | 23 | trial_data <- screen_data %>% 24 | select(-excluded) %>% 25 | left_join( 26 | randomized %>% 27 | select(id, treatment), 28 | by = "id" 29 | ) 30 | 31 | # usethis::use_data(trial_data) 32 | -------------------------------------------------------------------------------- /ggconsort.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 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /man/cohort_count.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cohort_count.R 3 | \name{cohort_count} 4 | \alias{cohort_count} 5 | \alias{cohort_count_int} 6 | \alias{cohort_count_adorn} 7 | \title{Count the number of rows in each ggconsort cohort} 8 | \usage{ 9 | cohort_count(.data, ...) 10 | 11 | cohort_count_int(.data, ...) 12 | 13 | cohort_count_adorn(.data, ..., .label_fn = NULL) 14 | } 15 | \arguments{ 16 | \item{.data}{A \code{ggconsort_cohort} object} 17 | 18 | \item{...}{Cohorts to include in the output, can be quoted or unquoted 19 | cohort names, or \pkg{tidyselect} helpers such as 20 | [tidyselect::starts_with()].} 21 | 22 | \item{.label_fn}{An optional custom function for formatting cohort counts} 23 | } 24 | \value{ 25 | A \code{tibble} with cohort name, row number total, and label. 26 | } 27 | \description{ 28 | Count the number of rows in each ggconsort cohort 29 | } 30 | \section{Functions}{ 31 | \itemize{ 32 | \item \code{cohort_count}: Returns a tibble with cohort name, row number total 33 | and label. 34 | 35 | \item \code{cohort_count_int}: Returns a named vector with cohort counts. 36 | 37 | \item \code{cohort_count_adorn}: Returns a cohort count in "(n = )" or 38 | other custom format 39 | }} 40 | 41 | \examples{ 42 | cohorts <- trial_data \%>\% 43 | cohort_start("Assessed for eligibility") \%>\% 44 | cohort_define( 45 | consented = .full \%>\% dplyr::filter(declined != 1), 46 | consented_chemonaive = consented \%>\% dplyr::filter(prior_chemo != 1) 47 | ) \%>\% 48 | cohort_label( 49 | consented = "Consented", 50 | consented_chemonaive = "Chemotherapy naive" 51 | ) 52 | 53 | cohorts \%>\% 54 | cohort_count() 55 | 56 | cohorts \%>\% 57 | cohort_count_adorn() 58 | 59 | cohorts \%>\% 60 | cohort_count_adorn( 61 | starts_with("consented"), 62 | .label_fn = function(cohort, label, count, ...) { 63 | glue::glue("{count} {label} ({cohort})") 64 | } 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /man/cohort_define.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cohort_define.R 3 | \name{cohort_define} 4 | \alias{cohort_define} 5 | \title{Define ggconsort cohorts} 6 | \usage{ 7 | cohort_define(.data, ...) 8 | } 9 | \arguments{ 10 | \item{.data}{A \code{ggconsort_cohort} object} 11 | 12 | \item{...}{A series of named expressions which define the cohorts} 13 | } 14 | \value{ 15 | The modified \code{ggconsort_cohort} object which now includes 16 | additional \code{$data} items according to provided cohort definitions 17 | } 18 | \description{ 19 | Following a call to \code{cohort_start}, use \code{cohort_define} 20 | to construct cohorts from the full source data which are appended 21 | to the \code{ggconsort_cohort} object. 22 | } 23 | -------------------------------------------------------------------------------- /man/cohort_label.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cohort_label.R 3 | \name{cohort_label} 4 | \alias{cohort_label} 5 | \title{Add labels to ggconsort cohorts} 6 | \usage{ 7 | cohort_label(.data, ...) 8 | } 9 | \arguments{ 10 | \item{.data}{A \code{ggconsort_cohort} object} 11 | 12 | \item{...}{A series of named expressions which provide labels 13 | corresponding to named cohorts in the \code{ggconsort_cohort} object} 14 | } 15 | \value{ 16 | The modified \code{ggconsort_cohort} object which now includes 17 | additional \code{$labels} items according to provided label definitions 18 | } 19 | \description{ 20 | Add labels to ggconsort cohorts 21 | } 22 | -------------------------------------------------------------------------------- /man/cohort_pull.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cohort_pull.R 3 | \name{cohort_pull} 4 | \alias{cohort_pull} 5 | \title{Extract a cohort data frame} 6 | \usage{ 7 | cohort_pull(.data, ...) 8 | } 9 | \arguments{ 10 | \item{.data}{A \code{ggconsort_cohort} object} 11 | 12 | \item{...}{The cohort to pull as an unquoted expression} 13 | } 14 | \value{ 15 | A tibble for a single cohort 16 | } 17 | \description{ 18 | Pull a single tibble from a \code{ggconsort_cohort} object, 19 | often for downstream analysis or deeper inspection. 20 | } 21 | \examples{ 22 | cohorts <- trial_data \%>\% 23 | cohort_start("Assessed for eligibility") \%>\% 24 | cohort_define( 25 | consented = .full \%>\% dplyr::filter(declined != 1), 26 | consented_chemonaive = consented \%>\% dplyr::filter(prior_chemo != 1) 27 | ) 28 | 29 | cohorts \%>\% cohort_pull(consented_chemonaive) 30 | } 31 | -------------------------------------------------------------------------------- /man/cohort_select.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cohort_select.R 3 | \name{cohort_select} 4 | \alias{cohort_select} 5 | \title{Subset ggconsort cohort objects} 6 | \usage{ 7 | cohort_select(.data, ...) 8 | } 9 | \arguments{ 10 | \item{.data}{A \code{ggconsort_cohort} object} 11 | 12 | \item{...}{The cohort(s) to pull as an unquoted or 13 | \code{tidyselect} style expression} 14 | } 15 | \value{ 16 | A \code{ggconsort_cohort} object 17 | } 18 | \description{ 19 | Select a subset of cohorts from a \code{ggconsort_cohort} 20 | object 21 | } 22 | \examples{ 23 | cohorts <- 24 | trial_data \%>\% 25 | cohort_start("Assessed for eligibility") \%>\% 26 | cohort_define( 27 | consented = .full \%>\% dplyr::filter(declined != 1), 28 | treatment_a = consented \%>\% dplyr::filter(treatment == "Drug A"), 29 | treatment_b = consented \%>\% dplyr::filter(treatment == "Drug B") 30 | ) \%>\% 31 | cohort_label( 32 | consented = "Consented", 33 | treatment_a = "Allocated to arm A", 34 | treatment_b = "Allocated to arm B" 35 | ) 36 | 37 | cohorts \%>\% cohort_select(consented) 38 | 39 | cohorts \%>\% 40 | cohort_select(starts_with("treatment_")) 41 | 42 | } 43 | -------------------------------------------------------------------------------- /man/cohort_start.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cohort_start.R 3 | \name{cohort_start} 4 | \alias{cohort_start} 5 | \title{Initialize a new ggconsort cohort} 6 | \usage{ 7 | cohort_start(.data, label = NULL) 8 | } 9 | \arguments{ 10 | \item{.data}{A data frame or tibble} 11 | 12 | \item{label}{A character string to describe the set of cohorts} 13 | } 14 | \value{ 15 | Returns a \code{ggconsort_cohort} object with 16 | \code{$data} and \code{$labels} items 17 | } 18 | \description{ 19 | Creates an object with an optional label which stores 20 | the originating source data for downstream ggconsort cohorts 21 | } 22 | -------------------------------------------------------------------------------- /man/consort_start.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/consort_start.R 3 | \name{consort_start} 4 | \alias{consort_start} 5 | \title{Initialize a ggconsort object for plotting. Used internally.} 6 | \usage{ 7 | consort_start(.data) 8 | } 9 | \arguments{ 10 | \item{.data}{A \code{ggconsort_cohort} object} 11 | } 12 | \description{ 13 | Initialize a ggconsort object for plotting. Used internally. 14 | } 15 | \keyword{Internal} 16 | -------------------------------------------------------------------------------- /man/figures/README-example-consort-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/man/figures/README-example-consort-1.png -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/man/figures/logo.png -------------------------------------------------------------------------------- /man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils-pipe.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \arguments{ 10 | \item{lhs}{A value or the magrittr placeholder.} 11 | 12 | \item{rhs}{A function call using the magrittr semantics.} 13 | } 14 | \value{ 15 | The result of calling `rhs(lhs)`. 16 | } 17 | \description{ 18 | See \code{magrittr::\link[magrittr:pipe]{\%>\%}} for details. 19 | } 20 | \keyword{internal} 21 | -------------------------------------------------------------------------------- /man/trial_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{trial_data} 5 | \alias{trial_data} 6 | \title{Screened and randomized patients} 7 | \format{ 8 | A tibble with 1200 rows and 5 variables: 9 | \describe{ 10 | \item{id}{patient ID} 11 | \item{declined}{indicator for declining to participate} 12 | \item{prior_chemo}{indicator for prior chemotherapy, a trial exclusion} 13 | \item{bone_mets}{indicator for bone metastases, a trial exclusion} 14 | \item{treatment}{treatment assignment} 15 | } 16 | } 17 | \source{ 18 | dev/sim-data.R 19 | } 20 | \usage{ 21 | trial_data 22 | } 23 | \description{ 24 | A simulated dataset containing 3 trial exclusion variables and 25 | a treatment allocation variable. 26 | } 27 | \keyword{datasets} 28 | -------------------------------------------------------------------------------- /pkgdown/extra.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary: #57786f; 3 | --secondary: #735058; 4 | } 5 | 6 | .navbar { 7 | background-color: var(--primary); 8 | border-color: var(--secondary); 9 | } 10 | 11 | .navbar-default .navbar-link, 12 | .navbar-default .navbar-nav > li > a, 13 | .navbar-default .navbar-nav > .active > a, 14 | .navbar-default .navbar-nav > .active > a:focus { 15 | color: #ffffff; 16 | } 17 | 18 | a:hover, 19 | .fas:hover, 20 | .navbar-default .navbar-nav > li > a:hover, 21 | .navbar-default .navbar-link:hover, 22 | .navbar-default .navbar-nav > .active > a:hover { 23 | color: var(--secondary); 24 | } 25 | 26 | .fas { 27 | color: #ffffff; 28 | } 29 | 30 | a, code a, pre a { 31 | color: var(--primary); 32 | } 33 | 34 | .label-default { 35 | background-color: var(--secondary); 36 | } 37 | 38 | .btn-primary, .btn-primary:hover { 39 | background-image: linear-gradient(var(--secondary), var(--secondary) 6%, var(--secondary)); 40 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe72510', endColorstr='#ffcb210e', GradientType=0); 41 | background-repeat: no-repeat; 42 | -webkit-filter: none; 43 | filter: none; 44 | border: 1px solid var(--secondary); 45 | } 46 | -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgerke/ggconsort/b0a879afa9ef5c9b3648bda11f5cb1763cffab4e/pkgdown/favicon/favicon.ico --------------------------------------------------------------------------------