VC++調用R語言


  一年多前做曲線擬合,當時需要用C++調用R語言來完成。

一、用R作曲線擬合

  先看一段用R語言作擬合的示例:

x <- runif(100,min=0,max=100)	#創建100個隨機數
y <- x*x+runif(x,-10,10)*x+10*rnorm(x)	#創建y向量
plot(x,y)	#繪制散點圖
matr <- data.frame(X=x, X2=x*x)	#建立解析矩陣
fm <- lsfit(matr ,y)	#最小二乘法解析
a <- coef(fm)	#從解析結果提取系數
f <- function(x) a[1]+ a["X"]*x+a["X2"]*x*x	#建立擬合函數
curve(f,col="red",add=TRUE)	#畫曲線

   運行結果為:

  這里創建的數據接近二次函數,因此擬合的目標是y = a0 + a1*x + a2*x2。換成線性方程組就是

[1,  X1,  X12]    [a0]    [y1]

[1,  X2,  X22]  *[a1] = [y2]

……                  [a2]

[1,  Xn,  Xn2]              [yn]

  在R語言中,如果是求解該線性方程組,可以使用solve(Matr,y);如果是用最小二乘法擬合,就要用函數lsfit(),它返回的結果再提取系數就可以得到系數的行列式。

二、C++調用R來實現

  如果用c++調用R來做,就需要借助工具。我使用的是R語言下的兩個包,Rcpp與RInside,作用分別是R調用C++與C++調用R,其中RInside依賴於Rcpp。RInside提供的接口如下:

int  parseEval(const std::string &line, SEXP &ans); // parse line, return in ans; error code rc
void parseEvalQ(const std::string &line);            // parse line, no return (throws on error)
void parseEvalQNT(const std::string &line);            // parse line, no return (no throw)
Proxy parseEval(const std::string &line);             // parse line, return SEXP (throws on error)
Proxy parseEvalNT(const std::string &line);            // parse line, return SEXP (no throw)

template <typename T>
void assign(const T& object, const std::string& nam) {
    global_env_m->assign( nam, object ) ;
}

……
Rcpp::Environment::Binding operator[]( const std::string& name );

  其中parseEval函數是用來執行語句的,而assign與[]是用來賦值的。

  所以使用RInside,以上過程就是這樣:

1 RInside RI;
2 RI["M"] = Rcpp::NumericMatrix(/*傳入矩陣*/);
3 RI["y"] = Rcpp::NumericVector(/*傳入數組*/);
4 std::vector<double> b2 = RI.parseEval("coef(lsfit(M,y))");

  最初是直接使用RInside的,結果編譯報錯,說是不支持VC++,沒辦法,只好先用gcc編譯成庫,再給VC++使用。

三、gcc編譯成庫給vc++用 

一、配置

  首先下載R,Rtools(使用里面的gcc),Rcpp,RInside,CodeBlocks(不帶編譯器),在CodeBlocks的compiler settings中如下設置:

PS:那時候Rtools里的gcc還是4.6.3,不支持C++11,為了使用C++11,費了好大功夫,最后失敗。現在gcc版本跟上來了。

  然后在CodeBlocks中新建一個dll工程:

 

  設置工程屬性:

  以下3張圖分別設置鏈接庫,頭文件目錄,庫目錄,對應着gcc的-l、-I、-L命令。

PS:user32其實是不必要的。

二、編碼

  這里只展示兩個簡單的函數,最小二乘擬合與解線性方程組,非常簡單。

  頭文件:

 1 #ifndef RINSIDE_4_VS_H
 2 #define RINSIDE_4_VS_H
 3 
 4 #include <boost/container/vector.hpp>
 5 #include <boost/multi_array.hpp>
 6 typedef boost::container::vector<double> vector_t;
 7 typedef boost::multi_array<double, 2> matrix_t;
 8 
 9 /** 
10    *
11    * \author     Li zhongxian
12    * \version    1.0
13    * \date       2016.2
14    *
15    */
16 #ifdef BUILD_DLL
17 #define DLL_EXPORT __declspec(dllexport)
18 #else
19 #define DLL_EXPORT __declspec(dllimport)
20 #endif
21 
22 #ifdef __cplusplus
23 extern "C"
24 {
25 #endif
26     /** \brief 對於線性方程組M·b=y,用最小二乘法擬合
27     *
28     * \param M 解析矩陣,大小為(系數-1)*案例
29     * \param y 指標向量,大小為案例數目
30     * \param b 系數向量,大小為系數數目
31     * \return 異常信息
32     */
33     DLL_EXPORT const char* lsfit(const matrix_t &M, const vector_t &y, vector_t &b);
34     DLL_EXPORT const char* lsfit_c(double M[], size_t rows, size_t cols, double y[], double b[]);
35     /** \brief 對於線性方程組M·b=y,求解b
36     *
37     * \param M 樣本矩陣,大小為系數*案例
38     * \param y 指標向量,大小為案例數目
39     * \param b 系數向量,大小為系數數目
40     * \return 異常信息
41     */
42     DLL_EXPORT const char* solve(const matrix_t &M, const vector_t &y, vector_t &b);
43     DLL_EXPORT const char* solve_c(double M[], size_t rows, size_t cols, double y[], double b[]);
44 
45 #ifdef __cplusplus
46 }
47 #endif
48 
49 #endif //H

  源文件:

  1 #include "RInside4vs.h"
  2 #include <RInside.h>
  3 //#include <fstream>
  4 
  5 inline Rcpp::NumericMatrix createMatrix(const matrix_t &Mat)
  6 {
  7     int rows = Mat.shape()[0];
  8      int cols = Mat.shape()[1];
  9     Rcpp::NumericMatrix M(cols,rows,Mat.data());
 10     return M;
 11 }
 12 
 13 inline Rcpp::NumericMatrix createMatrix_c(double data[], size_t rows, size_t cols)
 14 {
 15     Rcpp::NumericMatrix M(cols,rows,data);
 16     return M;
 17 }
 18 
 19 /// 所有函數共用一個RInside對象
 20 static RInside RI;
 21 
 22 DLL_EXPORT const char* lsfit(const matrix_t &M, const vector_t &y, vector_t &b)
 23 {
 24 
 25  //   std::ostringstream sout;
 26 //    std::ofstream fs("Rlog.txt");
 27 //    std::streambuf *x = std::cout.rdbuf(fs.rdbuf());
 28 //    char msg[200]="";
 29     try{
 30         RI["M"] = createMatrix(M);
 31     //    RI.parseEvalQ("cat('Showing M\n'); print(M)");
 32         RI["y"] = y;
 33     //    RI.parseEvalQ("cat('Showing y\n'); print(y)");
 34 
 35         std::vector<double> b2 = RI.parseEval("coef(lsfit(M,y))");
 36         std::copy(b2.begin(), b2.end(), b.begin());
 37 
 38     }catch(std::exception &e){
 39 //        std::cerr << "failed in " << __FUNCTION__ << std::endl;
 40     //    return strcat(strcpy(msg,sout.str().c_str()), e.what());
 41         return e.what();
 42     }
 43 /// 清理R空間
 44     RI.parseEvalQ("rm(list = ls())");
 45  //   std::cerr.rdbuf(x);
 46     return "";
 47 }
 48 
 49 DLL_EXPORT const char* lsfit_c(double M[], size_t rows, size_t cols, double y[], double b[])
 50 {
 51 
 52     try{
 53         RI["M"] = createMatrix_c(M,rows,cols);
 54     //    RI.parseEvalQ("cat('Showing M\n'); print(M)");
 55         RI["y"] = Rcpp::NumericVector(y, y+rows);
 56     //    RI.parseEvalQ("cat('Showing y\n'); print(y)");
 57 
 58         std::vector<double> b2 = RI.parseEval("coef(lsfit(M,y))");
 59         std::copy(b2.begin(), b2.end(), b);
 60 
 61     }catch(std::exception &e){
 62         return e.what();
 63     }
 64 /// 清理R空間
 65     RI.parseEvalQ("rm(list = ls())");
 66     return "";
 67 }
 68 
 69 DLL_EXPORT const char* solve(const matrix_t &M, const vector_t &y, vector_t &b)
 70 {
 71 
 72 //    std::cout << "len(y): " << y.size() << std::endl;
 73 //    std::cout << "len(M): " << M.size() << std::endl;
 74 //    std::cout << "rows: " << rows << std::endl;
 75 //    std::cout << "cols: " << cols << std::endl;
 76 
 77     try{
 78         RI["M"] = createMatrix(M);
 79     //    RI.parseEvalQ("cat('Showing M\n'); print(M)");
 80         RI["y"] = y;
 81     //    RI.parseEvalQ("cat('Showing y\n'); print(y)");
 82 
 83         std::vector<double> b2 = RI.parseEval("solve(M,y)");
 84         std::copy(b2.begin(), b2.end(), b.begin());
 85 
 86     }catch(std::exception &e){
 87         return e.what();
 88     }
 89 /// 清理R空間
 90     RI.parseEvalQ("rm(list = ls())");
 91     return "";
 92 }
 93 
 94 DLL_EXPORT const char* solve_c(double M[], size_t rows, size_t cols, double y[], double b[])
 95 {
 96 
 97     try{
 98         RI["M"] = createMatrix_c(M,rows,cols);
 99     //    RI.parseEvalQ("cat('Showing M\n'); print(M)");
100         RI["y"] = Rcpp::NumericVector(y, y+rows);
101     //    RI.parseEvalQ("cat('Showing y\n'); print(y)");
102 
103         std::vector<double> b2 = RI.parseEval("solve(M,y)");
104         std::copy(b2.begin(), b2.end(), b);
105 
106     }catch(std::exception &e){
107         return e.what();
108     }
109 /// 清理R空間
110     RI.parseEvalQ("rm(list = ls())");
111     return "";
112 }

三、使用

  編譯完成后,會生成libRInside4vs.a、libRInside4vs.def、RInside4vs.dll,只有dll是需要用到的。.a是gcc使用的.lib,VS不能用。打開cmd,切換到RInside4vs.dll所在的目錄,執行下面兩條語句:

  pexports RInside4vs.dll -o > RInside4vs.def

  lib /def:RInside4vs.def

  其中,pexports是gcc的擴展工具的命令,需要下載mingw-utils-0.3。lib是VS的命令。它們的配置這里就不細講了。

  執行完成后,即生成VS可用的RInside4vs.lib,這里的lib是引導文件,並不是靜態庫,它配合RInside4vs.dll使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM