I'm attempting to build a function to produce box plots via highcharter
. Once upon a time a function existed in highcharter called hcboxplot
which has since been deprecated and which also uses deprecated tidy functions at this point.
Therefore, I'm now using highcharter::data_to_boxplot
which creates a data.frame with list columns where each list includes boxplot.stats points. In an effort to simplify things for my team and I; I want to write a wrapper function for data_to_boxplot that builds out a basic boxplot.
Since data_to_boxplot
uses data variables I need to embrace -> {{}} my arguments in order to evaluate them in the right context. Maybe there's another way? I'm very new to tidy evaluation as I mainly work with data.table
and am having difficulty with handling optional parameters.
library(data.table)
library(highcharter)
library(tidyr)
# dummy data
tmp = data.table(
x1 = runif(30) * 10,
grp = rep(c("a","b","c"),10),
grp2 = rep(c("d","e"),15)
)
# transformed for hc
stat = data_to_boxplot(tmp, x1, grp, grp2)
# sample plot
highchart(type = "chart") %>%
hc_add_series_list(stat)
hc_boxplot = function(data, x, var = NULL, var2 = NULL, outliers = FALSE, ...) {
arggs = list(...)
d = data_to_boxplot(data = data, variable = {{x}}, group_var = {{var}}, group_var2 = {{var2}}, add_outliers = outliers, arggs)
# basic chart
highchart(type = "chart") %>%
hc_add_series_list(d)
}
hc_boxplot(data = tmp, x = x1, var = grp, name = "test")
# Error in setnames(x, value) : - This refers to the excluded var2 arg
# Can't assign 1 names to a 0 column data.table
This works for the most part when all arguments are used but if I omit var2
or var & var2
. Those args are still evaluated using {{}} even though to my understanding they should be missing. Below is a snippet from the data_to_boxplot code where the wrong portion of the if statement is evaluated. Since data_to_boxplot doesn't treat var2 as missing.
# where var2 == group_var2
# evaluates to TRUE instead of FALSE and proceeds to build an empty table
if (!missing(group_var2)) {
dg2 <- data %>% select({
{
group_var2
}
})
}
else {
dg2 <- data.frame(rep(NA, nrow(dx)))
}
Since the arguments are not environment variables, I'm wondering how I can signal to data_to_boxplot in my wrapper that the omitted arguments are in fact missing.
Additionally, I'm having trouble passing in named arguments via (...) to data_to_boxplot. In my example above I use name = "test"
which should name the series but am getting an undesired result. Where a new column is added named "arggs" with "test" in it instead of updated the column titled "name".
# A tibble: 2 × 5
name data id type arggs
<chr> <list> <chr> <chr> <named list>
1 d <list [3]> d boxplot <chr [1]>
2 e <list [3]> e boxplot <chr [1]>
Appreciate any help on this as I'm sufficiently stuck.
This is a bug in highcharter. They are currently using this pattern in data_to_boxplot()
:
if (!missing(group_var)) {
dg <- data %>% select({{ group_var }})
}
But using missing()
on arguments that might contain {{
doesn't work. What they should do instead:
# Defuse `group_var` into a quosure so we can inspect it
group_var <- enquo(group_var)
# Inspect the defused quosure with `quo_is_missing()`
if (!quo_is_missing(group_var)) {
# Inject it back into the data-masking expression
dg <- data %>% select(!!group_var)
}
Until this is fixed upstream, you can work around. This is not so trivial because missing values are involved:
hc_boxplot = function(data, x, var, var2, outliers = FALSE, ...) {
arggs = list(...)
# Capture defused expressions so we can inspect them and remove
# missing values
defused_args = enquos(
variable = x,
group_var = var,
group_var2 = var2
)
# Remove any missing arguments
defused_args = Filter(\(x) !rlang::quo_is_missing(x), defused_args)
# We'll inject the compacted list of defused expressions with `!!!`
# back into `data_to_boxplot()`. This is equivalent to using
# `do.call()` for a list of arguments. In data-masking functions,
# only `...` supports `!!!` automatically. To use it on named
# arguments like `variable` and `group_var1`, we need to explicitly
# enable `!!!` with `inject()`.
d = inject(
data_to_boxplot(
data,
!!!defused_args,
add_outliers = outliers,
!!!arggs
)
)
# basic chart
highchart(type = "chart") %>%
hc_add_series_list(d)
}