Igor Pro #1: Waveの扱い方
Igor Proは、WaveMetrics社が提供しているデータ解析・グラフ作成ソフトウェアです。GUIによる直感的操作で解析や作図をインタラクティブに実行でき、マクロや関数をプログラミングすることで複雑な処理も自動化できます。また、機能拡張を行うと外部装置を容易に制御することもできます。
この記事では、Igorの基本的で独自的な概念であるWaveの扱い方(CUI)について大切だと思うコトを断片的に綴っていく予定です。内容はIgor Pro version 8のマニュアルを参考にしています。
1. Waveの概念
Igorでは、ユーザーが扱うデータ系列をWaveという型のオブジェクトとして保存することができます。このオブジェクトは、他のプログラミング言語でいう配列と同様に扱われますが、構造体のようにいくつかのコンポーネントで構成されています。
例えば、Fig.1 左のようにグラフ表現される情報は、Fig.1 右のように一つのオブジェクトとしてまとめて保存することができます。Waveオブジェクトの構成要素は大まかに列挙すると、Name(オブジェクト名)、Data (例ではY軸系列を格納する配列)、Scale(例ではX軸系列の初期値とインクリメント)、Unit(軸ラベル)です。つまり、Waveオブジェクトは、データ系列とそれをグラフ表現するための付加的情報で構成されているとも言えます。
2. Waveの作成
2.1. Make & SetScale
Waveオブジェクトの作成方法はいくつもありますが、Makeコマンドを使用するのが基本です。例としてFig.1のWaveオブジェクトを作成してみます。まず、IgorのCommand Windowを開いて次のコマンドを入力します。
Make /O curve = {0, 0.5, 0.87, 1, 0.87, 0.5, 0, -0.5, -0.87, -1, -0.87, -0.5, 0}
print curve
//output: curve[0]={0, 0.5, 0.87, 1, 0.87, 0.5, 0, -0.5, -0.87, -1, -0.87, -0.5, 0}
Makeコマンドの入力形式は Make [flags] WaveName です。Igorでは、[]で囲われている項はオプション(指定しなくても実行可)、その他の項はコマンドを実行するために必要な引数です。上の1行目では、"curve"と名付けたWaveオブジェクトを作成し、そのコンポーネントであるデータ配列に数値系列{0, 0.5, 0.87, …, 0}を格納(初期化)しています。/Oはオプションフラグの一つで、これをMakeコマンドに付け足すことによってオブジェクトの上書き作成(Overwrite)を許可します。2行目は、printコマンドを使用してcurveに格納したデータを確認のために表示させています。
Makeコマンドを実行した時点では、軸のスケールやユニットの設定はデフォルトのままです。デフォルトでは、Makeで定義されたデータ配列のindex (Igorではpoint numberという)がX軸に割当てられ、軸のユニットは未設定状態(Display curveと実行するとグラフ上で確認できます)。これらを設定するためのコマンドはSetScaleです。上のコマンドに続けて次の二行を実行します。
SetScale /P x, 0, 30, "Degree", curve
SetScale /P y, 0, 0, "Amplitude", curve
SetScaleコマンドの入力形式は SetScale [flags] Dim, Start, Num [,Unit] [,WaveName] です。Dim は対象とする次元(軸)です。WaveNameを指定しない場合は直近に作成されたWaveオブジェクト名が使用されます。フラグが/Pの場合、StartとNum、UnitはそれぞれDimで指定した軸の開始値とインクリメント、ユニット(文字列)です。1行目ではDim = x, Start = 0, Delta = 30と設定しているのでX軸系列は{0, 30, 60, …, 360}となります。なお、スケールを設定できるのは独立変数を表す軸(ここではX軸)のみです。そのため、2行目のようにDim = yの場合は、フラグやStart、Numをどう設定しようとも、ユニットの設定のみが反映されます。
2.2. Waveを効率的に初期化する
2.1に記載したように中括弧{}内に数値を列挙して初期化する方法もありますが沢山列挙する場合は大変です。Igorでは、数値系列に何かしら規則性があれば、より効率的に書き表せます。例えば、{0, 1, 2, 3, 4}という数値系列で初期化する場合は次のように書くことができます。
Make /O/N=5 mywave = p
print mywave
//output: mywave[0] = {0, 1, 2, 3, 4}
1行目の/Nは、データ配列の長さ(要素数)を指定するフラグです。ここでは要素数を5としています。pは、格納先データ配列("="の左側)のpoint number(index)を配列として返すコマンドです。mywaveの要素数は5なので、mywave = pという処理は、mywave = {0, 1, 2, 3, 4}と同じ結果になります。また、pに対しては次のように四則演算を適用することもできます。
Make /O/N=5 mywave = 0.1 * p + 1
print mywave
//output: mywave[0] = {1, 1.1, 1.2, 1.3, 1.4}
さらに、pを関数の引数とすることも可能なので、次のように少し複雑な数値系列で初期化できます。
Make /O/N=13 mywave = sin(2 * pi * p / 12)
print mywave
//output: mywave[0] = {0, 0.5, 0.866025, 1, 0.866025, 0.5, 1.22465e-16, -0.5, -0.866025, -1, -0.866025, -0.5, -2.44929e-16}
2.3. 空(から)のWaveを作る
データ配列サイズがゼロのWaveを作成するには1行目のように書きます。作成したWaveをprintコマンドで表示させると、NaN(Not a Number)が表示され未定義状態であることがわかります。配列要素を後から追加する(Pythonでいうappendを実行する)には、5行目以降のように中括弧{}で追加する要素を囲います(もしくはConcatenateコマンドなどを使います)。
Make /N=0 mywave
print mywave
//output: mywave[0] = {NaN}
mywave[0] = {1.0}
mywave[1] = {1.1}
mywave[2] = {1.2, 1.3}
print mywave
//output: mywave[0]= {1.0, 1.1, 1.2, 1.3}
ちなみに、配列サイズを超えるindexに要素を追加しようとすると、次のように配列がゼロ埋めされます。
Make /N=0 mywave
mywave[5] = {1.5}
print mywave
//output: mywave[0]= {0,0,0,0,0,1.5}
2.4. 文字列リテラルを使用してWaveを作る
Wave以外にもデータ型があります。数値を格納するVariableや文字列を格納するStringなどが挙げられます。String型変数を初期化する際は、次のコードの1行目のように、文字列をダブルクォーテーション(")で括ります("で括られた文字の並びを文字列リテラルといいます)。次のコードの1行目を実行すると、String型変数nameに"mywave"が格納されます。この文字列リテラルを名前とするWaveオブジェクトを作成するには、2行目のようにString型変数に$マークを付けます。$を付けずに Make/O name = … と書いてしまうと、"name" と名づけられたWaveオブジェクトが作成されてしまいます。
String name = "mywave"
Make /O $name = {0, 1, 2, 3, 4, 5}
String型変数を使わず、文字リテラルをMakeコマンドの引数として直接渡す場合は、次のように丸括弧()で文字リテラルを括ります。
Make /O $("mywave") = {0, 1, 2, 3, 4, 5}
また、複数の文字リテラルは+演算子を使って次のように結合できます。
Variable n = 15
Make /O $("mywave" + num2str(n)) = {0, 1, 2, 3, 4, 5}
一行目ではVariable型変数 nに数値15を格納しています。二行目のnum2strは数値を文字に変換して返す関数です。したがって、実行結果として、「mywave15」というWaveオブジェクトが作成されます。
2.5. 指定したデータフォルダにWaveを作る
IgorにはData Folderという概念があります。フォルダを作成することでWaveや変数を階層的に管理でき、同じ名前をもつWaveや変数を保存先のフォルダで区別することもできます。新たなワークスペース(Experiment)を開いた時点でのカレントフォルダ(作業中のフォルダ)はroot:です(Igorではパスの区切り記号はコロンです)。試しに「wavebox」という名前のフォルダをroot直下に作成します。フォルダを新たに作成するにはNewDataFolderコマンドを使います。
NewDataFolder root:wavebox
或るフォルダ にWaveオブジェクトを作成および保存するには、次のように保存先を示すパスを明記します。ここでは絶対パスで指定します。
Make/O root:wavebox:mywave
//相対パスで指定する場合は Make/O :wavebox:mywave
なお、カレントフォルダ にWaveオブジェクトを作成する場合は、上で記述してきたようにパスを省略することもできます。カレントフォルダを確認するコマンドはpwd、移すコマンドはSetDataFolderです。
3. Waveの呼び出し
3.1. 関数内からWaveを呼び出す
ユーザーが関数を定義するときは、次のようにProcedureファイル(.ipf)に書きます。Waveオブジェクトは原則としてグローバル変数のように扱われるので、既存のWaveオブジェクトを関数内から呼び出すときは引数として渡す必要はなく、11行目のようにWAVEに続けてオブジェクト名を指定します。
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3
//以下のコードをProcedureファイルに記述(追記)する
Function MakeWave() //mywaveというWaveオブジェクトを作成する関数
Make/O/N=5 mywave = p
End
Function PrintWave() //mywaveを呼び出し、そのデータ配列を表示する関数
WAVE mywave
print mywave
End
Procedureファイルで定義した上記の関数は、Command Windowに次のように入力すると実行できます。
MakeWave()
PrintWave()
//output: mywave[0]={0,1,2,3,4}
3.2. 関数に文字列を渡し、呼び出すWaveを指定する
関数内に呼び出すWaveオブジェクトは、次のように文字列をその関数の引数として与えることで指定することができます。
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3
//以下のコードをProcedureファイルに記述する
Function MakeWave() //二つのWaveオブジェクトを作成する関数
Make/O/N=5 mywave_even = 2*p
Make/O/N=5 mywave_odd = 2*p+1
End
Function PrintWave(name) //nameで指定されたWaveオブジェクトのデータ配列を表示する。
String name //引数型の宣言
WAVE mywave = $(name)
print mywave
End
上記の関数PrintWaveでは、nameに格納された名前(文字列)のWaveオブジェクトを呼び出し、それを関数内限定でmywaveという名称で扱います。これらの関数をCommand Windowから呼び出した例がこちらです。
MakeWave()
PrintWave("mywave_even")
//output: mywave_even[0]={0,2,4,6,8}
PrintWave("mywave_odd")
//output: mywave_odd[0]={1,3,5,7,9}