実行したいファイルが保存してあるフォルダを自動で作業フォルダにしコードを実行する方法

昔っからやろうやろうと思っていておざなりになっていたお話。

モチベーション

せっせとコードを書いていると、だんだんファイルが肥大化してくるものでして、当然それを別ファイルに分けて、メインのファイルからsource関数やらでその別に作成したファイルに書いてある関数等をロードして使うわけです。ただR言語は実行ファイルのパスを自動でカレントフォルダ(作業フォルダ)にしてくれないため、ファイルを実行する前に実行したいファイルが格納してあるフォルダをsetwd関数を使って作業フォルダとして設定しておかねばならないわけです。これは面倒くさいぞということでなんとか致したい。

私の現状とその打開策

現在、RStudioをメインの開発環境で作業しており、特にコードを実行する際には「Run Line(s)(Ctrl+Ener)」を使っています。こいつは現在の選択している行、あるいは範囲の内容をコンソールに流し込んでくれるので逐次結果を確認しながら作業を進めていくには大変便利なのですが、上述のように他のファイルをsource関数なりで読み込む際には一度そのファイルがあるフォルダをsetwd関数使って作業フォルダにするか、ファイル名をフルパスで書かなければならなくて、特に後者はコードの再利用や保守性の観点からやりたくないぞという状況。

で、実際どう対処するのかというお話を実例で示す。例はRcppを使ったC++のコードのものだが、R言語のソースであってもほぼ同様でsourceCppで読み込んでいる箇所を単にsource関数にすればよい。まずsource(Cpp)関数で読み込むためのファイルを作成。今回は引数のベクトルをそれぞれ10倍したものを表示するという単純な関数を用意。


<TenTimes.cpp>

#include <Rcpp.h>
using namespace Rcpp;
//TenTimes class
template<class T> 
class TenTimes_ : public std::unary_function<T, void>
{
public:
    TenTimes_() : counter_(0) {}
    void operator() (T x) {
      Rcout << "Iteration:"       << counter_++
            << " 10 times value:" << 10.0*x
            << std::endl;
    }
private:  
    int counter_;
};
// [[Rcpp::export]]
void TenTimes(Rcpp::NumericVector x) {
  std::for_each(x.begin(), x.end(), TenTimes_<double>());
}


続いて同じフォルダに先ほど作成したファイルを読み込んで、そこに定義された関数を使うコードを記述しmain.Rとして保存する。
<main.R>

library(Rcpp)
#Initialize path
frame_files <- lapply(sys.frames(), function(x) x$ofile)
print(frame_files)
frame_files <- Filter(Negate(is.null), frame_files)
print(frame_files)
setwd(dirname(frame_files[[length(frame_files)]]))
#
sourceCpp("TenTimes.cpp")
x <- 1:10
#Show 10 times values
TenTimes(x)

このコードの

frame_files <- lapply(sys.frames(), function(x) x$ofile)
frame_files <- Filter(Negate(is.null), frame_files)
setwd(dirname(frame_files[[length(frame_files)]]))

が味噌で、これでmain.Rが保存してあるフォルダが自動的に作業フォルダへと変更される。


さらにこのmain.Rを実行するため、soure関数にmain.Rを食わせた(Rstudioで【Code】→【Source】 or Ctrl+Shift+S)結果はというと、

> source('~/hoge/main.R')
[[1]]
[1] "~/hoge/main.R"

[[2]]
NULL

[[3]]
NULL

[[4]]
NULL

[[1]]
[1] "~/hoge/main.R"

Iteration:0 10 times value:10
Iteration:1 10 times value:20
Iteration:2 10 times value:30
Iteration:3 10 times value:40
Iteration:4 10 times value:50
Iteration:5 10 times value:60
Iteration:6 10 times value:70
Iteration:7 10 times value:80
Iteration:8 10 times value:90
Iteration:9 10 times value:100

ちゃんと実行ファイル(main.R)のパス(~/hoge)を拾って来た後、処理が動いている様がわかる。
main.Rのprint分は実際には当然いらないもの。

参考LINK

この内容は以下のLINKを参考にした。特に一番上のLINKのhadley(ggplot2作者!)の回答そのまんまである。
私はあまりやらないのでここに記述していないが、コマンドライン上から上述のような内容をやりたい場合は同じく一番上のLINKのSuppressingfire氏の回答を見るとよい。
他のLINKは似たような質問+「R言語でのパス記述、書くあるべし」なお話。

さらに

で、別のやり方をゴミ箱人が提案してくれた!多謝!