Nothing
#' Wrapper around ggpairs
#'
#' @param xpdb <`xp_xtras> or <`xpose_data`> object
#' @param mapping `ggplot2` style mapping
#' @param cont_opts List of options to pass to `xplot_scatter`. See Details
#' @param dist_opts List of options to pass to `xplot_distribution`. See Details
#' @param cat_opts List of options to pass to `xplot_boxplot`. See Details
#' @param contcont_opts List of options to pass to `ggally_cors`. See Details
#' @param catcont_opts List of options to pass to `ggally_statistic`. See Details
#' @param catcat_opts A list with `use_rho` `TRUE` or `FALSE`. If `TRUE` (default),
#' then the Spearman rho is displayed, else the ggpairs default count is used.
#' @param title Plot title
#' @param subtitle Plot subtitle
#' @param caption Plot caption
#' @param tag Plot tag
#' @param plot_name Metadata name of plot
#' @param gg_theme As in `xpose`. This does not work reliably when changed from the default.
#' @param xp_theme As in `xpose`
#' @param opt Processing options for fetched data
#' @param quiet Silence extra debugging output
#' @param progress Show a progress bar as the plot is generated?
#' @param switch Passed to `ggpairs`
#' @param ... Ignored
#'
#' @description
#' Following the `xpose` design pattern to derive <[`ggpairs`][GGally::ggpairs]> plots.
#'
#' Established `xplot_` are used to generate parts of the grid.
#'
#' @details
#' There is only limited control over the underlying `ggpairs()` call given
#' the need to address abstractions in `GGally` and `xpose`. However, users
#' can modify key display features. For `scatter`, `distribution` and `boxplots`,
#' the `type` option is directly forwarded to the user. For upper elements of the matrix,
#' users can modify features of the text displayed or supply some other
#' function entirely (`other_fun`).
#'
#' `_opts` lists are consumed with <[`modifyList`][utils::modifyList]> from the default,
#' so there is no need to declare preferences that align with the default if updating
#' a subset.
#'
#'
#' @return specified pair plot
#' @export
#'
xplot_pairs <- function(
xpdb,
mapping = NULL,
cont_opts = list(
group = "ID",
guide = FALSE,
type = 'ps'
),
dist_opts = list(
guide = FALSE,
type = "hr"
),
cat_opts = list(
type = 'bo',
log = NULL
),
contcont_opts = list(
other_fun = NULL,
stars= FALSE,
digits = reportable_digits(xpdb),
title = "Pearson Corr"
),
catcont_opts = list(
other_fun = NULL,
stars= FALSE,
digits = reportable_digits(xpdb),
title = "Spearman rho"
),
catcat_opts = list(
use_rho = TRUE
),
title = NULL,
subtitle = NULL,
caption = NULL,
tag = NULL,
plot_name = 'pairs',
gg_theme,
xp_theme,
opt,
quiet,
progress = rlang::is_interactive() && quiet,
switch = NULL,
...
) {
#### Boilerplate for typical parts
# Check input
xpose::check_xpdb(xpdb, check = FALSE)
if (missing(quiet)) quiet <- xpdb$options$quiet
# Fetch data
if (missing(opt)) opt <- xpose::data_opt()
data <- xpose::fetch_data(xpdb, quiet = quiet, .problem = opt$problem, .subprob = opt$subprob,
.method = opt$method, .source = opt$source, simtab = opt$simtab,
filter = opt$filter, tidy = opt$tidy, index_col = opt$index_col,
value_col = opt$value_col, post_processing = opt$post_processing)
if (is.null(data) || nrow(data) == 0) {
rlang::abort('No data available for plotting. Please check the variable mapping and filering options.')
}
# Update _opts defauls
use_upt <- function(x_opt) {
opt_nm <- deparse(substitute(x_opt))
if (is.list(x_opt)) modifyList(eval(formals(xplot_pairs)[[opt_nm]]), x_opt) else cli::cli_abort("`{opt_nm}` must be a list.")
}
# Check types (allow incomplete list to specify opts)
cont_opts <- use_upt(cont_opts)
xpose::check_plot_type(cont_opts$type, allowed = c("l", "p", "s", "t"))
dist_opts <- use_upt(dist_opts)
xpose::check_plot_type(dist_opts$type, allowed = c("d", "h", "r"))
cat_opts <- use_upt(cat_opts)
xpose::check_plot_type(cat_opts$type, allowed = c('b', "p","v","o","l"))
# Check other options
contcont_opts <- use_upt(contcont_opts)
catcont_opts <- use_upt(catcont_opts)
catcat_opts <- use_upt(catcat_opts)
# Assign xp_theme
if (!missing(xp_theme)) xpdb <- xpose::update_themes(xpdb = xpdb, xp_theme = xp_theme)
# Update theme of non-xp_xtra object
if (!is_xp_xtras(xpdb)) xpdb <- xpose::update_themes(xpdb = xpdb, xp_theme = xp_xtra_theme(xpdb$xp_theme))
# Assign gg_theme
if (missing(gg_theme)) {
gg_theme <- xpdb$gg_theme
} else {
gg_theme <- xpose::update_themes(xpdb = xpdb, gg_theme = gg_theme)$gg_theme
}
if (is.function(gg_theme)) {
gg_theme <- do.call(gg_theme, args = list())
}
#### Wrapped functions for ggpairs
wrapped_scatter <- function(data = NULL, mapping = NULL) {
xpose::xplot_scatter(
xpdb = xpdb,
mapping = mapping,
group = cont_opts$group,
type = cont_opts$type,
guide = cont_opts$guid,
opt = opt,
gg_theme=xpdb$gg_theme,
xp_theme=xpdb$xp_theme,
quiet=quiet,
smooth_formula=y~x
)
}
wrapped_dist <- function(data = NULL, mapping = NULL) {
xpose::xplot_distrib(
xpdb = xpdb,
mapping = mapping,
group = dist_opts$group,
type = dist_opts$type,
guide = dist_opts$guid,
opt = opt,
gg_theme=xpdb$gg_theme,
xp_theme=xpdb$xp_theme,
quiet=quiet
)
}
wrapped_box <- function(data = NULL, mapping = NULL) {
orientation = formals(xplot_boxplot)$orientation
var_x <- rlang::eval_tidy(mapping$x, data)
var_y <- rlang::eval_tidy(mapping$y, data)
if (inherits(var_x, "factor") && inherits(var_y, "numeric")) {
orientation <- "x"
xscale = "discrete"
yscale = xpose::check_scales("y", cat_opts$log)
} else {
orientation <- "y"
yscale = "discrete"
xscale = xpose::check_scales("x", cat_opts$log)
}
xplot_boxplot(
xpdb = xpdb,
mapping = mapping,
group = cat_opts$group,
type = cat_opts$type,
guide = cat_opts$guid,
xscale = xscale,
yscale = yscale,
orientation = orientation,
opt = opt,
gg_theme=xpdb$gg_theme,
xp_theme=xpdb$xp_theme,
quiet=quiet
)
}
## Upper cells
if (!is.null(contcont_opts$other_fun)) {
if (!rlang::is_function(contcont_opts$other_fun)) {
cli::cli_abort("`contcont_opts$otherfun` must be a function compatible with `GGally::ggpairs()`, not a {cli::col_yellow(class(contcont_opts$other_fun))}.")
}
xp_cor <- contcont_opts$other_fun
} else {
if ("other_fun" %in% names(contcont_opts)) contcont_opts <- within(contcont_opts, rm(other_fun))
xp_cor <- GGally::wrapp("cor", params = contcont_opts)
}
if (!is.null(catcont_opts$other_fun)) {
if (!rlang::is_function(catcont_opts$other_fun)) {
cli::cli_abort("`catcont_opts$otherfun` must be a function compatible with `GGally::ggpairs()`, not a {cli::col_yellow(class(catcont_opts$other_fun))}.")
}
xp_aov <- catcont_opts$other_fun
} else {
if ("other_fun" %in% names(catcont_opts)) catcont_opts <- within(catcont_opts, rm(other_fun))
rho_fun <- function(x, y) {
corObj <- stats::cor.test(as.numeric(y),as.numeric(x), method="spearman", exact = FALSE)
cor_est <- as.numeric(corObj$estimate)
cor_txt <- formatC(cor_est, digits = catcont_opts$digits, format = "f")
if (isTRUE(catcont_opts$stars)) {
cor_txt <- stringr::str_c(cor_txt, GGally::signif_stars(corObj$p.value))
}
cor_txt
}
xp_rho <- GGally::wrap("statistic", text_fn = rho_fun, title = catcont_opts$title, sep=":\n")
}
if (catcat_opts$use_rho && exists("xp_rho")) {
catcat_upper <- xp_rho
} else {
catcat_upper <- wrap_xp_ggally("count", xp_theme = xpdb$xp_theme)
}
if (!"pairs_labeller" %in% names(xpdb$xp_theme)) {
use_labeller <- xpdb$xp_theme$labeller
} else {
use_labeller <- xpdb$xp_theme$pairs_labeller
}
xp <-
GGally::ggpairs(
data,
diag = list(continuous = wrapped_dist, discrete = wrap_xp_ggally("barDiag", xp_theme = xpdb$xp_theme), na = "naDiag"),
lower = list(continuous = wrapped_scatter, combo = wrapped_box, discrete = wrap_xp_ggally("facetbar", xp_theme = xpdb$xp_theme), na =
"na"),
upper = list(continuous = xp_cor, combo = xp_rho, discrete = catcat_upper, na = "na"),
progress = progress,
labeller = use_labeller,
switch = switch
) +
gg_theme
# Add labels
xp <- xp + ggplot2::labs(title = title, subtitle = subtitle, caption = caption)
if (utils::packageVersion('ggplot2') >= '3.0.0') {
xp <- xp + ggplot2::labs(tag = tag)
}
# Add metadata to plots
xp$xpose <- list(fun = plot_name,
summary = xpdb$summary,
problem = attr(data, 'problem'),
subprob = attr(data, 'subprob'),
method = attr(data, 'method'),
quiet = quiet,
xp_theme = xpdb$xp_theme[stringr::str_c(c('title', 'subtitle',
'caption', 'tag'), '_suffix')])
# Output the plot
xpose::as.xpose.plot(xp) %>%
structure(class=c("xp_xtra_plot", class(.)))
}
#' @method print xp_xtra_plot
#' @export
print.xp_xtra_plot <- function(x, page, ...) {
if (!inherits(x, "ggmatrix")) return(NextMethod())
if (xpose::is.xpose.plot(x)) {
x$title <- xpose::append_suffix(x$xpose, x$title,
"title")
x$gg$labs$subtitle <- xpose::append_suffix(x$xpose, x$gg$labs$subtitle,
"subtitle")
x$gg$labs$caption <- xpose::append_suffix(x$xpose, x$gg$labs$caption,
"caption")
if (utils::packageVersion("ggplot2") >= "3.0.0") {
x$gg$labs$tag <- xpose::append_suffix(x$xpose, x$gg$labs$tag,
"tag")
}
var_map <- x$mapping %>% as.character() %>% stringr::str_remove(pattern = "^~") %>%
ifelse(stringr::str_detect(., "\\.data\\[\\[\"\\w+\"]]"),
yes = stringr::str_remove_all(., "(\\.data\\[\\[\")|(\"]])"),
no = .) %>% purrr::set_names(names(x$mapping))
tr_vals <- function(xx) {
if (is.null(xx)) return(xx)
xx %>%
{`if`(
rlang::is_character(.),
list(.),
.
)} %>%
structure(class="list") %>%
purrr::map_if(stringr::str_detect(purrr::list_c(.), "@"),
.f = xpose::parse_title, xpdb = x$xpose, problem = x$xpose$problem,
quiet = x$xpose$quiet, ignore_key = c("page", "lastpage"),
extra_key = c("plotfun", "timeplot", names(var_map)),
extra_value = c(x$xpose$fun, format(Sys.time(), "%a %b %d %X %Z %Y"),
var_map)) %>%
structure(class=class(xx))
}
x$gg$labs <- x$gg$labs %>% tr_vals()
x$title <- x$title %>% tr_vals()
}
if (!missing(page)) NULL
nm_x <- x
class(nm_x) <- class(x)[!class(x) %in% c("xp_xtra_plot","xpose_plot")]
print(nm_x)
invisible(x)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.