Rcpp でファイルの行数をカウントする

新しいサイトでブログを再開してみました。名前はそのうち考えます。

とりあえず、せっかく Rcpp 入門を GitBook でまとめたところなので、布教のために Rcpp の活用例をちまちまと紹介して行こうかなと思います。

記念すべき最初の記事として teramonagi さんの 「ファイルの行数を調べたい」 に応えてみました。

ここでは C++ を用いた2通りの実装を紹介します。 よくみたら Rcpp のクラス全然使ってないですね。でも、これで OK なのです。

Rcpp コード

count_lines.cpp

#include <Rcpp.h>
#include <fstream>

// [[Rcpp::export]]
unsigned int Rcpp_count_lines1(std::string path){
  std::ifstream inFile(path.c_str()); 
  return std::count(std::istreambuf_iterator<char>(inFile), 
                          std::istreambuf_iterator<char>(), '\n');
}

// [[Rcpp::export]]
unsigned int Rcpp_count_lines2(std::string path){
  unsigned int n = 0;
  std::string line;
  std::ifstream myfile(path);
  while (std::getline(myfile, line)) ++n;
  return n; 
}

では、さっそく teramonagi さんの記事を参考に wc、 R.utils::countLines( ) と比較してみましょう!

Rcpp::sourceCpp('count_lines.cpp')
microbenchmark::microbenchmark(
    system("wc -l hoge.csv"),
    R.utils::countLines("hoge.csv"),
    Rcpp_count_lines1("hoge.csv"),
    Rcpp_count_lines2("hoge.csv"),
    times=5)

実行結果

Unit: seconds
                            expr       min        lq      mean    median        uq       max neval
        system("wc -l hoge.csv")  1.225005  1.240038  1.519176  1.317884  1.436273  2.376679     5
 R.utils::countLines("hoge.csv") 13.437869 14.185085 15.571043 15.480933 16.912233 17.839095     5
   Rcpp_count_lines1("hoge.csv")  2.959419  3.066835  3.308570  3.261107  3.618280  3.637206     5
   Rcpp_count_lines2("hoge.csv")  8.088860  8.184448  8.447557  8.358582  8.715715  8.890180     5

うーん、残念! wc には負けてしまいました。さすがに wc 速いですね。でも R.utils::countLines() には余裕で勝ちました。C++ でも std::getline() より std::istreambuf_iterator を使った方法のほうが倍以上高速なようです。

ただ wc と Rcpp_count_lines1 は改行コードを数えているだけなので、最後の行のラストに改行がない場合には、行数が1つ少なくカウントされてしまうのが、ちと残念ですね。Rcpp_count_lines2 の getline の方はちゃんとカウントしてくれます。

C++ というと機械学習アルゴリズムの実装のような高度な用途にだけ使うものだろうと思われるかもしれないですが、今回の例のように、ちょっとした日常業務のツールを作成する場面でも案外役に立ってくれそうです。

というわけで最初の記事は以上です。いかがでしたでしょうか? これからこのような感じで Rcpp の活用例を紹介してゆきたいと思います。

ではでは〜。