R与Cpp混合编程二

Last update: 2021-06-09

armadillo官网

http://arma.sourceforge.net/

可以使用ctrl+F在该网站上搜索需要查阅的函数,方便快捷。

Rcpp中自动支持缺失值

find_nonfinite可以查找NA,NaN, infinite.

arma::field<uvec> obsindex(arma::mat Xmis){
  
  int p = Xmis.n_cols, j;
  field<uvec> indexfield(p);
  for(j= 0; j< p; ++j){
    indexfield(j) = find_nonfinite(Xmis.col(j));
  }
  return(indexfield);
}

List返回值的类型的重复申明

  1. 为什么cpp中定义的函数返回List后,List的成分为矩阵时,无法做index_max()计算和返回该矩阵到另一个List的成分中??

返回的List的成分为矩阵时,需要重新申明其为矩阵。

List aList = getList(xx);
mat mat1 = aList["mat"]; // 注意这里的申明mat不能省略,否则无法强制转换。

注意事项

  1. 若A是一个矩阵,则A.col(k)可以直接支持diagmat(),repmat()函数中使用,表示它返回一个列向量。

  2. 若C是一个cube,则C.slice(k)是一个矩阵,支持矩阵的所有运算,比如.t(),.i()以及A + b*C.slice(k);而 C.col(k)不是一个矩阵而是一个subcube,不支持直接的矩阵运算,只能通过赋值给一个矩阵tmpMat后转化为一个矩阵后,再进行相应的矩阵运算。为了更加高效的使用cube,请注意cube的存储格式,将需要参与矩阵乘法的subcube储存到第三个维度上。

  3. 下面展示field和cube运算的例子,其中C是一个qqK的cube,B是一个K元的field,它的每一个成分都是一个矩阵。

C.slice(k).i() * B(r).col(j) +  B(r).col(l)
  1. 最大值最小值溢出,C++中最小值。非常接近0的溢出为0。比如\(4^{-200}*A %( 2* 4^{-200}*B)\),对于这种就需要先把\(4^{-200}\)约掉后再计算两个矩阵的逐元除法。

2)整除错误:C++中整数除整数等于相除之后再向下取整。如下示例代码:

 U0 = (bx - 1/n *A * tA * a) / as_scalar(1- 1/n * a.t()*tA * a);
## 这里1/n=0 而不等于真正的小数。应该修改成:
 U0 = (bx - 1.0/n *A * tA * a) / as_scalar(1- 1.0/n * a.t()*tA * a);

我知道这样C++有这个语法规则,但是写代码的时候还是容易疏忽,而且还找不到错误的点在哪里。

  1. 公式中的加号运算错误的写成减法运算,导致出错。这是我容易出错的地方。 例如:
X_tk = X - repmat(U0.t()  + Mu0.row(k)* W0.t(), n, 1);
#错误写成:
X_tk = X - repmat(U0.t()  - Mu0.row(k)* W0.t(), n, 1);
  1. 避免log(a)当a=0报错的情况,可以使用
log(a + (a==0)) # a=0时,值会等于0

返回目录

如何传递非结构化数据

R中list类型常常用于存储非结构化数据,如何将非结构化数据传入到C++中进行计算,这是一个非常重要的问题。 下面这段使用List的代码非常经典,值得学习。

#include "RcppArmadillo.h"
// [[Rcpp::depends(RcppArmadillo)]]
#include<ctime>


#define INT_MIN (-INT_MAX - 1)

using namespace Rcpp;
using namespace arma;
using namespace std;

//[[Rcpp::export]] 
List calList(const Rcpp::List& alist, const Rcpp::List blist){
  int r,  L1 = alist.length();
  cout<<"L1="<<L1<<endl;
  field<mat> Xf(1,2), Wf(1,2);
  for(r=0; r < L1; ++r){
    mat Xtmp = alist[r];
    Xf(0,r) = Xtmp;
    mat Wtmp = blist[r];
    Wf(0,r) = Wtmp;
  }
  List resList = List::create(
    Rcpp::Named("Xf") = Xf,
    Rcpp::Named("Wf") = Wf
  );
  return resList;
}

返回目录

类型转换

field<mat> Rf1 = ICM_fit["Rf"];
field<ivec> yf(r_max);
uvec y1_u = index_max(Rf1(r),1);
yf(r) = conv_to< ivec >::from(y1_u) + 1;

返回目录

逐行逐列做运算

使用.each_col()和.each_row()属性的用法,能够避免使用循环。

mat X(6, 5, fill::ones);
vec v = linspace<vec>(10,15,6);

X.each_col() += v;         // in-place addition of v to each column vector of X

mat Y = X.each_col() + v;  // generate Y by adding v to each column vector of X

// subtract v from columns 0 through to 3 in X
X.cols(0,3).each_col() -= v;


uvec indices(2);
indices(0) = 2;
indices(1) = 4;

X.each_col(indices) = v;   // copy v to columns 2 and 4 in X

返回目录

List套List

Armadillo中List套List的经典代码:


//[[Rcpp::export]]
List solution_pathCpp(const vec& Y, const mat& X,const vec& lambda_set, const double& alpha0,  const double& r2,
                      mat& beta_int, const int& maxIter=100, const double& epsObj=1e-4, const bool& verbose=true){
  
  int i, nLam = lambda_set.n_elem, p = X.n_cols;
  mat betaMat(p ,nLam, fill::zeros);
  List resList(nLam);
  List tmpList = lmPenAlphaCpp(Y, X, alpha0, lambda_set(0), r2, beta_int, maxIter, 
          epsObj, false, verbose);
  mat betai = tmpList["beta0"];
  resList[0] = tmpList;
  betaMat.col(0) = betai;
  if(nLam > 1){
    for(i=1; i< nLam; ++i){
      tmpList = lmPenAlphaCpp(Y, X, alpha0, lambda_set(0), r2, betai, maxIter, 
              epsObj, true, verbose);
      vec tmpvec = tmpList["beta0"];
      betaMat.col(i) = tmpvec;
      resList[i] = tmpList;
    }
    
  }
  List outList = List::create(
    Rcpp::Named("betaMat") = betaMat,
    Rcpp::Named("lambda_set") = lambda_set,
    Rcpp::Named("fitList") = resList
  );
  return(outList);
}

返回目录

非规则向量元素访问

Armadillo中的向量原来还可以这样访问元素 gamma(S):

uvec S = find(abs(gamma) <= r2);
  cout<<"S="<<S.t()<<endl;
  cout<<gamma(S).t()<<endl;
  double obj = pow(norm(Y- X* (gamma+ alpha)), 2.0) / n;
  if(S.n_elem >0){
    pen2 = lambda2* accu(gamma(S) % gamma(S)) * pow(S.n_elem, -nu);
  }else{
    pen2 = 1e20;
  }

返回目录