之前介绍了如何制作并发布自己的R语言包。这篇文章介绍怎么制作一个C++的R包,也就是使用Rcpp制作R包。

其实制作Rcpp的R包和普通的R包制作还是有区别的。本人也尝试按照之前的做法,在R脚本里面使用cppFunction()写一个C++脚本,以脚本运行的时候很完美,没有出现任何错误,但是当构建成包时,安装之后每次运行都出现错误。所以不得不尝试其他方法创建Rcpp的R包。

1、为什么要用Rcpp

Rcpp实际上是R语言调用C++的一个接口,而C++在运行速度上要比R语言快成百上千倍!!比如有如下两个数据:

> aa
  pos
1   2
2   4
3   6
> bb
  start end
1     1   3
2     5   8
3     7  10

其中aa$pos表示一段DNA序列的SNP位点,bb表示该DNA序列上的基因位置(包括基因起始位置和终止位置),如果你要判断aa中所有的SNP是否在基因上,我们通常的做法是写两个for循环,用aa中的每一个SNP位点去判断bb中的每一个基因。也就是要有3 * 3 = 9次运算。但是当数据量足够大的时候,假如有几十万SNP位点,有几万基因序列,那么这个循环可能要执行成百上千万次!这对R语言来说,运行时间会以小时来计了。 而C++可以在一两分钟内完成! 参考:如何提升备受诟病的R的运行速度

2、构建RcppR包过程

(1)准备工作

和之前的介绍一样,还是要安装并加载devtools 和 roxygen2两个工具包。 然后创建一个路径(“~/Documents/CheckOverlap),在此路径内构建R包

library(devtools)
library(roxygen2)
setwd("~/Documents/")
create("CheckOverlap")
setwd("./CheckOverlap/")
devtools::use_rcpp() #会自动生成一些相关文件

和之前的文件结构不同,这时会出现一个src文件夹。 (2)写C++函数 按照你的目的参考如下格式写C++函数,并将该文件保存成“~/Documents/CheckOverlap/src/CheckPointRange.cpp“。 如果是在Rstudio中,可以“新建” —“C++文件“,这样会自动生成一个模版。

// CheckPointRange.cpp
#include <Rcpp.h>
using namespace Rcpp;

//'@title Check if points in segments
//'@description
//'To check if a number in a range. 
//'Return a vector of 0,1 if the the number 
//'in a range(1) or not (0).
//'@param pos a vector of numbers of points
//'@param start a vector of numbers of starting position
//'@param end a vector of numbers of ending position
//'@examples
//'aa <- c(2,4,6)
//'bb1 <- c(1,5,7)
//'bb2 <- c(3,8,10)
//'CheckPoint(aa,bb1,bb2)
//'@useDynLib CheckOverlap
//'@import Rcpp
//'@export
// [[Rcpp::export]]
IntegerVector CheckPoint(IntegerVector pos, IntegerVector start, IntegerVector end){
  int pos_size=pos.size();
  int seg_size=start.size();
  IntegerVector ismask(pos_size);
  for (int i=0; i<pos_size; i++) {
    for (int j=0; j<seg_size; j++){
      if ( pos[i]>=start[j] && pos[i]<=end[j] ){
        ismask[i]=1;
        break;
      }
    }
  }
  return ismask;
}

#include <Rcpp.h>
using namespace Rcpp;
//'@title Check if segments contain point
//'@description
//'To check if a range contains points. 
//'Return a vector of 0,1 
//'with 1 = contain points, 0 = not contain points.
//'@param pos a vector of numbers of points
//'@param start a vector of numbers of starting position
//'@param end a vector of numbers of ending position
//'@examples
//'aa <- c(2,4,6)
//'bb1 <- c(1,5,7)
//'bb2 <- c(3,8,10)
//'CheckRange(aa,bb1,bb2)
//'@useDynLib CheckOverlap
//'@import Rcpp
//'@export
// [[Rcpp::export]]
IntegerVector CheckRange(IntegerVector pos, IntegerVector start, IntegerVector end){
  int pos_size=pos.size();
  int seg_size=start.size();
  IntegerVector ismask1(pos_size);
  for (int i=0; i<seg_size; i++) {
    for (int j=0; j<pos_size; j++){
      if ( pos[j]>=start[i] && pos[j]<=end[i] ){
        ismask1[i]=1;
        break;
      }
    }
  }
  return ismask1;
}

注意,这儿写了两个函数,一个是用来判断SNP是够在基因上(CheckPoint),另一个用来判断基因上是否含有SNP位点(CheckRange)。 其中很多关键字是不可缺少的,比如include, using, useDynLib 你的包名, import, export, [[Rcpp::export]]等。 然后

Rcpp::sourceCpp("./src/CheckPointRange.cpp")

或者在Rstudio点击“Source“

(3)建包

setwd("~/Documents/CheckOverlap/")
document()

这个时候去检查CheckOverlap文件夹,会有很多自动生成的文件,包括NAMESPACE、DESCRIPTION,在src子文件夹中,有.so, .o等文件名后缀的的文件。 在R子文件夹中,出现了一个RcppExports.R的文件,这个文件就相当于该包的主文件。 下面检查包,看看是否存在错误。

setwd("..")
devtools::check("CheckOverlap")

(4)本地安装并调试

setwd("~/Documents/")
install("CheckOverlap")
aa <- c(2,4,6)
bb1 <- c(1,5,7)
bb2 <- c(3,8,10)
CheckOverlap::CheckPoint(aa,bb1,bb2)
CheckOverlap::CheckRange(aa,bb1,bb2)

输出结果为

[1] 1 0 1
# 即SNP2和6位于基因上
[1] 1 1 0
# 即基因片段1-3和5-8含有SNP位点。

至此,R包的构建已经完成

3、R包``发布 设置GitHub账户,参考: https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/ https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/ 在自己的GitHub账户上创建一个目录,用来存放你要发布的R包。

在本地terminal中:

cd ~/Documents/CheckOverlap/
git init
git add *
git commit -m "first version"
git remote add wyg git@github.com:Yiguan/CheckOverlap.git
git push -u wyg master

创建,发布;全部完成! 一个需要几个小时完成的任务,现在只需要一两分钟就可以完成!

======= THE END =======

参考资料:

https://www.r-bloggers.com/rcpp-and-roxygen2/

http://r-pkgs.had.co.nz/src.html

http://www.deanbodenham.com/learn/make-an-r-package-using-rcpp.html