apexcharter/R/facets.R

320 lines
8.8 KiB
R

#' @importFrom rlang eval_tidy
get_facets <- function(data, vars) {
byvars <- lapply(X = vars, FUN = eval_tidy, data = data)
facets <- split(x = data, f = byvars, sep = "|__|")
lapply(
X = seq_along(facets),
FUN = function(i) {
facet <- facets[[i]]
attr(facet, "keys") <- strsplit(
x = names(facets)[i],
split = "|__|", fixed = TRUE
)[[1]]
facet
}
)
}
set_scale <- function(ax, values, scales = c("fixed", "free", "free_y", "free_x"), axis = c("x", "y")) {
if (is.null(scales))
return(ax)
scales <- match.arg(scales)
axis <- match.arg(axis)
if (is.null(values))
return(ax)
if (inherits(values, c("numeric", "integer", "Date", "POSIXt"))) {
range_vals <- range(pretty(values), na.rm = TRUE)
} else {
range_vals <- NULL
}
fmt <- function(x, time = inherits(values, c("Date", "POSIXt"))) {
if (is.null(x))
return(NULL)
if (time)
x <- format_date(x)
x
}
fun_axis <- switch(
axis,
"x" = ax_xaxis,
"y" = ax_yaxis
)
if (scales == "fixed") {
ax <- fun_axis(
ax = ax,
min = fmt(range_vals[1]),
max = fmt(range_vals[2])
)
} else if (scales == "free") {
ax <- fun_axis(
ax = ax,
min = character(0),
max = character(0)
)
} else {
ax <- fun_axis(
ax = ax,
min = fmt(range_vals[1]),
max = fmt(range_vals[2])
)
if (scales == "free_x" & axis == "x") {
ax <- fun_axis(
ax = ax,
min = character(0),
max = character(0)
)
}
if (scales == "free_y" & axis == "y") {
ax <- fun_axis(
ax = ax,
min = character(0),
max = character(0)
)
}
}
return(ax)
}
#' @importFrom rlang eval_tidy is_null is_function
build_facets <- function(chart) {
data <- chart$x$data
mapall <- lapply(chart$x$mapping, eval_tidy, data = data)
labeller <- chart$x$facet$labeller
facets_data <- get_facets(data, chart$x$facet$facets)
nums <- seq_along(facets_data)
dims <- get_grid_dims(nums, nrow = chart$x$facet$nrow, ncol = chart$x$facet$ncol)
grid <- matrix(
data = c(
nums,
rep(NA, times = (dims$nrow * dims$ncol) - length(nums))
),
nrow = dims$nrow,
ncol = dims$ncol,
byrow = TRUE
)
lrow <- get_last_row(grid)
lapply(
X = nums,
FUN = function(i) {
new <- chart
facet <- facets_data[[i]]
if (!is_null(labeller) && is_function(labeller)) {
keys <- attr(facet, "keys")
text <- labeller(keys)
new <- ax_title(new, text = text, margin = 0, floating = length(text) <= 1)
}
mapdata <- lapply(chart$x$mapping, eval_tidy, data = facet)
if (chart$x$facet$scales %in% c("fixed", "free_y") & chart$x$type %in% c("bar")) {
mapdata <- complete_mapdata(mapdata, mapall)
}
if (chart$x$facet$scales %in% c("fixed", "free_x") & chart$x$type %in% c("column")) {
mapdata <- complete_mapdata(mapdata, mapall)
}
new$x$ax_opts$series <- make_series(mapdata, chart$x$mapping, chart$x$type, chart$x$serie_name)
new <- set_scale(new, mapall$x, scales = chart$x$facet$scales, axis = "x")
new <- set_scale(new, mapall$y, scales = chart$x$facet$scales, axis = "y")
if (chart$x$facet$scales %in% c("fixed", "free_x")) {
new <- ax_yaxis(new, show = i %in% grid[, 1])
}
# if (chart$x$facet$scales %in% c("fixed", "free_y")) {
# new <- ax_xaxis(new, labels = list(show = i %in% lrow), axisTicks = list(show = TRUE))
# }
if (chart$x$facet$scales %in% c("fixed", "free_y") & chart$x$type %in% c("bar", "column")) {
new <- ax_xaxis(new, labels = list(show = i %in% lrow))
}
if (!is.null(new$x$colors_manual)) {
new <- ax_colors_manual(ax = new, values = new$x$colors_manual)
}
new$height <- chart$x$facet$chart_height
new$x$facet <- NULL
class(new) <- setdiff(class(new), "apex_facet")
return(new)
}
)
}
get_last_row <- function(mat) {
apply(X = mat, MARGIN = 2, FUN = function(x) {
x <- x[!is.na(x)]
x[length(x)]
})
}
#' Facet wrap for ApexCharts
#'
#' @param ax An \code{apexcharts} \code{htmlwidget} object.
#' @param facets Variable(s) to use for facetting, wrapped in \code{vars(...)}.
#' @param nrow,ncol Number of row and column in output matrix.
#' @param scales Should scales be fixed (\code{"fixed"}, the default),
#' free (\code{"free"}), or free in one dimension (\code{"free_x"}, \code{"free_y"})?
#' @param labeller A function with one argument containing for each facet the value of the faceting variable.
#' @param chart_height Individual chart height.
#'
#' @return An \code{apexcharts} \code{htmlwidget} object.
#' @export
#'
#' @importFrom rlang quos syms
#'
#' @example examples/facet_wrap.R
ax_facet_wrap <- function(ax,
facets,
nrow = NULL,
ncol = NULL,
scales = c("fixed", "free", "free_y", "free_x"),
labeller = label_value,
chart_height = "300px") {
if (!inherits(ax, "apex"))
stop("ax_facet_wrap only works with charts generated with apex()", call. = FALSE)
scales <- match.arg(scales)
if (is.character(facets))
facets <- quos(!!!syms(facets))
ax$x$facet <- list(
facets = facets,
nrow = nrow,
ncol = ncol,
scales = scales,
labeller = labeller,
chart_height = chart_height
)
class(ax) <- c("apex_facet", class(ax))
return(ax)
}
# Shiny -------------------------------------------------------------------
#' @title Shiny bindings for faceting with apexcharter
#'
#' @description Output and render functions for using apexcharter faceting within Shiny
#' applications and interactive Rmd documents.
#'
#' @param outputId output variable to read from
#'
#' @return An Apexcharts output that can be included in the application UI.
#' @export
#'
#' @name apexcharter-shiny-facets
#'
#' @importFrom htmltools tagList
#' @importFrom shiny uiOutput
#' @importFrom htmlwidgets getDependency
#'
#' @example examples/facet-shiny.R
apexfacetOutput <- function(outputId) {
tagList(
uiOutput(outputId = outputId),
getDependency(name = "apexcharter", package = "apexcharter")
)
}
#' @param expr An expression that generates a apexcharter facet.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#'
#' @export
#'
#' @rdname apexcharter-shiny-facets
#'
#' @importFrom shiny exprToFunction createRenderFunction createWebDependency
#' @importFrom htmltools renderTags resolveDependencies
renderApexfacet <- function(expr, env = parent.frame(), quoted = FALSE) {
func <- exprToFunction(expr, env, quoted)
createRenderFunction(
func = func,
transform = function(result, shinysession, name, ...) {
if (is.null(result) || length(result) == 0)
return(NULL)
if (!inherits(result, "apex_facet")) {
stop(
"renderApexfacet: 'expr' must return an apexcharter facet object.",
call. = FALSE
)
}
facets_charts <- build_facets(result)
TAG <- build_grid(
content = facets_charts,
nrow = result$x$facet$nrow,
ncol = result$x$facet$ncol
)
rendered <- renderTags(TAG)
deps <- lapply(
X = resolveDependencies(rendered$dependencies),
FUN = createWebDependency
)
list(
html = rendered$html,
deps = deps
)
}, apexfacetOutput, list()
)
}
# Print methods -----------------------------------------------------------
#' @export
print.apex_facet <- function(x, ...) {
facets_charts <- build_facets(x)
TAG <- build_grid(facets_charts, nrow = x$x$facet$nrow, ncol = x$x$facet$ncol)
print(htmltools::browsable(TAG))
}
knit_print.apex_facet <- function(x, ..., options = NULL) {
facets_charts <- build_facets(x)
TAG <- build_grid(facets_charts, nrow = x$x$facet$nrow, ncol = x$x$facet$ncol)
knitr::knit_print(htmltools::browsable(TAG), options = options, ...)
}
# Complete ----------------------------------------------------------------
complete_mapdata <- function(mapdata, mapall) {
data <- as.data.frame(mapdata)
full_data <- data.frame(x = unique(mapall$x), stringsAsFactors = FALSE)
full_data <- merge(
x = full_data,
y = data,
by = "x",
all.x = TRUE,
sort = TRUE
)
full_data$y[is.na(full_data$y)] <- 0
return(as.list(full_data))
}
complete_data <- function(data, vars, fill_var, fill_value = 0) {
full_data <- expand.grid(lapply(
X = data[, vars],
FUN = unique
))
full_data <- merge(
x = full_data,
y = data,
by = vars,
all.x = TRUE,
sort = FALSE
)
full_data[[fill_var]][is.na(full_data[[fill_var]])] <- fill_value
return(full_data)
}