<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/non-standard-evaluation-and-function-composition-in-r/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://www.win-vector.com/blog/2017/06/non-standard-evaluation-and-function-composition-in-r/"> 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 this article we will discuss composing standard-evaluation interfaces (SE) and composing non-standard-evaluation interfaces (NSE) in <a rel="nofollow" href="https://www.r-project.org/" target="_blank"><code>R</code></a>.</p>
<p>In <a rel="nofollow" href="https://www.r-project.org/" target="_blank"><code>R</code></a> the package <a rel="nofollow" href="https://cran.r-project.org/package=rlang" target="_blank"><code>tidyeval</code>/<code>rlang</code></a> is a tool for building domain specific languages intended to allow easier composition of NSE interfaces.</p>
<p>To use it you must know some of its structure and notation. Here are some details paraphrased from the major <code>tidyeval</code>/<code>rlang</code> client, the package dplyr: <a rel="nofollow" href="https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html" target="_blank"><code>vignette('programming', package = 'dplyr')</code></a>).</p>
<ul>
<li>&quot;<code>:=</code>&quot; is needed to make left-hand-side re-mapping possible (adding yet another &quot;more than one assignment type operator running around&quot; notation issue).</li>
<li>&quot;<code>!!</code>&quot; substitution requires parenthesis to safely bind (so the notation is actually &quot;<code>(!! )</code>&quot;, not &quot;<code>!!</code>&quot;).</li>
<li>Left-hand-sides of expressions are names or strings, while right-hand-sides are <code>quosures</code>/expressions.</li>
</ul>
<p><span></span></p>
<h2>Example</h2>
<p>Let’s apply <code>tidyeval</code>/<code>rlang</code> notation to the task of building re-usable generic in <a rel="nofollow" href="https://www.r-project.org/" target="_blank"><code>R</code></a>.</p>
<div>
<pre><code><span># setup</span>
<span>suppressPackageStartupMessages</span>(<span>library</span>(<span>&quot;dplyr&quot;</span>))
<span>packageVersion</span>(<span>&quot;dplyr&quot;</span>)</code></pre>
</div>
<pre><code>## [1] '0.7.0'</code></pre>
<p><a rel="nofollow" href="https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html" target="_blank"><code>vignette('programming', package = 'dplyr')</code></a> includes the following example:</p>
<div>
<pre><code>my_mutate &lt;-<span> </span>function(df, expr) {
  expr &lt;-<span> </span><span>enquo</span>(expr)
  mean_name &lt;-<span> </span><span>paste0</span>(<span>&quot;mean_&quot;</span>, <span>quo_name</span>(expr))
  sum_name &lt;-<span> </span><span>paste0</span>(<span>&quot;sum_&quot;</span>, <span>quo_name</span>(expr))

  <span>mutate</span>(df, 
    !!mean_name :<span>=</span><span> </span><span>mean</span>(!!expr), 
    !!sum_name :<span>=</span><span> </span><span>sum</span>(!!expr)
  )
}</code></pre>
</div>
<p>We can try this:</p>
<div>
<pre><code>d &lt;-<span> </span><span>data.frame</span>(<span>a=</span><span>1</span>)
<span>my_mutate</span>(d, a)</code></pre>
</div>
<pre><code>##   a mean_a sum_a
## 1 1      1     1</code></pre>
<h3>SE Example</h3>
<p>From this example we can figure out how to use <code>tidyeval</code>/<code>rlang</code> notation to build a standard interface version of a function that adds one to a column and lands the value in an arbitrary column:</p>
<div>
<pre><code>tidy_add_one_se &lt;-<span> </span>function(df, res_var_name, input_var_name) {
  input_var &lt;-<span> </span><span>as.name</span>(input_var_name)
  res_var &lt;-<span> </span>res_var_name
  <span>mutate</span>(df,
         !!res_var :<span>=</span><span> </span>(!!input_var) +<span> </span><span>1</span>)
}

<span>tidy_add_one_se</span>(d, <span>'res'</span>, <span>'a'</span>)</code></pre>
</div>
<pre><code>##   a res
## 1 1   2</code></pre>
<p>And we can re-wrap <code>tidy_add_one_se</code> as into a &quot;add one to self&quot; function as we show here:</p>
<div>
<pre><code>tidy_increment_se &lt;-<span> </span>function(df, var_name) {
  <span>tidy_add_one_se</span>(df, var_name, var_name)
}

<span>tidy_increment_se</span>(d, <span>'a'</span>)</code></pre>
</div>
<pre><code>##   a
## 1 2</code></pre>
<h3>NSE Example</h3>
<p>We can also use the <code>tidyeval</code>/<code>rlang</code> notation more as it is intended: to wrap or compose a non-standard interface in another non-standard interface.</p>
<div>
<pre><code>tidy_add_one_nse &lt;-<span> </span>function(df, res_var, input_var) {
  input_var &lt;-<span> </span><span>enquo</span>(input_var)
  res_var &lt;-<span> </span><span>quo_name</span>(<span>enquo</span>(res_var))
  <span>mutate</span>(df,
         !!res_var :<span>=</span><span> </span>(!!input_var) +<span> </span><span>1</span>)
}

<span>tidy_add_one_nse</span>(d, res, a)</code></pre>
</div>
<pre><code>##   a res
## 1 1   2</code></pre>
<p>And we even wrap this again as a new &quot;add one to self&quot; function:</p>
<div>
<pre><code>tidy_increment_nse &lt;-<span> </span>function(df, var) {
  var &lt;-<span> </span><span>enquo</span>(var)
  <span>tidy_add_one_nse</span>(df, !!var, !!var)
}

<span>tidy_increment_nse</span>(d, a)</code></pre>
</div>
<pre><code>##   a
## 1 2</code></pre>
<p>(The above <code>enquo()</code> then &quot;<code>!!</code>&quot; pattern is pretty much necissary, as the simpler idea of just passing <code>var</code> through doesn’t work.)</p>
<h4>An Issue</h4>
<p>We could try use <code>base::substitute()</code> instead of <code>quo_name(enquo())</code> in the non-standard-evaluation wrapper. At first this appears to work, but it runs into trouble when we try to compose non-standard-evaluation functions with each other.</p>
<div>
<pre><code>tidy_add_one_nse_subs &lt;-<span> </span>function(df, res_var, input_var) {
  input_var &lt;-<span> </span><span>enquo</span>(input_var)
  res_var &lt;-<span> </span><span>substitute</span>(res_var)
  <span>mutate</span>(df,
         !!res_var :<span>=</span><span> </span>(!!input_var) +<span> </span><span>1</span>)
}

<span>tidy_add_one_nse_subs</span>(d, res, a)</code></pre>
</div>
<pre><code>##   a res
## 1 1   2</code></pre>
<p>However this seemingly similar variation is not re-composable in the same manner.</p>
<div>
<pre><code>tidy_increment_nse_subs &lt;-<span> </span>function(df, var) {
  var &lt;-<span> </span><span>enquo</span>(var)
  <span>tidy_add_one_nse_subs</span>(df, !!var, !!var)
}

<span>tidy_increment_nse_subs</span>(d, a)</code></pre>
</div>
<pre><code>## Error: LHS must be a name or string</code></pre>
<p>Likely there is some way to get this to work, but my point is:</p>
<ul>
<li>The obvious way didn’t work.</li>
<li>Some NSE functions can’t be re-used in standard NSE composition. You may not know which ones those are ahead of time. Presumably functions from major packages are so-vetted, but you may not be able to trust &quot;one off compositions&quot; to be safe to re-compose.</li>
</ul>
<h2>wrapr::let</h2>
<p>It is easy to specify the function we want with <a rel="nofollow" href="https://cran.r-project.org/package=wrapr" target="_blank"><code>wrapr</code></a> as follows (both using standard evaluation, and using non-standard evaluation):</p>
<h3>SE version</h3>
<div>
<pre><code><span>library</span>(<span>&quot;wrapr&quot;</span>)

wrapr_add_one_se &lt;-<span> </span>function(df, res_var_name, input_var_name) {
  wrapr::<span>let</span>(
    <span>c</span>(<span>RESVAR=</span> res_var_name,
      <span>INPUTVAR=</span> input_var_name),
    df %>%
<span> </span><span>mutate</span>(<span>RESVAR =</span> INPUTVAR +<span> </span><span>1</span>)
  )
}

<span>wrapr_add_one_se</span>(d, <span>'res'</span>, <span>'a'</span>)</code></pre>
</div>
<pre><code>##   a res
## 1 1   2</code></pre>
<p>Standard composition:</p>
<div>
<pre><code>wrapr_increment_se &lt;-<span> </span>function(df, var_name) {
  <span>wrapr_add_one_se</span>(df, var_name, var_name)
}

<span>wrapr_increment_se</span>(d, <span>'a'</span>)</code></pre>
</div>
<pre><code>##   a
## 1 2</code></pre>
<h3>NSE version</h3>
<p>Non-standard evaluation interface:</p>
<div>
<pre><code>wrapr_add_one_nse &lt;-<span> </span>function(df, res_var, input_var) {
  wrapr::<span>let</span>(
    <span>c</span>(<span>RESVAR=</span> <span>substitute</span>(res_var),
      <span>INPUTVAR=</span> <span>substitute</span>(input_var)),
    df %>%
<span> </span><span>mutate</span>(<span>RESVAR =</span> INPUTVAR +<span> </span><span>1</span>)
  )
}

<span>wrapr_add_one_nse</span>(d, res, a)</code></pre>
</div>
<pre><code>##   a res
## 1 1   2</code></pre>
<p><code>wrapr::let()</code>‘s NSE composition pattern seems to work even when applied to itself:</p>
<div>
<pre><code>wrapr_increment_nse &lt;-<span> </span>function(df, var) {
  wrapr::<span>let</span>(
    <span>c</span>(<span>VAR=</span> <span>substitute</span>(var)),
    <span>wrapr_add_one_nse</span>(df, VAR, VAR)
  )
}

<span>wrapr_increment_nse</span>(d, a)</code></pre>
</div>
<pre><code>##   a
## 1 2</code></pre>
<h3>Abstract Syntax Tree Version</h3>
<p>Or, if you are uncomfortable with macros being implemented through string-substitution one can use <code>wrapr::let()</code> in &quot;language mode&quot; (where it works directly on abstract syntax trees).</p>
<h4>SE re-do</h4>
<div>
<pre><code>wrapr_add_one_se &lt;-<span> </span>function(df, res_var_name, input_var_name) {
  wrapr::<span>let</span>(
    <span>c</span>(<span>RESVAR=</span> res_var_name,
      <span>INPUTVAR=</span> input_var_name),
    df %>%
<span> </span><span>mutate</span>(<span>RESVAR =</span> INPUTVAR +<span> </span><span>1</span>),
    <span>subsMethod=</span> <span>'langsubs'</span>
  )
}

<span>wrapr_add_one_se</span>(d, <span>'res'</span>, <span>'a'</span>)</code></pre>
</div>
<pre><code>##   a res
## 1 1   2</code></pre>
<div>
<pre><code>wrapr_increment_se &lt;-<span> </span>function(df, var_name) {
  <span>wrapr_add_one_se</span>(df, var_name, var_name)
}

<span>wrapr_increment_se</span>(d, <span>'a'</span>)</code></pre>
</div>
<pre><code>##   a
## 1 2</code></pre>
<h3>NSE re-do</h3>
<div>
<pre><code>wrapr_add_one_nse &lt;-<span> </span>function(df, res_var, input_var) {
  wrapr::<span>let</span>(
    <span>c</span>(<span>RESVAR=</span> <span>substitute</span>(res_var),
      <span>INPUTVAR=</span> <span>substitute</span>(input_var)),
    df %>%
<span> </span><span>mutate</span>(<span>RESVAR =</span> INPUTVAR +<span> </span><span>1</span>),
    <span>subsMethod=</span> <span>'langsubs'</span>
  )
}

<span>wrapr_add_one_nse</span>(d, res, a)</code></pre>
</div>
<pre><code>##   a res
## 1 1   2</code></pre>
<div>
<pre><code>wrapr_increment_nse &lt;-<span> </span>function(df, var) {
  wrapr::<span>let</span>(
    <span>c</span>(<span>VAR=</span> <span>substitute</span>(var)),
    <span>wrapr_add_one_nse</span>(df, VAR, VAR),
    <span>subsMethod=</span> <span>'langsubs'</span>
  )
}

<span>wrapr_increment_nse</span>(d, a)</code></pre>
</div>
<pre><code>##   a
## 1 2</code></pre>
<h2>Conclusion</h2>
<p><code>tidyeval</code>/<code>rlang</code> provides general tools to compose or daisy-chain non-standard-evaluation functions (i.e., write new non-standard-evaluation functions in terms of others. This tries to abrogate the issue that it can be hard to compose non-standard function interfaces (i.e., one can not <a rel="nofollow" href="https://www.youtube.com/watch?v=iKLGxzzm9Hk" target="_blank">parameterize them or program over them</a> without a tool such as <code>tidyeval</code>/<code>rlang</code>). In contrast <code>wrapr::let()</code> concentrates on standard evaluation, providing a tool that allows one to re-wrap non-standard-evaluation interfaces as standard evaluation interfaces.</p>
<p>A lot of the <code>tidyeval</code>/<code>rlang</code> design is centered on treating variable names as lexical closures that capture an environment they should be evaluated in. This does make them more like general <code>R</code> functions (which also have this behavior).</p>
<p>However, creating so many long-term bindings is actually counter to some common data analyst practice.</p>
<p>The <code>my_mutate(df, expr)</code> example itself from <code>vignette('programming', package = 'dplyr')</code> even shows the pattern I am referring to: the analyst transiently pairs a chosen concrete data set to abstract variable names. One argument is the data and the other is the expression to be applied to that data (and only that data, with clean code not capturing values from environments).</p>
<p>Many methods are written expecting to be re-run on different data (for example <code>predict()</code>). This has the huge advantage that it documents your intent to change out what data is being applied (such as running a procedure twice, once on training data and once on future application data).</p>
<p>This is a principle we also strongly apply in our <a rel="nofollow" href="http://www.win-vector.com/blog/2017/06/use-a-join-controller-to-document-your-work/" target="_blank">join controller</a> which has no issue sharing variables out as an external spreadsheet, because it thinks of variable names (here meaning columns names) as fundamentally being strings (not as <code>quosures</code> temporally working &quot;under cover&quot; in string representations).</p>
	<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="http://www.win-vector.com/blog/2017/06/non-standard-evaluation-and-function-composition-in-r/"> R – Win-Vector Blog</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a rel="nofollow" href="https://www.r-project.org/" title="The R Project for Statistical Computing">R</a> news and <a rel="nofollow" href="https://www.r-bloggers.com/search/tutorial" title="R tutorials">tutorials</a> on topics such as: <a rel="nofollow" href="https://www.r-bloggers.com/search/data%20science" title="Data science">Data science</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data" title="Big Data">Big Data, <a rel="nofollow" href="https://www.r-users.com/" title="R jobs">R jobs</a>, visualization (<a rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2" title="ggplot and ggplot2 tutorials">ggplot2</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/boxplot" title="Boxplots using lattice and ggplot2 tutorials">Boxplots</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/map" title="Maps and gis">maps</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/animation" title="Animation in R">animation</a>), programming (<a rel="nofollow" href="https://www.r-bloggers.com/search/RStudio" title="RStudio IDE for R">RStudio</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/sweave" title="Sweave and literate programming">Sweave</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX" title="LaTeX in R">LaTeX</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/SQL" title="SQL and databases">SQL</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/eclipse" title="Eclipse IDE for R">Eclipse</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/git" title="git and github, Version Control System">git</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/hadoop" title="Large data in R using Hadoop">hadoop</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping" title="Web Scraping of google, facebook, yahoo, twitter and more using R">Web Scraping</a>) statistics (<a rel="nofollow" href="https://www.r-bloggers.com/search/regression" title="Regressions and ANOVA analysis tutorials">regression</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/PCA" title="principal component analysis tutorial">PCA</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/time+series" title="Time series">time series</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/trading" title="finance trading">trading</a>) and more...
<img width="1" alt="" src="http://feeds.feedburner.com/~r/RBloggers/~4/fkMOodws3Qk" height="1"></a></div>