<p><div><div><div><div data-width="100" data-show-faces="false" data-send="true" data-layout="button_count" data-height="21" data-href="https://www.r-bloggers.com/on-the-ifelse-function/"></div></div><div><a data-url="https://www.r-bloggers.com/on-the-ifelse-function/" data-counturl="https://www.r-bloggers.com/on-the-ifelse-function/" data-count="horizontal" data-text="On the ifelse function" class="twitter-share-button" data-via="rbloggers" href="https://twitter.com/share"></a></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://privefl.github.io/blog/On-the-ifelse-function/"> Florian Privé</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<section>
<p>In this post, I will talk about the <a target="_blank" href="https://stat.ethz.ch/R-manual/R-devel/library/base/html/ifelse.html"><strong>ifelse</strong> function</a>, which behaviour can be easily misunderstood, as pointed out in <a target="_blank" href="http://stackoverflow.com/questions/40026975/subsetting-with-negative-indices-best-practices">my latest question on SO</a>. I will try to show how it can be used, and misued. We will also check if it is as fast as we could expect from a vectorized base function of R.</p>
<div>
<h2>How can it be used?</h2>
<p>The first example comes directly from the R documentation:</p>
<div>
<pre><code>x &lt;-<span> </span><span>c</span>(<span>6</span>:-<span>4</span>)
<span>sqrt</span>(x)  <span>#- gives warning</span></code></pre>
</div>
<pre><code>## Warning in sqrt(x): NaNs produced</code></pre>
<pre><code>##  [1] 2.449490 2.236068 2.000000 1.732051 1.414214 1.000000 0.000000      NaN      NaN
## [10]      NaN      NaN</code></pre>
<div>
<pre><code><span>sqrt</span>(<span>ifelse</span>(x >=<span> </span><span>0</span>, x, <span>NA</span>))  <span># no warning</span></code></pre>
</div>
<pre><code>##  [1] 2.449490 2.236068 2.000000 1.732051 1.414214 1.000000 0.000000       NA       NA
## [10]       NA       NA</code></pre>
<p>So, it can be used, for instance, to handle special cases, in a vectorized, succinct way.</p>
<p>The second example comes from the <a target="_blank" href="https://cran.r-project.org/web/packages/Rcpp/vignettes/Rcpp-sugar.pdf">vignette of Rcpp Sugar</a>:</p>
<div>
<pre><code>foo &lt;-<span> </span>function(x, y) {
  <span>ifelse</span>(x &lt;<span> </span>y, x*x, -(y*y))
}
<span>foo</span>(<span>1</span>:<span>5</span>, <span>5</span>:<span>1</span>)</code></pre>
</div>
<pre><code>## [1]  1  4 -9 -4 -1</code></pre>
<p>So, it can be used to construct a vector, by doing an element-wise comparison of two vectors, and specifying a custom output for each comparison.</p>
<p>A last example, just for the pleasure:</p>
<div>
<pre><code>(a &lt;-<span> </span><span>matrix</span>(<span>1</span>:<span>9</span>, <span>3</span>, <span>3</span>))</code></pre>
</div>
<pre><code>##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9</code></pre>
<div>
<pre><code><span>ifelse</span>(a %%<span> </span><span>2</span> ==<span> </span><span>0</span>, a, <span>0</span>)</code></pre>
</div>
<pre><code>##      [,1] [,2] [,3]
## [1,]    0    4    0
## [2,]    2    0    8
## [3,]    0    6    0</code></pre>
</div>
<div>
<h2>How can it be misused?</h2>
<p>I think many people think they can use <code>ifelse</code> as a shorter way of writing an <code>if-then-else</code> statement (this is a mistake I made). For example, I use:</p>
<div>
<pre><code>legend.pos &lt;-<span> </span><span>ifelse</span>(is.top, <span>ifelse</span>(is.right, <span>&quot;topright&quot;</span>, <span>&quot;topleft&quot;</span>),
                     <span>ifelse</span>(is.right, <span>&quot;bottomright&quot;</span>, <span>&quot;bottomleft&quot;</span>))</code></pre>
</div>
<p>instead of:</p>
<div>
<pre><code>if (is.top) {
  if (is.right) {
    legend.pos &lt;-<span> &quot;topright&quot;</span>
  } else {
    legend.pos &lt;-<span> &quot;topleft&quot;</span>
  }
} else {
  if (is.right) {
    legend.pos &lt;-<span> &quot;bottomright&quot;</span>
  } else {
    legend.pos &lt;-<span> &quot;bottomleft&quot;</span>
  }
}</code></pre>
</div>
<p>That works, but this doesn’t:</p>
<div>
<pre><code><span>ifelse</span>(<span>FALSE</span>, <span>0</span>, <span>1</span>:<span>5</span>)</code></pre>
</div>
<pre><code>## [1] 1</code></pre>
<p>Indeed, if you read carefully the R documentation, you see that <code>ifelse</code> is returning a vector of the same length and attributes as the condition (here, of length 1).</p>
<p>If you really want to use a more succinct notation, you could use</p>
<div>
<pre><code><span>`</span><span>if</span><span>`</span>(<span>FALSE</span>, <span>0</span>, <span>1</span>:<span>5</span>)</code></pre>
</div>
<pre><code>## [1] 1 2 3 4 5</code></pre>
<p>If you’re not familiar with this notation, I suggest you read <a target="_blank" href="http://adv-r.had.co.nz/Functions.html">the chapter about functions in book <em>Advanced R</em></a>.</p>
</div>
<div>
<h2>Benchmarks</h2>
<div>
<h3>Reimplementing ‘abs’</h3>
<div>
<pre><code>abs2 &lt;-<span> </span>function(x) {
  <span>ifelse</span>(x &lt;<span> </span><span>0</span>, -x, x)
}
<span>abs2</span>(-<span>5</span>:<span>5</span>)</code></pre>
</div>
<pre><code>##  [1] 5 4 3 2 1 0 1 2 3 4 5</code></pre>
<div>
<pre><code><span>library</span>(microbenchmark)
x &lt;-<span> </span><span>rnorm</span>(<span>1e4</span>)

<span>print</span>(<span>microbenchmark</span>(
  <span>abs</span>(x), 
  <span>abs2</span>(x)
))</code></pre>
</div>
<pre><code>## Unit: microseconds
##     expr     min       lq       mean   median      uq       max neval
##   abs(x)   3.973   5.2975   36.19779   6.9530   9.271  1613.386   100
##  abs2(x) 496.299 523.9450 1595.51016 549.7695 634.859 80076.957   100</code></pre>
</div>
<div>
<h3>Comparing with C++</h3>
<p>Consider the Rcpp Sugar example again, 4 means to compute it:</p>
<div>
<pre><code><span>#include &lt;Rcpp.h></span>
<span>using</span> <span>namespace</span> Rcpp;

<span>// [[Rcpp::export]]</span>
NumericVector fooRcpp(<span>const</span> NumericVector&amp; x, <span>const</span> NumericVector&amp; y) {
  <span>int</span> n = x.size();
  NumericVector res(n);
  <span>double</span> x_, y_;
  <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; n; i++) { 
    x_ = x[i];
    y_ = y[i];
    <span>if</span> (x_ &lt; y_) {
      res[i] = x_*x_;
    } <span>else</span> {
      res[i] = -(y_*y_);
    }
  }
  <span>return</span> res;
}</code></pre>
</div>
<div>
<pre><code><span>fooRcpp</span>(<span>1</span>:<span>5</span>, <span>5</span>:<span>1</span>)</code></pre>
</div>
<pre><code>## [1]  1  4 -9 -4 -1</code></pre>
<div>
<pre><code><span>#include &lt;Rcpp.h></span>
<span>using</span> <span>namespace</span> Rcpp;

<span>// [[Rcpp::export]]</span>
NumericVector fooRcppSugar(<span>const</span> NumericVector&amp; x, 
                           <span>const</span> NumericVector&amp; y) {
  <span>return</span> ifelse(x &lt; y, x*x, -(y*y));
}</code></pre>
</div>
<div>
<pre><code><span>fooRcppSugar</span>(<span>1</span>:<span>5</span>, <span>5</span>:<span>1</span>)</code></pre>
</div>
<pre><code>## [1]  1  4 -9 -4 -1</code></pre>
<div>
<pre><code>foo2 &lt;-<span> </span>function(x, y) {
  cond &lt;-<span> </span>(x &lt;<span> </span>y)
  cond *<span> </span>x^<span>2</span> -<span> </span>(<span>1</span> -<span> </span>cond) *<span> </span>y^<span>2</span>
}
<span>foo2</span>(<span>1</span>:<span>5</span>, <span>5</span>:<span>1</span>)</code></pre>
</div>
<pre><code>## [1]  1  4 -9 -4 -1</code></pre>
<div>
<pre><code>x &lt;-<span> </span><span>rnorm</span>(<span>1e4</span>)
y &lt;-<span> </span><span>rnorm</span>(<span>1e4</span>)
<span>print</span>(<span>microbenchmark</span>(
  <span>foo</span>(x, y),
  <span>foo2</span>(x, y),
  <span>fooRcpp</span>(x, y),
  <span>fooRcppSugar</span>(x, y)
))</code></pre>
</div>
<pre><code>## Unit: microseconds
##                expr     min       lq      mean  median       uq      max neval
##           foo(x, y) 510.535 542.6510 872.23474 563.510 716.9680 2439.447   100
##          foo2(x, y)  71.183  75.1560 147.17468  83.765  93.8635 1977.250   100
##       fooRcpp(x, y)  40.393  44.6970  63.59186  47.676  51.1535 1468.038   100
##  fooRcppSugar(x, y) 138.394 141.3745 179.16429 142.533 161.4045 1575.972   100</code></pre>
<p>Even if it is a vectorized base R function, <code>ifelse</code> is known to be slow.</p>
</div>
</div>
<div>
<h2>Conclusion</h2>
<p>Beware when you use the <code>ifelse</code> function. Moreover, if you make a substantial number of calls to it, be aware that it isn’t very fast, but it exists at least 3 faster alternatives to it.</p>
</div>
</section>
<p><div><div><div><div data-width="55" data-show-faces="false" data-send="true" data-layout="box_count" data-height="62" data-href="https://www.r-bloggers.com/on-the-ifelse-function/"></div></div><div><a data-url="https://www.r-bloggers.com/on-the-ifelse-function/" data-counturl="https://www.r-bloggers.com/on-the-ifelse-function/" data-count="vertical" data-text="On the ifelse function" class="twitter-share-button" data-via="rbloggers" href="https://twitter.com/share"></a></div><div></div></div><div></div></div>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://privefl.github.io/blog/On-the-ifelse-function/"> Florian Privé</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 title="The R Project for Statistical Computing" rel="nofollow" href="https://www.r-project.org/">R</a> news and <a title="R tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=tutorial">tutorials</a> on topics such as: <a title="Data science" rel="nofollow" href="https://www.r-bloggers.com/?s=data%20science">Data science</a>, <a title="Big Data" rel="nofollow" href="https://www.r-bloggers.com/?s=Big%20Data">Big Data, <a title="R jobs" rel="nofollow" href="https://www.r-users.com/">R jobs</a>, visualization (<a title="ggplot and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=ggplot2">ggplot2</a>, <a title="Boxplots using lattice and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=boxplot">Boxplots</a>, <a title="Maps and gis" rel="nofollow" href="https://www.r-bloggers.com/?s=map">maps</a>, <a title="Animation in R" rel="nofollow" href="https://www.r-bloggers.com/?s=animation">animation</a>), programming (<a title="RStudio IDE for R" rel="nofollow" href="https://www.r-bloggers.com/?s=RStudio">RStudio</a>, <a title="Sweave and literate programming" rel="nofollow" href="https://www.r-bloggers.com/?s=sweave">Sweave</a>, <a title="LaTeX in R" rel="nofollow" href="https://www.r-bloggers.com/?s=LaTeX">LaTeX</a>, <a title="SQL and databases" rel="nofollow" href="https://www.r-bloggers.com/?s=SQL">SQL</a>, <a title="Eclipse IDE for R" rel="nofollow" href="https://www.r-bloggers.com/?s=eclipse">Eclipse</a>, <a title="git and github, Version Control System" rel="nofollow" href="https://www.r-bloggers.com/?s=git">git</a>, <a title="Large data in R using Hadoop" rel="nofollow" href="https://www.r-bloggers.com/?s=hadoop">hadoop</a>, <a title="Web Scraping of google, facebook, yahoo, twitter and more using R" rel="nofollow" href="https://www.r-bloggers.com/?s=Web+Scraping">Web Scraping</a>) statistics (<a title="Regressions and ANOVA analysis tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=regression">regression</a>, <a title="principal component analysis tutorial" rel="nofollow" href="https://www.r-bloggers.com/?s=PCA">PCA</a>, <a title="Time series" rel="nofollow" href="https://www.r-bloggers.com/?s=time+series">time series</a>, <a title="finance trading" rel="nofollow" href="https://www.r-bloggers.com/?s=trading">trading</a>) and more...
<img height="1" alt="" width="1" src="http://feeds.feedburner.com/~r/RBloggers/~4/g9_zI7XYH9U"></a></div>