1. 相关配置和说明
由于Dirk的书Seamless R and C++ Integration with Rcpp是13年出版的,当时Rcpp Attributes这一特性还没有被CRAN批准,所以当时调用和编写Rcpp函数还比较繁琐。Rcpp Attributes(2016)极大简化了这一过程(“provides an even more direct connection between C++ and R”),保留了内联函数,并提供了sourceCpp
函数用于调用外部的.cpp
文件。换句话说,我们可以将某C++函数存在某个.cpp
文件中,再从R脚本文件中,像使用source
一样,通过sourceCpp
来调用此C++函数。
例如,在R脚本文件中,我们希望调用名叫test.cpp
文件中的函数,我们可以采用如下操作:
1
2
3
|
library(Rcpp)
Sys.setenv( "PKG_CXXFLAGS" = "-std=c++11" )
sourceCpp( "test.cpp" )
|
其中第二行的意思是使用C++11的标准来编译文件。
在test.cpp
文件中, 头文件使用Rcpp.h
,需要输出到R中的函数放置在//[[Rcpp::export]]
之后。如果要输出到R中的函数需要调用其他C++函数,可以将这些需要调用的函数放在//[[Rcpp::export]]
之前。
1
2
3
|
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp: :export ]]
|
为进行代数计算,Rcpp提供了RcppArmadillo和RcppEigen。如果要使用此包,需要在函数文件开头注明依赖关系,例如// [[Rcpp::depends(RcppArmadillo)]]
,并载入相关头文件:
1
2
3
4
5
6
|
// [[Rcpp: :depends (RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <Rcpp.h>
using namespace Rcpp;
using namespace arma;
// [[Rcpp: :export ]]
|
C++的基本知识可以参见此处。
2. 常用数据类型
关键字 | 描述 |
---|---|
int/double/bool/String/auto | 整数型/数值型/布尔值/字符型/自动识别(C++11) |
IntegerVector | 整型向量 |
NumericVector | 数值型向量(元素的类型为double) |
ComplexVector | 复数向量 Not Sure |
LogicalVector | 逻辑型向量; R的逻辑型变量可以取三种值:TRUE, FALSE, NA; 而C++布尔值只有两个,true or false。如果将R的NA转化为C++中的布尔值,则会返回true。 |
CharacterVector | 字符型向量 |
ExpressionVector | vectors of expression types |
RawVector | vectors of type raw |
IntegerMatrix | 整型矩阵 |
NumericMatrix | 数值型矩阵(元素的类型为double) |
LogicalMatrix | 逻辑型矩阵 |
CharacterMatrix | 字符矩阵 |
List aka GenericVector | 列表;lists;类似于R中列表,其元素可以使任何数据类型 |
DataFrame | 数据框;data frames;在Rcpp内部,数据框其实是通过列表实现的 |
Function | 函数型 |
Environment | 环境型;可用于引用R环境中的函数、其他R包中的函数、操作R环境中的变量 |
RObject | 可以被R识别的类型 |
注释:
某些R对象可以通过as<Some_RcppObject>(Some_RObject)
转化为转化为Rcpp对象。例如:
在R中拟合一个线性模型(其为List),并将其传入C++函数中
1
|
>mod=lm( Y ~ X );
|
1
2
|
NumericVector resid = as<NumericVector>(mod[ "residuals" ]);
NumericVector fitted = as<NumericVector>(mod[ "fitted.values" ]);
|
可以通过as<some_STL_vector>(Some_RcppVector)
,将NumericVector
转换为std::vector
。例如:
1
2
|
std: :vector <double> vec;
vec = as<std: :vector <double>>(x);
|
在函数中,可以用wrap()
,将std::vector
转换为NumericVector
。例如:
1
2
3
|
arma: :vec long_vec( 16 ,arma: :fill : :randn );
vector<double> long_vec2 = conv_to<vector<double>>: :from (long_vec);
NumericVector output = wrap(long_vec2);
|
在函数返回时,可以使用wrap()
,将C++ STL类型转化为R可识别类型。示例见后面输入和输出示例部分。
以上数据类型除了Environment
之外(Function
不确定),大多可直接作为函数返回值,并被自动转化为R对象。
算数和逻辑运算符号+, -, *, /, ++, --, pow(x,p), <, <=, >, >=, ==, !=
。逻辑关系符号&&, ||, !
。
3. 常用数据类型的建立
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// 1 . Vector
NumericVector V1 (n);//创立了一个长度为n的默认初始化的数值型向量 V1 。
NumericVector V2 =NumericVector: :create ( 1 , 2 , 3 ); //创立了一个数值型向量 V2 ,并初始化使其含有三个数 1 , 2 , 3 。
LogicalVector V3 =LogicalVector: :create ( true , false ,R_NaN);//创立了一个逻辑型变量 V3 。如果将其转化为 R Object ,则其含有三个值 TRUE , FALSE , NA 。
// 2 . Matrix
NumericMatrix M1 (nrow,ncol);//创立了一个nrow*ncol的默认初始化的数值型矩阵。
// 3 . Multidimensional Array
NumericVector out=NumericVector(Dimension( 2 , 2 , 3 ));//创立了一个多维数组。然而我不知道有什么卵用。。
// 4 . List
NumericMatrix y1( 2 , 2 );
NumericVector y2( 5 );
List L =List: :create (Named( "y1" )=y1,
Named( "y2" )=y2);
// 5 . DataFrame
NumericVector a=NumericVector: :create ( 1 , 2 , 3 );
CharacterVector b=CharacterVector: :create ( "a" , "b" , "c" );
std: :vector <std: :string > c( 3 );
c[ 0 ]= "A" ;c[ 1 ]= "B" ;c[ 2 ]= "C" ;
DataFrame DF =DataFrame: :create (Named( "col1" )=a,
Named( "col2" )=b,
Named( "col3" )=c);
|
4. 常用数据类型元素访问
元素访问 | 描述 |
---|---|
[n] | 对于向量类型或者列表,访问第n个元素。对于矩阵类型,首先把矩阵的下一列接到上一列之下,从而构成一个长列向量,并访问第n个元素。不同于R,n从0开始。 |
(i,j) | 对于矩阵类型,访问第(i,j)个元素。不同于R,i和j从0开始。不同于向量,此处用圆括号。 |
List["name1"]/DataFrame["name2"] | 访问List中名为name1的元素/访问DataFrame中,名为name2的列。 |
5. 成员函数
成员函数 | 描述 |
---|---|
X.size() | 返回X的长度;适用于向量或者矩阵,如果是矩阵,则先向量化 |
X.push_back(a) | 将a添加进X的末尾;适用于向量 |
X.push_front(b) | 将b添加进X的开头;适用于向量 |
X.ncol() | 返回X的列数 |
X.nrow() | 返回X的行数 |
6. 语法糖
6.1 算术和逻辑运算符
+, -, *, /, pow(x,p), <, <=, >, >=, ==, !=, !
以上运算符均可向量化。
6.2. 常用函数
is.na()
Produces a logical sugar expression of the same length. Each element of the result expression evaluates to TRUE if the corresponding input is a missing value, or FALSE otherwise.
seq_len()
seq_len( 10 )
will generate an integer vector from 1 to 10 (Note: not from 0 to 9), which is very useful in conjugation withsapply()
and lapply()
.
pmin(a,b)
and pmax(a,b)
a
and b
are two vectors. pmin()
(or pmax()
) compares the i <script type="math/tex" id="MathJax-Element-1">i</script>th elements of a
and b
and return the smaller (larger) one.
ifelse()
ifelse( x > y, x+y, x-y )
means if x>y
is true, then do the addition; otherwise do the subtraction.
sapply()
sapply
applies a C++ function to each element of the given expression to create a new expression. The type of the resulting expression is deduced by the compiler from the result type of the function.
The function can be a free C++ function such as the overload generated by the template function below:
1
2
3
4
5
|
template <typename T >
T square( const T & x){
return x * x ;
}
sapply( seq_len( 10 ), square<int> ) ;
|
Alternatively, the function can be a functor whose type has a nested type called result_type
1
2
3
4
5
6
7
|
template <typename T >
struct square : std: :unary_function < T , T > {
T operator()(const T & x){
return x * x ;
}
}
sapply( seq_len( 10 ), square<int>() ) ;
|
lappy()
lapply
is similar to sapply except that the result is allways an list expression (an expression of type VECSXP
).
sign()
其他函数
- 数学函数: abs(), acos(), asin(), atan(), beta(), ceil(), ceiling(), choose(), cos(), cosh(), digamma(), exp(), expm1(), factorial(), floor(), gamma(), lbeta(), lchoose(), lfactorial(), lgamma(), log(), log10(), log1p(), pentagamma(), psigamma(), round(), signif(), sin(), sinh(), sqrt(), tan(), tanh(), tetragamma(), trigamma(), trunc().
- 汇总函数: mean(), min(), max(), sum(), sd(), and (for vectors) var()
- 返回向量的汇总函数: cumsum(), diff(), pmin(), and pmax()
- 查找函数: match(), self_match(), which_max(), which_min()
- 重复值处理函数: duplicated(), unique()
7. STL
Rcpp可以使用C++的标准模板库STL中的数据结构和算法。Rcpp也可以使用Boost中的数据结构和算法。
7.1. 迭代器
此处仅仅以一个例子代替,详细参见C++ Primer,或者此处。
1
2
3
4
5
6
7
8
9
10
11
|
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp: :export ]]
double sum3(NumericVector x) {
double total = 0 ;
NumericVector: :iterator it;
for (it = x. begin (); it != x. end (); ++it) {
total += *it;
}
return total;
}
|
7.2. 算法
头文件<algorithm>
中提供了许多的算法(可以和迭代器共用),具体可以参见此处。
For example, we could write a basic Rcpp version of findInterval()
that takes two arguments a vector of values and a vector of breaks, and locates the bin that each x falls into.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <algorithm>
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp: :export ]]
IntegerVector findInterval2(NumericVector x, NumericVector breaks) {
IntegerVector out(x.size());
NumericVector: :iterator it, pos;
IntegerVector: :iterator out_it;
for (it = x. begin (), out_it = out. begin (); it != x. end ();
++it, ++out_it) {
pos = std: :upper_bound (breaks. begin (), breaks. end (), *it);
*out_it = std: :distance (breaks. begin (), pos);
}
return out;
}
|
7.3. 数据结构
STL所提供的数据结构也是可以使用的,Rcpp知道如何将STL的数据结构转换成R的数据结构,所以可以从函数中直接返回他们,而不需要自己进行转换。
具体请参考此处。
7.3.1. Vectors
详细信息请参见处此
创建vector<int>
, vector<bool>
, vector<double>
, vector<String>
元素访问
利用标准的[]
符号访问元素
元素增加
利用.push_back()
增加元素。
存储空间分配
如果事先知道向量长度,可用.reserve()
分配足够的存储空间。
例子:
The following code implements run length encoding (rle()). It produces two vectors of output: a vector of values, and a vector lengths giving how many times each element is repeated. It works by looping through the input vector x comparing each value to the previous: if it's the same, then it increments the last value in lengths; if it's different, it adds the value to the end of values, and sets the corresponding length to 1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp: :export ]]
List rleC(NumericVector x) {
std: :vector <int> lengths;
std: :vector <double> values;
// Initialise first value
int i = 0 ;
double prev = x[ 0 ];
values.push_back(prev);
lengths.push_back( 1 );
NumericVector: :iterator it;
for (it = x. begin () + 1 ; it != x. end (); ++it) {
if (prev == *it) {
lengths[i]++;
} else {
values.push_back(*it);
lengths.push_back( 1 );
i++;
prev = *it;
}
}
return List: :create (
_ [ "lengths" ] = lengths,
_ [ "values" ] = values
);
}
|
7.3.2. Sets
STL中的集合std::set
不允许元素重复,而std::multiset
允许元素重复。集合对于检测重复和确定不重复的元素具有重要意义((like unique
, duplicated
, or in
))。
Ordered set: std::set
和std::multiset
。
Unordered set: std::unordered_set
一般而言unordered set比较快,因为它们使用的是hash table而不是tree的方法。unordered_set<int>
, unordered_set<bool>
, etc
7.3.3. Maps
与table()
和match()
关系密切。
Ordered map: std::map
Unordered map: std::unordered_map
Since maps have a value and a key, you need to specify both types when initialising a map:
map<double, int>
, unordered_map<int, double>
.
8. 与R环境的互动
通过Environment
Rcpp可以获取当前R全局环境(Global Environment)中的变量和载入的函数,并可以对全局环境中的变量进行修改。我们也可以通过Environment
获取其他R包中的函数,并在Rcpp中使用。
获取其他R包中的函数
1
2
3
|
Rcpp::Environment stats( "package:stats" );
Rcpp::Function rnorm = stats[ "rnorm" ];
return rnorm( 10 , Rcpp::Named( "sd" , 100 . 0 ));
|
获取R全局环境中的变量并进行更改
假设R全局环境中有一个向量x=c(1,2,3)
,我们希望在Rcpp中改变它的值。
1
2
3
4
|
Rcpp::Environment global = Rcpp::Environment: :global_env ();//获取全局环境并赋值给Environment型变量global
Rcpp::NumericVector tmp = global[ "x" ];//获取x
tmp=pow(tmp, 2 );//平方
global[ "x" ]=tmp;//将新的值赋予到全局环境中的x
|
获取R全局环境中的载入的函数
假设全局环境中有R函数funR
,其定义为:
1
2
3
4
|
x=c( 1 , 2 , 3 );
funR<-function(x){
return (-x);
}
|
并有R变量x=c(1,2,3)
。我们希望在Rcpp中调用此函数并应用在向量x上。
1
2
3
4
5
6
7
8
9
10
|
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp: :export ]]
NumericVector funC() {
Rcpp::Environment global =
Rcpp::Environment: :global_env ();
Rcpp::Function funRinC = global[ "funR" ];
Rcpp::NumericVector tmp = global[ "x" ];
return funRinC(tmp);
}
|
9. 用Rcpp创建R包
见此文
10. 输入和输出示例
如何传递数组
如果要传递高维数组,可以将其存为向量,并附上维数信息。有两种方式:
通过.attr("dim")设置维数
NumericVector
可以包含维数信息。数组可以用过NumericVector
输出到R中。此NumericVector
可以通过.attr(“dim”)
设置其维数信息。
1
2
3
4
5
|
// Dimension最多设置三个维数
output.attr( "dim" ) = Dimension( 3 , 4 , 2 );
// 可以给.attr(“dim”)赋予一个向量,则可以设置超过三个维数
NumericVector dim = NumericVector: :create ( 2 , 2 , 2 , 2 );
output.attr( "dim" ) = dim;
|
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 返回一个 3 * 3 * 2 数组
RObject func(){
arma: :vec long_vec( 18 ,arma: :fill : :randn );
vector<double> long_vec2 = conv_to<vector<double>>: :from (long_vec);
NumericVector output = wrap(long_vec2);
output.attr( "dim" )=Dimension( 3 , 3 , 2 );
return wrap(output);
}
// 返回一个 2 * 2 * 2 * 2 数组
// 注意con_to<>: :from ()
RObject func(){
arma: :vec long_vec( 16 ,arma: :fill : :randn );
vector<double> long_vec2 = conv_to<vector<double>>: :from (long_vec);
NumericVector output = wrap(long_vec2);
NumericVector dim = NumericVector: :create ( 2 , 2 , 2 , 2 );
output.attr( "dim" )=dim;
return wrap(output);
}
|
另外建立一个向量存维数,在R中再通过.attr("dim")设置维数
函数返回一维STL vector
自动转化为R中的向量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
vector<double> func(NumericVector x){
vector<double> vec;
vec = as<vector<double>>(x);
return vec;
}
NumericVector func(NumericVector x){
vector<double> vec;
vec = as<vector<double>>(x);
return wrap(vec);
}
RObject func(NumericVector x){
vector<double> vec;
vec = as<vector<double>>(x);
return wrap(vec);
}
|
函数返回二维STL vector
自动转化为R中的list,list中的每个元素是一个vector。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
vector<vector<double>> func(NumericVector x) {
vector<vector<double>> mat;
for (int i= 0 ;i!= 3 ;++i){
mat.push_back(as<vector<double>>(x));
}
return mat;
}
RObject func(NumericVector x) {
vector<vector<double>> mat;
for (int i= 0 ;i!= 3 ;++i){
mat.push_back(as<vector<double> >(x));
}
return wrap(mat);
}
|
返回Armadillo matrix, Cube 或 field
自动转化为R中的matrix
1
2
3
4
5
6
7
8
|
NumericMatrix func(){
arma: :mat A ( 3 , 4 ,arma: :fill : :randu );
return wrap( A );
}
arma: :mat func(){
arma: :mat A ( 3 , 4 ,arma: :fill : :randu );
return A ;
}
|
自动转化为R中的三维array
1
2
3
4
5
6
7
8
|
arma: :cube func(){
arma: :cube A ( 3 , 4 , 5 ,arma: :fill : :randu );
return A ;
}
RObject func(){
arma: :cube A ( 3 , 4 , 5 ,arma: :fill : :randu );
return wrap( A );
}
|
自动转化为R list,每个元素存储一个R向量,但此向量有维数信息(通过.Internal(inspect())
查询)。
1
2
3
4
5
6
7
8
|
RObject func() {
arma: :cube A ( 3 , 4 , 2 ,arma: :fill : :randu );
arma: :cube B ( 3 , 4 , 2 ,arma: :fill : :randu );
arma: :field <arma: :cube > F ( 2 , 1 );
F ( 0 )= A ;
F ( 1 )= B ;
return wrap( F );
}
|
参考文献:
Eddelbuettel, D. (2013). Seamless R and C++ Integration with Rcpp. Springer Publishing Company, Incorporated. ·
Allaire, J.J. (2016). Rcpp Attributes.
Eddelbuettel, D. (2016). Rcpp syntactic sugar.
http://adv-r.had.co.nz/Rcpp.html
http://blog.csdn.net/a358463121
http://www.runoob.com/cplusplus/cpp-operators.html
如需引用,请注明出处。
以上就是R语言学习Rcpp知识全面整理的详细内容,更多关于Rcpp知识全面整理的资料请关注服务器之家其它相关文章!
原文链接:https://blog.csdn.net/iamsuperman2/article/details/73251928