<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/quasiquotation-in-r-via-bquote/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://www.win-vector.com/blog/2018/10/quasiquotation-in-r-via-bquote/"> R – Win-Vector Blog</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p>In August of 2003 Thomas Lumley added <code>bquote()</code> to <a rel="nofollow" href="https://www.r-project.org/" target="_blank"><code>R</code></a> 1.8.1. This gave <code>R</code> and <code>R</code> users an explicit Lisp-style quasiquotation capability. <code>bquote()</code> and quasiquotation are actually quite powerful. Professor Thomas Lumley should get, and should continue to receive, a lot of credit and thanks for introducing the concept into <code>R</code>.</p>
<p>In fact <code>bquote()</code> is already powerful enough to build a version of <code>dplyr 0.5.0</code> with quasiquotation semantics quite close (from a user perspective) to what is now claimed in <code>tidyeval</code>/<code>rlang</code>.</p>
<p>Let’s take a look at that.</p>
<p><span></span></p>
<p>
<p>For our example problem: suppose we wanted to average a ratio of columns by groups, but want to write code so that what columns are to be used is specified elsewhere (perhaps coming in as function arguments). You would write code such as the following in <code>dplyr 0.7.*</code>:</p>
<div>
<pre><code><span>suppressPackageStartupMessages</span>(<span>library</span>(<span>&quot;dplyr&quot;</span>))

<span># define our parameters</span>
<span># pretend these come from far away</span>
<span># or as function arguments.</span>
group_nm &lt;-<span> </span><span>as.name</span>(<span>&quot;am&quot;</span>)
num_nm &lt;-<span> </span><span>as.name</span>(<span>&quot;hp&quot;</span>)
den_nm &lt;-<span> </span><span>as.name</span>(<span>&quot;cyl&quot;</span>)
derived_nm &lt;-<span> </span><span>as.name</span>(<span>paste0</span>(num_nm, <span>&quot;_per_&quot;</span>, den_nm))
mean_nm &lt;-<span> </span><span>as.name</span>(<span>paste0</span>(<span>&quot;mean_&quot;</span>, derived_nm))
count_nm &lt;-<span> </span><span>as.name</span>(<span>&quot;group_count&quot;</span>)</code></pre>
</div>
<p>And then you would execute something like the following. We are not running the following block (so there is no result), as we have <code>dplyr 0.5.0</code> installed, which does not yet accept the quasiquotation notation.</p>
<div>
<pre><code><span># apply a parameterized pipeline using rlang::!!</span>
mtcars %>%
<span> </span><span>group_by</span>(!!group_nm) %>%
<span> </span><span>mutate</span>(!!derived_nm :<span>=</span><span> </span>!!num_nm/!!den_nm) %>%
<span> </span><span>summarize</span>(
    !!mean_nm :<span>=</span><span> </span><span>mean</span>(!!derived_nm),
    !!count_nm :<span>=</span><span> </span><span>n</span>()
  ) %>%
<span> </span><span>ungroup</span>() %>%
<span> </span><span>arrange</span>(!!group_nm)</code></pre>
</div>
<p>Writing the above code requires two ideas:</p>
<ul>
<li>quasiqotation (marking some symbols for substitution, in this case using <code>!!</code> as the marker). As we said, quasiquotation has been available in <code>R</code> since 2003.</li>
<li>Using <code>:=</code> as a substitute for <code>=</code> to allow substitution marks on the left-hand sides of assignment like expressions without triggering syntax errors. <code>:=</code> is an unused left-over assignment operator that has low-precedence (like assignments do), but not all the syntactic restrictions of <code>=</code>. This is an idea used in <code>data.table</code> for quite some time, and is actually <a rel="nofollow" href="https://github.com/tidyverse/dplyr/blob/bec50d3b1740db1fb28724e7e560cdf19924b97c/R/manip-dt.r" target="_blank">first visible in <code>dplyr</code> when used as part of <code>dplyr</code>‘s <code>data.table</code> adapter in May 2013</a>).</li>
</ul>
<p><code>dplyr 0.5.0</code> (released June 2016) did not yet incorporate quasiqotation, so we can not run the above code in <code>dplyr 0.5.0</code> unless we take the steps of:</p>
<ul>
<li>Replacing the <code>!!x</code> substitution markings with <code>bquote()</code>‘s <code>.(x)</code> notation.</li>
<li>Changing the <code>:=</code> back to <code>=</code>.</li>
<li>Placing the whole pipeline in an <code>eval(bquote())</code> block.</li>
<li>Not directly substituting symbols on the left-hand sides of expressions.</li>
</ul>
<p>This looks like the following.</p>
<div>
<pre><code><span># apply a partially parameterized pipeline using bquote</span>
<span>eval</span>(<span>bquote</span>(
  
mtcars %>%
<span> </span><span>group_by</span>(.(group_nm)) %>%
<span> </span><span>mutate</span>(<span>TMP1 =</span> .(num_nm)/.(den_nm)) %>%
<span> </span><span>rename_</span>(<span>.dots =</span> <span>setNames</span>(<span>&quot;TMP1&quot;</span>, <span>as.character</span>(derived_nm))) %>%
<span> </span><span>summarize</span>(
    <span>TMP2 =</span> <span>mean</span>(.(derived_nm)),
    <span>TMP3 =</span> <span>n</span>()
  ) %>%
<span> </span><span>ungroup</span>() %>%
<span> </span><span>rename_</span>(<span>.dots =</span> <span>setNames</span>(<span>&quot;TMP2&quot;</span>, <span>as.character</span>(mean_nm))) %>%
<span> </span><span>rename_</span>(<span>.dots =</span> <span>setNames</span>(<span>&quot;TMP3&quot;</span>, <span>as.character</span>(count_nm))) %>%
<span> </span><span>arrange</span>(.(group_nm))

))</code></pre>
</div>
<pre><code>## # A tibble: 2 x 3
##      am mean_hp_per_cyl group_count
##   </code></pre>