こんにちは、Rayです。
今回は、MATLABでファイルやフォルダのパス指定についてお話しします。
- PCを乗り換える
- データをPCからHDD等に移動する
- 他の人とデータを共有する
などなど、MATLABのコードやデータが置かれているパスを変更せざるを得ない場合があります。
こんな時、すべてのパスを絶対パスで指定していると悲惨なことになります!
相対パスを利用することで解決できることも多いですが、ファイル同士がかなり離れた位置にあるような場合には不向きです。
WindowsとMacではホームディレクトリのパスが違うのも厄介なポイントです。
これらの問題をもれなく経験した私が、現在どのように対処しているか紹介します。
ぜひ参考にして、パス問題に悩まされないMATLAB使いになってください!
少し長いですが、同じ問題を抱えている人にとって必ず役に立つ情報だと思います!
パス問題
簡単な例
MATLABで計算した結果を保存したmatファイルをload関数で読み込む場合を考えます。
AliceとBobがdata.matという名前の同じファイルを持っているとします。
彼らのPC上において、ファイルの完全なパスは、
Alice:C:/Users/Alice/data.mat
Bob:C:/Users/Bob/data.mat
となっています。
このデータはMATLABで、
load('C:/Users/Alice/data.mat')
を実行すれば、Aliceはデータを読み込むことができます。
しかし、Bobは読み込めません。
何を当然なことを…
と思われると思いますが、これが結構厄介な問題なんです。
Bobがデータを読み込みたい場合、ファイル名を指定している部分をすべて書き換える必要があります。
読み込みたいデータのパス名をすべて書き換えるなんてやってられないので、最初から工夫しておきましょう!
パス問題が起こるケース
パス問題は様々な場面で起こり得ます。
- 他者とデータ(スクリプト)を共有する場合
- PCを乗り換える場合
- データの保存先を変更する場合
①は前項で挙げた例が該当します。
自分が書いたコードを他者に共有することは多いと思うので注意が必要です。
②は、特にWindows・Mac間での乗り換え時に大問題になります。
同一OS間であっても、ユーザー名が変わったりするとパスは変わります。
③は、PCの容量がいっぱいになったからHDDやクラウドストレージ(DropboxやGoogle Driveなど)に移そう!という場合です。
相対パスを利用する方法
これは最も簡単で、よく使われる方法だと思います。
実装例と、この方法の欠点を説明します。
相対パスを利用する例
読み込みたいデータのパスが、
C:/Users/Alice/matlab/data/data.mat
で、データを読み込むスクリプト(readdata.m)のパスが、
C:/Users/Alice/matlab/code/readdata.m
だとします。
(current directoryは C:/Users/Alice/matlab/code とします)
この場合、readdata.mの中で、
load('../data/data.mat')
と記述することでdata.matを読み込むことができます。
他者に共有する場合や、ファイルを移動する場合には、matlabフォルダごと渡すことでパス問題は生じません。
もちろん、matlabフォルダを置く位置は、ホームディレクトリ直下でなくても構いません。
相対パスの欠点
相対パスを利用することの欠点を挙げます。
相対パスは便利なので私もよく使いますが、欠点も理解した上で適切に利用しましょう!
- current directoryをreaddata.mが置かれているディレクトリに変更する必要がある
- ファイル同士の位置関係を完全に把握する必要がある
- PCからHDD等のデータにアクセスできない
それぞれについてもう少し詳しく説明します。
①current directoryの変更について
前項「相対パスを利用する例」の場合、readdata.m内で、
load('../data/data.mat')
を実行するためには、readdata.mが存在する
C:/Users/Alice/matlab/code
にcurrent directory(現在のパス)を変更する必要があります。
まったく別のディレクトリで作業をしているときにreaddata.mを呼ぼうとすると、その度にcurrent directoryが変わってしまいます。
これはかなり煩わしい…!
②ファイル同士の位置関係を完全に把握する必要がある
前項「相対パスを利用する例」のように、data.matとreaddata.mが近い階層に存在する場合、相対パスはとても便利です。
一方で、ファイル同士がかなり離れた位置に存在する場合には不向きです。
例えば、
load('../../../../data/subjectA/setC/2022/03/01/run3/data.mat')
みたいなコードになってしまいます。
ディレクトリの階層を完全に把握できていないと
4階層前の、dataフォルダの…
あれ?3階層前だったっけ?
のように混乱して、正しいコードを書くことが難しくなります。
また、他者にデータを共有する場合や、データを移動する場合、かなり上の階層のフォルダをすべて共有・移動することになります。
上の階層になるほど含まれるデータの量は増えるので、扱いにくいのは明白です。
③PCからHDD等の他のデバイスのデータにアクセスできない
データ解析用のスクリプト(readdata.m)はPC、解析するデータ(data.mat)はHDDに保存してあることは多いと思います。
この場合、相対パスを使ってHDD上のデータにアクセスすることはできません。
いや、できないことはないですね。
- 「../」を繰り返して、HDDがマウントされたディレクトリまで戻る
- HDD内のディレクトリにcurrent directoryを変更する
などの方法はあります。
しかし、1つ目の方法は絶対パスを使うよりも煩雑になるだけですし、2つ目の方法はcurrent directoryが変わる煩わしさが生じます。
実質、相対パスを使ってHDD上のデータにアクセスすることはできないと言って差し支えないかなと思っています。
OSに依らずホームディレクトリのパスを取得する方法
MATLAB上でJavaの関数を呼び出すことができます。
このうち、
java.lang.System.getProperty('user.home')
を実行することで、ホームディレクトリのパスをString配列として得ることができます。
ユーザー名がAliceだとすると、
- Windowsの場合:”C:/Users/Alice”
- Macの場合:”/Users/Alice”
- Linuxの場合:”/home/Alice”
といった感じです。
コンピュータの環境変数を読み取っているのだと思います。
MATLABをインストールしたLinux機が手元にないので、Linuxの場合の出力はあくまで予想です。
これを使うと、
load([char(java.lang.System.getProperty('user.home')), '/data/data.mat'])
とすることで、OSやユーザー名に依らず、data.matを読み込むことができます。
つまり、ホームディレクトリ以下の構造さえ揃えておけば、実験用のLinux、解析用のWindows、自分用のMacのすべてで同じスクリプトを利用可能になります。
色々なディレクトリに簡単にアクセスする方法
前項では、ホームディレクトリのパスを取得する方法を紹介しました。
しかし、毎回
java.lang.System.getProperty(‘user.home’)
と入力するのは少し長くて面倒だと思いませんか?
また、ホームディレクトリ以外にも簡単にアクセスできたら良いなと思いませんか?
例えば、
- データを格納しているフォルダ
- 解析スクリプトを格納しているフォルダ
- 解析結果を格納しているフォルダ
などです。
私は、この目的を達成するために関数を自作して使用しています。
この関数を公開し、紹介しますので、ご自由にお使いください。
mypath関数【自作関数】
次のようなディレクトリ構造を考えてみます。
(コンピュータはMac、ユーザー名はray(私の名前です)としています)
ホームディレクトリにはAnalysisフォルダとExperimentsフォルダを配置。
Experimentsフォルダ内には、実験1、2のデータと解析結果を保存するフォルダを配置。
外付けHDDには、実験3のデータと解析結果を保存するフォルダを配置。
この状況で、各フォルダへの絶対パスを簡単に取得するために、次のような関数を作成しました。
function path = mypath(str) % 引数の候補 arguments str string {mustBeMember(str, ... [ "HOME" "ANALYSIS" "EXPERIMENTS" "EXP1" "DATA1" "RESULT1" "EXP2" "DATA2" "RESULT2" "EXP3" "DATA3" "RESULT3" ])} = "HOME" end % strをすべて大文字にする str = string(upper(str)); switch str case "HOME" % ホームディレクトリ path = char(java.lang.System.getProperty('user.home')); case "HDD" % 外付けHDD path = fullfile('/Volumes', 'HDD'); case "ANALYSIS" % 解析スクリプト path = fullfile(mypath("HOME"), 'Analysis'); case "EXPERIMENTS" % 実験データ path = fullfile(mypath("HOME"), 'Experiments'); case "EXP1" % 実験1 path = fullfile(mypath("EXPERIMENTS"), 'Exp1'); case "DATA1" % 実験1のデータ path = fullfile(mypath("EXP1"), 'data'); case "RESULT1" % 実験1の解析結果 path = fullfile(mypath("EXP1"), 'result'); case "EXP2" % 実験2 path = fullfile(mypath("EXPERIMENTS"), 'Exp2'); case "DATA2" % 実験2のデータ path = fullfile(mypath("EXP2"), 'data'); case "RESULT2" % 実験2の解析結果 path = fullfile(mypath("EXP2"), 'result'); case "EXP3" % 実験3 path = fullfile(mypath("HDD"), 'Exp3'); case "DATA3" % 実験3のデータ path = fullfile(mypath("EXP3"), 'data'); case "RESULT3" % 実験3の解析結果 path = fullfile(mypath("EXP3"), 'result'); otherwise error('指定されたフォルダはMyPath関数に登録されていません.') end % エラー処理 if exist(path, 'dir') ~= 7 warning('指定されたフォルダは存在しません.\n%s', path) end end
このmypath関数はこちらよりダウンロード可能です。
この関数を使って、実験2のdata_xxx.mat
/Users/ray/Experiments/Exp2/data/data_xxx.mat
を読み取りたい場合、
load(fullfile(mypath("DATA2"), 'data_xxx.mat'))
というコードを実行すればOKです。
かなりシンプルで使いやすい!
mypath関数の利点
引数の候補を登録している
MATLAB上で
mypath(“EXP
まで入力した状態でtabキーを押すと、
のように、引数の候補が表示されます。
これが割と、コード記述時の負担軽減になって快適なんです!
ホームフォルダのパスを簡単に取得できる
Javaの関数を自分で記述すると、
java.lang.System.getProperty('user.home')
ですが、mypath関数を使う場合、
mypath("HOME")
だけで済みます。
コードは短い方が見やすいですよね
深い階層にあるデータの絶対パスを簡単に取得できる
mypath関数に深い階層のパスを登録しておけば、mypath(“xxx”) だけで完全パスを取得できます。
実験や解析を繰り返していると、どうしてもディレクトリ構造が複雑になりがちだと思います。
よく使うディレクトリのパスを登録しておくと、とても便利です。
ディレクトリの変更に対応しやすい
コンピュータの容量に限界が来たため、実験データをすべて外付けHDDに移す場合を考えてみます。
この場合、mypath関数の
case "EXP2" % 実験2 path = fullfile(mypath("EXPERIMENTS"), 'Exp2');
の部分を
case "EXP2" % 実験2 path = fullfile(mypath("HDD"), 'Exp2');
に変更するだけです。
mypath関数を呼び出しているコード側を変更する必要は一切ありません。
他者に共有する場合や、MacからWindowsに乗り換える場合などにも同様に対応可能です。
mypath関数の注意点
mypath.mファイルを持っていない人には使えない
当然ですが、mypath.mファイルを持っていないと関数は使えません。
他者に共有する場合には、mypath.mファイルも共有する必要があります。
また、自身で使う場合にも、mypath関数にアクセスできる状況でなければ使えません。
mypath関数を置いているディレクトリをMATLABの検索パスに登録しておくことをおすすめします。
検索パスの追加方法は下記の公式ドキュメントに従うと簡単です。
外付けHDDのパスに注意
外付けHDDのパスは、Macの場合は
/Volumes/(HDDの名前)
となりますが,Windowsの場合には、
D:/
などになります。
このようなOSによる違いに対応するためには、ispc関数やismac関数によってOSを判定し、処理を分岐することになると思います。
Windowsの場合には、HDDのドライブレター(DとかEとか)がどのアルファベットになるかは環境によるので、この点についても少し工夫が必要かもしれません。
まとめ
今回は、MATLABで様々なパスにアクセスする方法を紹介しました。
記事内で紹介したmypath関数はダウンロード可能ですので、自由に改変してご使用ください。
私自身はかなり多用しています!
mypath関数は使うつもりがない方も、Java関数を用いてホームディレクトリのパスを取得する方法は必ず覚えておくべきだと思います。
長くなりましたが、誰かのお役に立てれば幸いです。
ご質問などありましたら、Contactページよりお願いします。
コメント