EVOLUTION-MANAGER
Edit File: splice-operator.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>R: Splice operator !!!</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" type="text/css" href="R.css" /> </head><body> <table width="100%" summary="page for splice-operator {rlang}"><tr><td>splice-operator {rlang}</td><td style="text-align: right;">R Documentation</td></tr></table> <h2>Splice operator <code style="white-space: pre;">!!!</code></h2> <h3>Description</h3> <p>The splice operator <code style="white-space: pre;">!!!</code> implemented in <a href="dyn-dots.html">dynamic dots</a> injects a list of arguments into a function call. It belongs to the family of <a href="topic-inject.html">injection</a> operators and provides the same functionality as <code><a href="../../base/html/do.call.html">do.call()</a></code>. </p> <p>The two main cases for splice injection are: </p> <ul> <li><p> Turning a list of inputs into distinct arguments. This is especially useful with functions that take data in <code>...</code>, such as <code><a href="../../base/html/cbind.html">base::rbind()</a></code>. </p> <div class="sourceCode r"><pre>dfs <- list(mtcars, mtcars) inject(rbind(!!!mtcars)) </pre></div> </li> <li><p> Injecting <a href="topic-defuse.html">defused expressions</a> like <a href="sym.html">symbolised</a> column names. </p> <p>For tidyverse APIs, this second case is no longer as useful since dplyr 1.0 and the <code>across()</code> operator. </p> </li></ul> <h3>Where does <code style="white-space: pre;">!!!</code> work?</h3> <p><code style="white-space: pre;">!!!</code> does not work everywhere, you can only use it within certain special functions: </p> <ul> <li><p> Functions taking <a href="dyn-dots.html">dynamic dots</a> like <code><a href="list2.html">list2()</a></code>. </p> </li> <li><p> Functions taking <a href="topic-defuse.html">defused</a> and <a href="topic-data-mask.html">data-masked</a> arguments, which are dynamic by default. </p> </li> <li><p> Inside <code><a href="inject.html">inject()</a></code>. </p> </li></ul> <p>Most tidyverse functions support <code style="white-space: pre;">!!!</code> out of the box. With base functions you need to use <code><a href="inject.html">inject()</a></code> to enable <code style="white-space: pre;">!!!</code>. </p> <p>Using the operator out of context may lead to incorrect results, see <a href="topic-inject-out-of-context.html">What happens if I use injection operators out of context?</a>. </p> <h3>Splicing a list of arguments</h3> <p>Take a function like <code><a href="../../base/html/cbind.html">base::rbind()</a></code> that takes data in <code>...</code>. This sort of functions takes a variable number of arguments. </p> <div class="sourceCode r"><pre>df1 <- data.frame(x = 1) df2 <- data.frame(x = 2) rbind(df1, df2) #> x #> 1 1 #> 2 2 </pre></div> <p>Passing individual arguments is only possible for a fixed amount of arguments. When the arguments are in a list whose length is variable (and potentially very large), we need a programmatic approach like the splicing syntax <code style="white-space: pre;">!!!</code>: </p> <div class="sourceCode r"><pre>dfs <- list(df1, df2) inject(rbind(!!!dfs)) #> x #> 1 1 #> 2 2 </pre></div> <p>Because <code>rbind()</code> is a base function we used <code><a href="inject.html">inject()</a></code> to explicitly enable <code style="white-space: pre;">!!!</code>. However, many functions implement <a href="list2.html">dynamic dots</a> with <code style="white-space: pre;">!!!</code> implicitly enabled out of the box. </p> <div class="sourceCode r"><pre>tidyr::expand_grid(x = 1:2, y = c("a", "b")) #> # A tibble: 4 x 2 #> x y #> <int> <chr> #> 1 1 a #> 2 1 b #> 3 2 a #> 4 2 b xs <- list(x = 1:2, y = c("a", "b")) tidyr::expand_grid(!!!xs) #> # A tibble: 4 x 2 #> x y #> <int> <chr> #> 1 1 a #> 2 1 b #> 3 2 a #> 4 2 b </pre></div> <p>Note how the expanded grid has the right column names. That's because we spliced a <em>named</em> list. Splicing causes each name of the list to become an argument name. </p> <div class="sourceCode r"><pre>tidyr::expand_grid(!!!set_names(xs, toupper)) #> # A tibble: 4 x 2 #> X Y #> <int> <chr> #> 1 1 a #> 2 1 b #> 3 2 a #> 4 2 b </pre></div> <h3>Splicing a list of expressions</h3> <p>Another usage for <code style="white-space: pre;">!!!</code> is to inject <a href="topic-defuse.html">defused expressions</a> into <a href="topic-data-mask.html">data-masked</a> dots. However this usage is no longer a common pattern for programming with tidyverse functions and we recommend using other patterns if possible. </p> <p>First, instead of using the <a href="topic-data-mask-programming.html">defuse-and-inject pattern</a> with <code>...</code>, you can simply pass them on as you normally would. These two expressions are completely equivalent: </p> <div class="sourceCode r"><pre>my_group_by <- function(.data, ...) { .data %>% dplyr::group_by(!!!enquos(...)) } # This equivalent syntax is preferred my_group_by <- function(.data, ...) { .data %>% dplyr::group_by(...) } </pre></div> <p>Second, more complex applications such as <a href="topic-metaprogramming.html">transformation patterns</a> can be solved with the <code>across()</code> operation introduced in dplyr 1.0. Say you want to take the <code>mean()</code> of all expressions in <code>...</code>. Before <code>across()</code>, you had to defuse the <code>...</code> expressions, wrap them in a call to <code>mean()</code>, and inject them in <code>summarise()</code>. </p> <div class="sourceCode r"><pre>my_mean <- function(.data, ...) { # Defuse dots and auto-name them exprs <- enquos(..., .named = TRUE) # Wrap the expressions in a call to `mean()` exprs <- purrr::map(exprs, ~ call("mean", .x, na.rm = TRUE)) # Inject them .data %>% dplyr::summarise(!!!exprs) } </pre></div> <p>It is much easier to use <code>across()</code> instead: </p> <div class="sourceCode r"><pre>my_mean <- function(.data, ...) { .data %>% dplyr::summarise(across(c(...), ~ mean(.x, na.rm = TRUE))) } </pre></div> <h3>Performance of injected dots and dynamic dots</h3> <p>Take this <a href="dyn-dots.html">dynamic dots</a> function: </p> <div class="sourceCode r"><pre>n_args <- function(...) { length(list2(...)) } </pre></div> <p>Because it takes dynamic dots you can splice with <code style="white-space: pre;">!!!</code> out of the box. </p> <div class="sourceCode r"><pre>n_args(1, 2) #> [1] 2 n_args(!!!mtcars) #> [1] 11 </pre></div> <p>Equivalently you could enable <code style="white-space: pre;">!!!</code> explicitly with <code><a href="inject.html">inject()</a></code>. </p> <div class="sourceCode r"><pre>inject(n_args(!!!mtcars)) #> [1] 11 </pre></div> <p>While the result is the same, what is going on under the hood is completely different. <code><a href="list2.html">list2()</a></code> is a dots collector that special-cases <code style="white-space: pre;">!!!</code> arguments. On the other hand, <code><a href="inject.html">inject()</a></code> operates on the language and creates a function call containing as many arguments as there are elements in the spliced list. If you supply a list of size 1e6, <code>inject()</code> is creating one million arguments before evaluation. This can be much slower. </p> <div class="sourceCode r"><pre>xs <- rep(list(1), 1e6) system.time( n_args(!!!xs) ) #> user system elapsed #> 0.009 0.000 0.009 system.time( inject(n_args(!!!xs)) ) #> user system elapsed #> 0.445 0.012 0.457 </pre></div> <p>The same issue occurs when functions taking dynamic dots are called inside a data-masking function like <code>dplyr::mutate()</code>. The mechanism that enables <code style="white-space: pre;">!!!</code> injection in these arguments is the same as in <code>inject()</code>. </p> <h3>See Also</h3> <ul> <li> <p><a href="topic-inject.html">Injecting with !!, !!!, and glue syntax</a> </p> </li> <li> <p><code><a href="inject.html">inject()</a></code> </p> </li> <li> <p><code><a href="exec.html">exec()</a></code> </p> </li></ul> <hr /><div style="text-align: center;">[Package <em>rlang</em> version 1.0.6 <a href="00Index.html">Index</a>]</div> </body></html>