117

Rで使用するCコードの記述方法を学ぶための最良のリソースは何ですか?R拡張機能のシステムと外国語インターフェースのセクションについては知っていますが、かなり難しいと思います。Rで使用するCコードを作成するための優れたリソース(オンラインとオフラインの両方)は何ですか?

明確にするために、Cコードの記述方法を学びたくはありません。RとCをより適切に統合する方法を学びたいと思います。たとえば、C整数ベクトルからR整数ベクトルに(またはその逆に)変換するにはどうすればよいですか。またはCスカラーからRベクトルへ?

4

4 に答える 4

73

古き良きものがありますソースを使用してください、ルーク!--- R自体には、学習できる(非常に効率的な)Cコードがたくさんあり、CRANには何百ものパッケージがあり、信頼できる作成者からのものもあります。これは、研究および適応するための実際のテスト済みの例を提供します。

しかし、Joshが疑ったように、私はC ++、つまりRcppに傾倒しています。例もたくさんあります。

編集:私が役に立ったと思った2冊の本がありました:

  • 1つ目は、Venables and Ripleyの「SProgramming ですが、歯が長くなっています(そして、何年もの間、第2版の噂があります)。当時は他に何もありませんでした。
  • Chambersの「SoftwareforDataAnalysis 」の第2部は、はるかに最近で、R中心の感触がはるかに優れています。また、Rの拡張に関する2つの章があります。CとC++の両方について説明します。さらに、ジョンは私がダイジェストで行ったことを細かく切り刻み、それだけで入場料の価値があります。

とは言うものの、ジョンはRオブジェクトとC ++オブジェクト(Rcppを介して)の一致が非常に自然であることに気付いたため、 Rcpp (および貢献)が好きになりました-そしてReferenceClassesはそこで役立ちます。

編集2: ハドリーの焦点を絞った質問で、C++を検討することを強くお勧めします。あなたがCと関係しなければならない非常に多くの定型文のナンセンスがあります---非常に退屈で非常に避けられます。Rcpp-はじめにビネットをご覧ください。もう1つの簡単な例は、このブログ投稿です。ここでは、10%の違いを心配する代わりに(Radford Nealの例の1つで) 、C ++で80倍の増加を得ることができます(もちろん、考案された例です)。

編集3: C ++エラーが発生する可能性があるという点で複雑さがあります。これは、控えめに言っても、理解するのが難しいものです。ただし、 Rcppを拡張するのではなく、単に使用するためには、ほとんど必要ありません。このコストは否定できませんが、コードが単純で、定型文が少なく、PROTECT / UNPROTECTがなく、メモリ管理がないなどの利点があります。DougBatesは昨日、C++とRcppがRの記述に非常に似ていると述べました。 C++を書くよりも。YMMVとそのすべて。

于 2010-11-05T13:36:54.860 に答える
58

ハドリー、

あなたは間違いなくCコードに似たC++コードを書くことができます。

C ++がCよりも複雑であるとあなたが言っていることを理解しています。これは、オブジェクト、テンプレート、STL、テンプレートメタプログラミングなど、すべてをマスターしたい場合です...ほとんどの人はこれらのものを必要とせず、他の人に頼ることができますそれに。Rcppの実装は非常に複雑ですが、冷蔵庫がどのように機能するかわからないからといって、ドアを開けて新鮮なミルクを手に入れることができないという意味ではありません...

Rへの多くの貢献から、私が驚いたのは、Rがやや退屈だと思うことです(データ操作、グラフィックス、文字列操作など)。Rの内部CAPIを使用して、さらに多くの驚きに備えましょう。これは非常に面倒です。

時々、R-extsまたはR-intsのマニュアルを読みます。これは役に立ちます。しかし、ほとんどの場合、私が本当に何かについて知りたいときは、Rソースにアクセスします。また、Simonなどによって作成されたパッケージのソースにもアクセスします(通常、そこで学ぶことはたくさんあります)。

Rcppは、APIのこれらの面倒な側面をなくすように設計されています。

いくつかの例に基づいて、より複雑でわかりにくいものなどを自分で判断できます。この関数は、CAPIを使用して文字ベクトルを作成します。

SEXP foobar(){
  SEXP ab;
  PROTECT(ab = allocVector(STRSXP, 2));
  SET_STRING_ELT( ab, 0, mkChar("foo") );
  SET_STRING_ELT( ab, 1, mkChar("bar") );
  UNPROTECT(1);
}

Rcppを使用すると、次と同じ関数を記述できます。

SEXP foobar(){
   return Rcpp::CharacterVector::create( "foo", "bar" ) ;
}

また:

SEXP foobar(){
   Rcpp::CharacterVector res(2) ;
   res[0] = "foo" ;
   res[1] = "bar" ;
   return res ;
}

ダークが言ったように、いくつかのビネットには他の例があります。また、それぞれがコードの非常に特定の部分をテストし、ある程度自明であるため、通常、単体テストに人々を向けます。

私は明らかにここで偏見を持っていますが、RのC APIを学ぶのではなく、Rcppに精通し、不明な点やRcppで実行できないと思われる場合は、メーリングリストにアクセスすることをお勧めします。

とにかく、売り込みの終わり。

最終的にどのようなコードを書きたいかによります。

ロマン

于 2010-11-05T14:47:20.160 に答える
30

@hadley:残念ながら、C++の使用を開始するのに役立つ特定のリソースを念頭に置いていません。私はScottMeyersの本(Effective C ++、More Effective C ++など)からそれを取り上げましたが、これらは実際には入門書とは言えません。

C ++コードを呼び出すために、ほぼ排他的に.Callインターフェイスを使用します。ルールは簡単です:

  • C++関数はRオブジェクトを返す必要があります。すべてのRオブジェクトはSEXPです。
  • C ++関数は、0から65のRオブジェクトを入力として受け取ります(ここでもSEXP)
  • extern "C"またはRcppが定義するRcppExportエイリアスのいずれかを使用して、Cリンケージで宣言する必要があります(実際にはそうではありませんが、後で使用するために保存できます) 。

したがって、.Call関数はいくつかのヘッダーファイルで次のように宣言されます。

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

.cppファイルにこのように実装されています:

SEXP foo( SEXP x1, SEXP x2 ){
   ...
}

Rcppを使用するためのRAPIについて知ることはあまりありません。

ほとんどの人は、Rcppで数値ベクトルのみを扱いたいと思っています。これは、NumericVectorクラスを使用して行います。数値ベクトルを作成するには、いくつかの方法があります。

Rから受け継いだ既存のオブジェクトから:

 SEXP foo( SEXP x_) {
    Rcpp::NumericVector x( x_ ) ;
    ...
 }

:: create static関数を使用して、指定された値を使用します。

 Rcpp::NumericVector x = Rcpp::NumericVector::create( 1.0, 2.0, 3.0 ) ;
 Rcpp::NumericVector x = Rcpp::NumericVector::create( 
    _["a"] = 1.0, 
    _["b"] = 2.0, 
    _["c"] = 3
 ) ;

与えられたサイズの:

 Rcpp::NumericVector x( 10 ) ;      // filled with 0.0
 Rcpp::NumericVector x( 10, 2.0 ) ; // filled with 2.0

次に、ベクトルができたら、そこから1つの要素を抽出するのが最も便利です。これは、0ベースのインデックスを使用してoperator []を使用して実行されるため、たとえば、数値ベクトルの値を合計すると、次のようになります。

SEXP sum( SEXP x_ ){
   Rcpp::NumericVector x(x_) ;
   double res = 0.0 ;
   for( int i=0; i<x.size(), i++){
      res += x[i] ;
   }
   return Rcpp::wrap( res ) ;
}

しかし、Rcpp砂糖を使用すると、これをはるかにうまく行うことができます。

using namespace Rcpp ;
SEXP sum( SEXP x_ ){
   NumericVector x(x_) ;
   double res = sum( x ) ;
   return wrap( res ) ;
}

前に言ったように、それはすべてあなたが書きたいコードの種類に依存します。Rcppに依存するパッケージで人々が何をしているのかを調べ、ビネット、単体テストを確認し、メーリングリストで私たちに戻ってきてください。いつでも喜んでお手伝いさせていただきます。

于 2010-11-08T12:32:46.650 に答える
20

@jbremnant:その通りです。Rcppクラスは、RAIIパターンに近いものを実装します。Rcppオブジェクトが作成されると、コンストラクターは適切な手段を講じて、基になるRオブジェクト(SEXP)がガベージコレクターから保護されるようにします。デストラクタは保護を撤回します。これは、Rcpp-intrductionビネットで説明されています。基盤となる実装は、RAPI関数R_PreserveObjectおよびR_ReleaseObjectに依存しています。

実際、C++カプセル化によるパフォーマンスの低下があります。インライン化などでこれを最小限に抑えるようにしています...ペナルティは小さく、コードの記述と保守にかかる時間の増加を考慮すると、それほど重要ではありません。

RcppクラスFunctionからR関数を呼び出すのは、Capiを使用してevalを直接呼び出すよりも時間がかかります。これは、予防策を講じて関数呼び出しをtryCatchブロックにラップし、RエラーをキャプチャしてC ++例外にプロモートし、C++の標準のtry/catchを使用して処理できるようにするためです。

ほとんどの人はベクトル(特にNumericVector)を使用したいと考えており、このクラスのペナルティは非常に小さいです。examples / ConvolveBenchmarksディレクトリには、R-extsの悪名高い畳み込み関数のいくつかのバリエーションが含まれており、ビネットにはベンチマーク結果があります。Rcppを使用すると、RAPIを使用するベンチマークコードよりも高速になります。

于 2010-11-08T12:12:18.190 に答える