sugar Functions in Rcpp Package in R (Example)

 

You can make your R code more efficient by integrating C++ code via the Rcpp package. However, the C++ programming language which you have to use for this can be tricky to learn.

Luckily, there is Rcpp sugar. Rcpp sugar is implemented in Rcpp and gives you a list of operators and functions with which you can write R-like vector operations in C++.

In this post, we show you some examples. If you did not work with the Rcpp package before, take a look at our blog post about Rcpp first.

We cover the following topics.

Let’s get to the sugar!

 

What is Rcpp sugar?

Rcpp sugar comes with Rcpp and is a collection of different functions and operators. These allow you to write R-like vector operations in C++ code. So as an R-user the Rcpp sugar code is typically easier to learn and understand than native C++ code.

We show you some examples of Rcpp sugar based on its description by Dirk Eddelbuettel and Romain François which you can find here. In particular, Rcpp sugar allows us to write vector-wise operations like in R.

 

Example 1: Operate with Two Vectors

We first load the Rcpp package.

if (!require('Rcpp', quietly = TRUE)) { install.packages('Rcpp') } 
library('Rcpp') # Load package 'Rcpp'

Now, as an example, we have two vectors called “x” and “y” of same length. We want to perform element-wise operators on these vectors that depend on their respective values. In R we can define such a function as follows.

fun_r <- function (x, y) {
  ifelse ( x * y > 0, x - y, x * y )
}
 
fun_r(x = 1:5, y = 6:10)
# [1] -5 -5 -5 -5 -5

To write the same function in native C++, we would have to define a loop which conducts the operations element-wise.

cppFunction(' 
  Rcpp::NumericVector fun_cpp( Rcpp::NumericVector x, Rcpp::NumericVector y ) {
 
    // Get the length of vector x, store it as "n"
    int n = x.size() ;
 
    // Initialize all objects for the loop
    Rcpp::NumericVector res( n ) ;
    double x_i = 0.0, y_i = 0.0 ;
 
    // Conduct the loop
    for ( int i=0; i<n; i++){
      x_i = x[i] ;
      y_i = y[i] ;
      if ( x_i * y_i > 0 ){
       res[i] = x_i - y_i ;
      } else {
       res[i] = x_i * y_i ;
     }
    }
    return res ;
  }
')
 
fun_cpp(x = 1:5, y = 6:10)
# [1] -5 -5 -5 -5 -5

This looks a bit too complicated right? Rcpp sugar gives us the possibility to define this function much more R-like in C++. Here is how we can write the C++ code instead:

cppFunction(' 
  Rcpp::NumericVector fun_cpp_sugar( Rcpp::NumericVector x, Rcpp::NumericVector y ) {
  Rcpp::NumericVector res = Rcpp::wrap( ifelse( x * y > 0, x - y, x * y )) ;
    return res ;
  }
')
 
fun_cpp_sugar(x = 1:5, y = 6:10)
# [1] -5 -5 -5 -5 -5

We need use Rcpp::wrap() to return non-SEXP objects. Function fun_cpp_sugar() is now much more neat than the previously defined function fun_cpp_sugar() and very similar to our R code above.

As R users, we use Rcpp and C++ primarily to make our code more efficient. So what is the efficiency gain when we use Sugar Expressions or native C++? Let’s do a micro-benchmarking of the above three functions to see.

library(microbenchmark)
 
set.seed(6)
x = rnorm(10000); y = rnorm(10000)
 
microbenchmark::microbenchmark("R"          = fun_r(x, y),
                               "Cpp_Native" = fun_cpp(x, y),
                               "Cpp_Sugar"  = fun_cpp_sugar(x, y))
 
# Unit: microseconds
#       expr   min    lq    mean median     uq    max neval
#          R 248.1 258.6 385.129 278.70 325.60 4200.0   100
# Cpp_Native  65.1  68.0  73.937  70.45  77.05  106.8   100
#  Cpp_Sugar 124.7 127.9 143.876 130.85 135.65  725.5   100

Interesting. Both native C++ and C++ sugar are much faster than R. But we also see that native C++ is much faster than using C++ with the sugar expressions.

So in the end, as is often the case, it comes down to convenience. Are the operations we want to perform elaborate enough to write them in native C++ or do we perhaps just write the vector operations with Rcpp sugar functions and operators and save ourselves the time of programming in C++.

 

Example 2: Other sugar Operations

We make some other examples of Rcpp sugar operations. A list of different operators which you can use on vectors is given here and here. Let’s try some of these.

This time, we define a result list in which we evaluate different sugar operations in the list elements.

cppFunction(' 
double rcpp_mean(Rcpp::NumericVector x) {
  return Rcpp::mean(x);
}
')
rcpp_mean(rnorm(30))
 
cppFunction(' 
  List fun_cpp_sugar_2( Rcpp::NumericVector x ) {
 
    List res;
 
    res["some calculation"]                               = x * x;
    res["second calculation"]                             = x + sin(x);
    res["difference between any two succeeding elements"] = diff(x) ;
    res["calculate x^5"]                                  = pow(x, 5) ;
    res["generate rnorm values"]                          = rnorm(x.size(), 0, 4) ;
    res["logical vector: is element < 3"]                 = x < 3 ;
    res["logical: is any vector value < 3"]               = is_true( any(x < 3) ) ;
 
    double res2              = Rcpp::mean( x );
    Rcpp::NumericVector res3 = Rcpp::cumsum( x );
 
    res["mean of vector values"]    = res2;
    res["cumulative sum of values"] = res3;
 
    return res ;
  }
')

The code returns the following list for some example values.

set.seed(65)
fun_cpp_sugar_2(1:3)
 
# $`some calculation`
# [1] 1 4 9
# 
# $`second calculation`
# [1] 1.841471 2.909297 3.141120
# 
# $`difference between any two succeeding elements`
# [1] 1 1
# 
# $`calculate x^5`
# [1]   1  32 243
# 
# $`generate rnorm values`
# [1] -4.787282 -3.806670  1.114546
# 
# $`logical vector: is element < 3`
# [1]  TRUE  TRUE FALSE
# 
# $`logical: is any vector value < 3`
# [1] TRUE
# 
# $`mean of vector values`
# [1] 2
# 
# $`cumulative sum of values`
# [1] 1 3 6

 

Video & Further Resources

For further information on Rcpp, check out the following video, where Dirk Eddelbuettel and Romain François give a talk about the integration of R and C++ via Rcpp, posted by the Google TechTalks channel.

 

 

Furthermore, the following posts on https://statisticsglobe.com/ may be of interest to you.

 

Here, we showed you some examples of how to conduct R-like vector operations in C++ via Rcpp. For any comments or questions, use the section below.

 

Anna-Lena Wölwer Survey Statistician & R Programmer

This page was created in collaboration with Anna-Lena Wölwer. Have a look at Anna-Lena’s author page to get more information about her academic background and the other articles she has written for Statistics Globe.

 

Subscribe to the Statistics Globe Newsletter

Get regular updates on the latest tutorials, offers & news at Statistics Globe.
I hate spam & you may opt out anytime: Privacy Policy.


Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Top