コラム: パスのお話

kenji sato

2018/09/28

前提知識

Path(パス)について

少し寄り道をしてファイルの位置を表すルールと関連する用語について確認しておきましょう。 ここで扱う内容はRに関する解説ではありませんが,非常に大切なトピックなので目を通しておくことを強くおすすめします

みなさんが普段 Microsoft Windows をお使いなら,

C:¥Users¥username¥Documents

あるいは

C:\Users\username\Documents

といった表現を見たことがあると思います。これは

というような意味だということはエクスプローラーを使っていれば想像ができると思います。 このように,ファイルやフォルダがある位置を指定するテキスト表現のことを「パス」(Path) といいます

フォルダ という言葉を使う代わりに ディレクトリ という言葉を使うことがありますが,両者は同じことを表していますので違いについて深く考える必要はありません。

パスの表記に関するルールを説明する前に「作業ディレクトリ」という用語について説明しておきましょう。

作業ディレクトリ

R のようなコマンドを通じてコンピュータを操作するようなシステムでは「自分がいまどのディレクトリで作業しているか」を意識しておく必要があります。なぜこんなことを考えないといけないかというと

  • 「あのファイル」に書かれてある命令を実行してほしい
  • 出力結果を「あのディレクトリ」に保存してほしい

といったことをコンピュータに対して要求するときに必要になるからです。「あのファイル」「あのディレクトリ」を正しく指図できないと,「ファイルが読み込めない」とか「結果がどこに行ったかわからない」といったことが起こってしまいます。

あなたが今作業をしているディレクトを作業ディレクトリ/作業フォルダ, ワーキング・ディレクトリ, working directory),カレント・ディレクトリ (current directory) などのように呼びます。

これを R上で知るには “get working directory” という意味のコマンド

> getwd()

を使います。さらに,次のコマンドで作業ディレクトリの中にあるディレクトリ・ファイルの一覧を(隠しファイル・フォルダも含めて)表示することができます。

> list.files(all.files = TRUE)

ちなみに,TRUE というのは「真偽」の「真」を表す定数です。「all.files という設定項目をオンにして dir() 関数を実行してくださいね」というような意味です。

練習 1

  1. getwd() で表示されたフォルダにエクスプローラーを使って移動してみてください。
  2. そこに list.files(all.files = TRUE) で表示されたファイル・フォルダが実際にあることを確かめてください。

絶対パス・相対パス

パスというのは,ファイルやフォルダの位置を表す「住所」のようなものです。ドライブ(C:¥)を起点にして表す「絶対パス」と,作業ディレクトリを起点にして表す「相対パス」があります。

絶対パス (absolute path)

絶対パスはドライブ(C:¥)を表す記号を起点として,

C:¥Users¥username¥Documents¥rclub.R

のようにファイルやフォルダを指し示す表記方法です。ただし,R ではバックスラッシュ`` や 円マーク の代わりにスラッシュ / を使うことができます。Mac や Linux と共通の表記になりますので,スラッシュを使用することを推奨します。

C:/Users/username/Documents/rclub.R

絶対パスの利点として,作業ディレクトリの場所にかかわらず共通の方法でファイル・ディレクトリを指定できることが挙げられます。欠点としては,自分以外の人にプログラムを使ってもらう場合にうまく行かない場合があります。

フォルダの絶対パスを確認する方法

Windows のエクスプローラーではフォルダの絶対パスは次の図のようにすれば,確認できます。

Fig: エクスプローラーで絶対パスを確認する

Fig: エクスプローラーで絶対パスを確認する

Mac (macOS 10.11+)のFinder の場合は,

  • ファイル・フォルダを右クリックしてメニューを表示させます。
  • Optionキーを押し続けて Copy …. as Pathname (英語の場合)を表示させる
  • これをクリックしてクリップボードにコピーする
  • どこでもいいので,テキストを入力できる欄にペーストする。

練習 2

Windows の使用を想定しています。Mac の場合は適切に読み替えてください。

  1. getwd() で表示されたディレクトリにエクスプローラーで移動してください
  2. その場所で,右クリックで表示されるメニューを使って「新規テキストファイル」(new.txt としましょう)を作成してください。
  3. ファイルを開いて,なんでもいいので適当に(ポエムなど)書いて保存・終了してください。
  4. そのファイルを表す「絶対パス」を書き出してください。
  5. 次のコマンドを実行して,先程書いたこと(ポエム)が表示されることを確認してください。

    > file.edit("C:/absolute/path/to/new.txt")
    • パスの部分(C:/..../new.txt)は適切に置き換えること。
    • 引用符で括ることを忘れないこと。

これで絶対パスについては終了です。

ちなみに,file.edit() を実行したときに表れた,テキストが表示されている部分をエディター (テキスト・エディター)と呼びます。詳しくは Lesson 1-2: はじめてのRスクリプト を参照してください。

macOS や Linux などのUNIX系のOSでは絶対パスの起点は “/” となります。これを ルートディレクトリ と呼びます。したがって,絶対パスは次のような形式を取ります。

/Users/username/Documents/new.txt

Windows上のRでも / を使うことができます。

> normalizePath("/")

と打ち込んでみてください。スラッシュを引用符でくくることを忘れないでください。

"C:\\" という結果が帰ってきましたか?バックスラッシュまたは円記号が2つ重なっていますが,Windows のドライブを表す記号に(ほぼ)一致していますね。なぜ2つ重ねるかは今はひとまず無視しておいてかまいません。この理由はおいおい分かります。

相対パス (relative path)

相対パスというのは,現在の作業ディレクトリを起点にしてファイルを指定する方法です。例えば,現在作業しているディレクトリの構造が以下のようになっているとします。(スラッシュ / で終わっているものがディレクトリです)

HOME/
├── Data/
│   └── numbers.txt
├── RClub/ ← これが作業ディレクトリだとする
│   ├── R/
│   │   └── script.R
│   └── RClub.Rproj
└── note.txt
  • HOME ディレクトリには DataRClub という2つのディレクトリと, note.txt というファイルが入っている
  • Data ディレクトリには numbers.txt というファイルが入っている。
  • RClub というディレクトリには R というディレクトリ (その中には script.R というファイル)と RClub.Rproj というファイルが入っている

という状態です。ここで, RClub が作業ディレクトリであると考えてください。 必要であれば同じ構造を再現してみてください。(HOME.zip をダウンロード→展開して,HOME/RClub/RClub.Rproj をダブルクリックして RStudio を起動すればOK)

作業ディレクトリの中にあるディレクトリ・ファイルを指定するには

"R"        "R/script.R"

という表現を用います。「作業ディレクトリ」(あるいは,特定のディレクトリ自身を表す)を表す特別な記号 . (ドット)を用いて,

"./R"      "./R/script.R"

を使うことができます。

それでは,作業ディレクトリの下にないファイルやディレクトリを指定するにはどうすればよいでしょうか?さきほど list.files(all.files = TRUE) を実行したときに ... という見慣れない(?)ものが出てきたことに気がついたでしょうか。. はすでに説明した「作業ディレクトリ」のことでしたね。

もう1つドットを増やした .. (ドットドット)は「親ディレクトリ」を表す特別な記号です。 RClub に対する「親ディレクトリ」は「HOME」という名前のディレクトリです。この親ディレクトリを起点にすれば,Data/note.txt も含まれていますから,

"../Data/numbers.txt"     "../note.txt"

といった表現で,作業ディレクトリの隣にあるディレクトリの中のファイルを指定することができます。

このように,作業ディレクトリを起点にして表示されたパスを「相対パス」と呼びます。 相対パスは normalizePath() を使うと絶対パスに置換できます。

> normalizePath("..")

setwd() を避ける

RStudio の“Files”ペインを辿って “Set As Working Directory” を使うとワーキング・ディレクトリを変更することができます。あるいはコマンド setwd() を使っても同じことができます。この機能を使うと,パスの表記ルールを知らなくてもコードを実行できるので,利用している人も多いかもしれません。

しかし,作業ディレクトリの変更を頻繁に行うと,作業ディレクトリの把握が困難になりますし,また「作業ディレクトリを移動する」という操作をデータ分析のプロトコル(手順)に入れてしまうことは分析の再現性の観点からも望ましくありません。

この機能は極力使わないようにしましょう。私のおすすめは,次の約束にしたがうことです。

  • プロジェクトごとにフォルダを作る
  • 「ワーキング・ディレクトリの変更」は最初の1回だけ,プロジェクトの作業を 開始するときだけ実行する
  • コードや共有可能なデータは作業ディレクトリを起点とした相対パスを 利用して読み書きする
  • 共有不可能なデータは “~” (後述)を使った絶対パスを利用して読み込む

ホームディレクトリ

ディレクトリに関連して「ホームディレクトリ」とか「ホーム」「HOME」に関して説明しておきます。「ホームディレクトリに新しいフォルダを作ってください …」などのように使います。ユーザーが作成した文書やファイルを置く場合は,通常この「ホームディレクトリ」に置くことが期待されます。デスクトップのショートカットから RStudio を起動すると,通常,ホームディレクトリが作業ディレクトリになるのはそのためです。

自分のコンピュータのホームディレクトリの絶対パスを知っておくことは重要です。次のコマンドを実行してください。

> Sys.getenv("HOME")
## [1] "/Users/kenjisato"

Sys.getenv() は R からシステムの設定を読み取るための関数です。(ときどきお目にかかります)。ご覧のようにホームディレクトリにはユーザー名(上の例では kenjisato)が入っています。コンピューターを利用しているユーザーのファイルを格納しているディレクトリであるということです。

ユーザー名が変わるとホームディレクトリのパスが変わってしまうと,複数の人が参加しているプロジェクトでは大変面倒なことになります。あるいは,次のようなケースでも,いちいちコードの書き換えが必要になるかもしれません。

  • プロジェクトのソースコード(プログラム)は個人のコンピュータで開発したいのだけど,
  • プロジェクトのデータには社外秘の情報が含まれるので社内の特定のコンピュータから出しては いけない

環境を移してもコードがそのまま使えるようにするのが望ましいのです。そのためには,ユーザー名を含まないようにしなければなりません。

解決のための1つの方法は,Sys.getenv("HOME") を使うということです。しかし,この問題は人類すべてにとって重要なので,ホームディレクトリには ~ という省略名が付けられています。こちらを使うのが普通です。

~ が表しているものを確認するために,次のコマンドを実行してみてください。(normalizePath("~") でもOK)

> path.expand("~")
## [1] "/Users/kenjisato"

大文字と小文字の区別

R は大文字と小文字を区別します。すなわち,Sys.getenv()sys.getenv()Sys.Getenv() と同じではありませんし,HOMEhome と同じではありません。

sys.getenv("HOME")
## Error in sys.getenv("HOME"): could not find function "sys.getenv"

sys.getenv() という関数が存在しないというエラーが出ています。人間には同じに見えても,R にとっては存在しない関数なのです。

Sys.getenv("home")
## [1] ""

エラーではありませんが,home という環境変数がないため,なにも表示されませんでした。

まとめ

用語 記号 R で絶対パスを確認する方法
作業ディレクトリ
カレントディレクトリ
. getwd()
normalizePath(".")
親ディレクトリ .. normalizePath("..")
ルートディレクトリ /
Winows の標準はC:¥
normalizePath("/")
ホームディレクトリ ~ path.expand("~")
normalizePath("~")
Sys.getenv("HOME")