ソフトウェア/Igor/システムの基本 のバックアップの現在との差分(No.7)
更新- 追加された行はこの色です。
- 削除された行はこの色です。
[[ソフトウェア/Igor]] #contents * 初期画面 [#qa5cfefe] Igor を起動すると、次のような画面が現れます。 &ref(igor-window.png,,50%); - 上はデータを入力したり、表示したりするテーブル - 下は後で説明するコマンドウィンドウです ** データを入力してウェーブを作る [#n982e9d3] 上のテーブルの所に適当に10個の数値を入力しました。 &ref(wave0-table.png,,50%); 上には勝手に wave0 という名前が付きました。 グラフ化するような一連の数値データのことを Igor ではウェーブと呼んでいます。 ** データブラウザ [#pc089e06] で、wave0 というのは、今入力したウェーブに自動的に付けられた名前です。 Igor では複数のウェーブに好きな名前を付けてメモリ上に保持できます。 現在メモリ上に存在するウェーブ(やグローバル変数)の一覧を表示するには、 [データ]-[Data Browser] を使います。 &ref(data-browser.png,,50%); 現れた Data Browser は root の直下に wave0 というウェーブが存在することを示しています。 左の Plot にチェックが入っていれば、wave0 をクリックして選択するだけで下の部分にグラフ概形が表示されます。 &ref(data-browser2.png,,50%); - wave0 の部分をダブルクリックすると新しいテーブルが - プロットの部分をダブルクリックすると新しいグラフが 表示されます。 Igor では1つのウェーブを複数のテーブルやグラフに表示できるので、 うかつにダブルクリックすると、どんどん新しいグラフやテーブルが作成されてしまいます。 &ref(many-tables-graphs.png,,50%); グラフやテーブルはデータそのものではなく、データを表示するためだけの物なので、 グラフウィンドウを閉じてもデータそのものは失われません。 ですので、不必要なグラフは閉じて構いません。 で、グラフを閉じようとすると、何かを保存するかどうか聞かれるのですが、 これは、「今閉じようとしているグラフとまったく同じグラフ(大きさ、色、太さ、表示範囲など) を作るためのおまじない」を保存するかどうか聞かれているものです。 &ref(close-graph.png,,50%); 必要なければ無視して [保存しない] を選んで大丈夫です。 ** ファイルからデータを読み込む [#d058811d] 次のようなテキストファイルを作って、data.txt として保存しましょう。 1 2 4 5 7 5 4 2 1 これを読み込んでグラフにしてみます。[データ]-[ウェーブをロード]-[区切りテキストをロード] から、 &ref(load-data.png,,50%); ウェーブ名は既定値の wave1 のままにして [読み込み] すると、 &ref(load-data2.png,,50%); Data Browser に wave1 が表示され、プロットをダブルクリックすることでグラフを表示できました。 &ref(wave1-loaded.png,,50%); * もっともお手軽な定型処理の仕方 - コマンドラインへの貼り付け [#vde5fae2] 複数のグラフの見た目を揃えたい場合など、 ある動作を繰り返し行いたい場合に使える最もお手軽な方法は、 コマンドウィンドウに(複数の)コマンドをクリップボードから貼り付けて実行するやりかたです。 ** コマンドウィンドウとは [#b70f7a04] コマンドウィンドウというのは、~ [ウィンドウ]-[コマンドウィンドウ]~ を選択するか、もっと普通には~ Ctrl+J~ を押すと現れる、 &attachref(ソフトウェア/Igor/IgorCommandWindow.PNG,,50%); このウィンドウです。 このウィンドウには「操作のログ」が残ります。 上記の操作をした後であれば、次のようになっているはずで、 &ref(command-window2.png,,50%); - "Display/K=0 wave0" は、wave0 のグラフを作製したこと - "Edit/K=0 'wave0'" は、wave0 のテーブルを作製したこと などを表しています。 実はこの「ログ」はそのまま Igor のコマンドになっていて、 同じ文字列をコマンドウィンドウに打ち込むことで、 以前に行った操作を再度実行することができます。 例えば、"Display wave0" としてみると、新しいグラフが表示されるのを確認できるはずです。 &ref(display-wave0.png,,50%); ** Hello world! [#b2c00dfb] コマンドは他にもたくさんあります。 一旦、Ctrl+N を押してこれまでのデータを消去してから始めましょう。 例えばここに、 LANG:Igor DoAlert 0, "Hello world!" と打ち込んで、最後に ENTER を押すと、 &attachref(ソフトウェア/Igor/CommandWindowHelloWorld.PNG,,50%); のように Hello world! というアラートを表示できます。 ** 複数行のコマンドを貼り付け [#u2155aeb] このコマンドウィンドウには複数行のコマンドを一度に貼り付けられます。 次のテキストをコピーしてコマンドウィンドウに貼り付け、 最後に ENTER を押すと、 LANG:Igor Make/N=100 wave0 = sin(2*pi*x/99*2) Make/N=100 wave1 = cos(2*pi*x/99*3) Display wave1 vs wave0 ModifyGraph width={Plan,1,bottom,left} ModifyGraph mode=3,marker=60 &attachref(ソフトウェア/Igor/CommandWindowDemo.PNG,,50%); このように一連の処理を一度に実行できます。 「名称はウェーブとしてすでに存在しています」となった場合には、 すでに wave0 や wave1 という名前のウェーブが存在していますので、 必要なデータをすべて保存してから Ctrl+N で新規エクスペリメントを作成してからリトライしてみて下さい。 ** いくつもあるグラフの見た目を揃える [#ted95041] 次のコマンドで、ランダムデータからなる4つのウェーブを作成してグラフに表示します。 LANG:Igor make wave0=gnoise(1), wave1=gnoise(1), wave2=gnoise(1), wave3=gnoise(1) display wave0 display wave1 display wave2 display wave3 次の手順で1つのグラフ (Graph3:wave3) の見た目を整えました。 - Graph3 を選択する~ ~ - グラフ上で赤線部分をダブルクリックし、[カラー] で "青" を選択、[モード] を "折れ線とマーカー" にして、[サイズ] を "1"、形状を "●" にする。~ &ref(trace-appearance.png,,50%);~ ~ - [グラフ]-[グラフを修正] 幅を 6cm 高さを 3cm、フォントを Times New Roman 12pt にする~ &ref(modify-graph.png,,50%);~ ~ - [グラフ]-[軸ラベル] から縦横軸にラベルを付ける~ &ref(graph-axes-labels.png,,50%);~ ~ これらを終えると、コマンドウィンドウには一連の操作のログが残っていることを確認できます (黒く選択されている領域)。 &attachref(ソフトウェア/Igor/CommandWindowDemo2.PNG,,50%); で、他の3つのグラフも同様にして見た目を揃えたい場合に、 これらの操作を一々繰り返すのは大変です。 そういう時にこの操作ログが大活躍するのです。 まず上図のように操作ログをマウスカーソルで選択して Ctrl+C でコピーします。 これで操作ログをクリップボードに入れられます。 今の場合、その内容は LANG:Igor *ModifyGraph mode=4,marker=19,msize=1,rgb=(0,0,65280) *ModifyGraph msize=3 *ModifyGraph width=170.079,height=85.0394,gFont="Times New Roman",gfSize=12 *Label left "Value [a.u.]";DelayUpdate *Label bottom "Time [s]" のようになります。~ (始め、マーカーサイズを1にしましたが、小さすぎたので3に直した様子も残っていますね) この内容はコマンドのログでもあるのですが、 同じ動作を行うためのコマンドのリストにもなっています。 ですので、先頭の * を取り除けばそのまま Igor のコマンドとして解釈可能です。 ここで、Graph0:wave0 のグラフを選択し、前面に持ってきてから~ Ctrl+J でコマンドウィンドウを出し、~ Ctrl+V で先ほどコピーしたコマンドリストを貼り付けます。~ (各行の先頭に * が付いていますが気にしなくて大丈夫です) &attachref(ソフトウェア/Igor/CommandWindowDemo3.PNG,,50%); 貼り付け後は上図のように「最終行」しか見えませんが、 実際にはちゃんとすべての行が貼り付けられています。 最後に ENTER を押すと、先ほどのコマンドがすべて実行され、 Graph0:wave0 の見た目が Graph3:wave3 と同じように設定されます。 &attachref(ソフトウェア/Igor/CommandWindowDemo4.PNG,,50%); この操作を Graph1:wave1, Graph2:wave2 についても行うことで、 すべてのグラフの見た目を統一することができました。 &attachref(ソフトウェア/Igor/CommandWindowDemo5.png,,50%); Igor で一連の動作を繰り返すにはこのように、 + まずは一回メニューなどから必要な操作をやってみて、 + コマンドウィンドウのログから必要部分を取りだして、 + 必要に応じてテキストエディタなどで編集して、 -- コマンドウィンドウに貼り付けて実行する~ または -- Ctrl+M で出るプロシージャウィンドウに貼り付けて関数とする というのが一般的な手順になります。 始めからコマンドをすべて覚えていなくても メニューから選んだコマンドの名前をコマンドウィンドウのログから 知ることができるのが良いところですね。 * Igor のマルチスレッド対応 [#w13d601c] Igor Pro 6.1 から、マルチスレッド対応が強化されたそうです。 LANG:Igor wave0 = any_expression(x) のような代入文で時間がかかっている場合、 LANG:Igor MultiThread wave0 = any_expression(x) とするだけで、個々の座標値に対する計算を異なるスレッドで行ってくれます。 当然、wave の個々の座標の値の間に依存関係がある場合にはうまくいきませんが、 独立に計算できる場合にはこれだけでかなり速度が上がります。 ** より高度なマルチスレッド [#fe54477c] もっと複雑なことをしたい場合には、 ThreadGroupCreate / ThreadStart / ThreadGroupPutDF / ThreadGroupGetDF / ThreadGroupWait / ThreadGroupRelease といった関数を使うことで、きめ細やかなマルチスレッド制御を行えます。 実際やってみると、メモリ確保などに時間がかかっていることも多いようで、 単純にスレッド数倍もの速度向上は望めませんが、シングルスレッド比で 2倍くらいの速度は簡単に出せるようでした。 例えば、Intel Core i7-3520M @ 2.9GHz + Windows 7 (64bit) で 1024 本のスペクトルに対して解析を行った結果、 - シングルスレッド = 11.0 秒 - マルチスレッド = 4.3秒 という結果が得られました。 ** 注意点 [#w6cc64fa] このようにマルチスレッドで使う関数、例えば SomeMutiThreadableFunction(x) には ThreadSafe というキーワードを付けて定義する必要があります。 LANG:Igor ThreadSafe function SomeMutiThreadableFunction(x) // マルチスレッド対応コード end このような ThreadSafe な関数からは、同じく ThreadSafe とマークされた関数以外を呼び出せません。 やってみるとすぐ分かるのですが、現在のところ ThreadSafe とマークした関数には デバッガ機能が働かず、ブレークやステップ実行ができません。 シングルスレッドで十分にテストしてからマルチスレッドに移行しないと、 うまく動かないときに面倒なことになります。 デバッグ環境設定のようなところで「Multithread 機能をオフにして、 すべてをシングルスレッド(ノンプリエンプティブマルチスレッド)で動作させる」 なんていうのを選べると、デバッグが楽になって良いんですけどね・・・ 幸い、print や printf は ThreadSafe 関数からも呼べるので、 デバッグにはこれらを使って頑張るしか今のところ手はないようです? * コードのモジュール化 Tips [#gee1c41b] ** static function の利用 [#g3663cd9] モジュール内(1つの .ipf ファイル内)でしか使わない関数には static を付けておくことで、そのモジュールの外から呼び出せなくなります。 この「隠蔽」により、モジュールの利用者がモジュールの使い方を見分けやすくなります。 また、異なるモジュールで関数名が被る心配が無くなるのも強みです。 どうしてもモジュール外部から参照したい場合には、 #pragma ModuleName = ModuleName でモジュールに名前を付けておいて、 ModuleName#FunctionName として参照します。 ** FUNCREF を利用したコールバック [#e0927ac9] FUNCREF を利用したコールバックを使うとコードの依存関係を切り離せる場合があります。 例えば、2次元データの中から条件に合致する点を探し、 そのデータ点に対して処理を行いたい場合に、 LANG:Igor function DoSomethingOnThePoints() wave image variable i, j for(i=0; i<DimSize(image, 0); i+=1) for(j=0; j<DimSize(image, 1); j+=1) if( #condition# ) DoSomething(i, j, image[i][j]) endif endfor endfor end というようなコードが思い浮かびます。 しかし、同様の点に対して、異なる関数 DoAnotherThing(i, j, image[i][j]) を呼び出したい時に、DoAnotherThingOnThePoints() という別の関数を書いて、 そこにループ処理を貼り付けるのはばからしく感じられます。そこで、 LANG:Igor function DoSomethingProcProto(i, j, v) variable i, j, v // DoSomething のプロトタイプなので、実際の処理は何もしない end function DoAnythingOnThePoints(DoSomethingCallback) string DoSomethingCallback FUNCREF DoSomethingProcProto DoSomething = $DoSomethingCallback wave image variable i, j for(i=0; i<DimSize(image, 0); i+=1) for(j=0; j<DimSize(image, 1); j+=1) if( #condition# ) DoSomething(i, j, image[i][j]) endif endfor endfor end この DoAnythingOnThePoints と言う関数に、呼び出して欲しい関数名を与えれば、 任意の関数を同じ手順で呼び出すことができるようになります。 static 関数を渡すときには、モジュール名を付けて "ModuleName#FunctionName" として渡せばOKです。 DoSomethingProcProto は DoSomething という関数リファレンスの「型」、 すなわち、引数リストの型及び返り値の型を規定するためのダミー関数なので、 中身は何も書かなくて構いません。 このように文字列で書いた任意の関数を FUNCREF で呼び出す手法は、 特定のデータフォルダにグローバル変数としてコールバック関数名を書いておくことで コードの動作を替えるなど、様々な使い道があって、コードとコードの依存性を遮断して、 モジュール性を高めるために活用できます。 *** イディオム [#a652091a] モジュール名のデータフォルダの中に特定の文字列変数を用意して、 そこに呼び出してもらいたい関数名を書いておくと適当なタイミングで 呼んでもらえるようにする。 例えばそのモジュールではある画像を表示する役割を持っていて、 画像上をクリックするとその点の座標を引数に、 (登録されていれば)そのコールバック関数を呼ぶような場合。 モジュール名を Module1.ipf とすると、 Module1.ipf 内で、 LANG:Igor // コールバック関数のプロトタイプ // x, y には画像上でクリックした座標が入る function ImageClickProcProto(x, y) variable x, y end // 画像をクリックしたときにモジュール内で呼び出される。 // 必要に応じてユーザー指定のコールバック関数を呼ぶ static function ImageClicked(x, y) variable x, y // モジュール名と同じ名前のデータフォルダー root:Packages:Module1 に // ImageClickProc という文字列変数が定義されていて SVAR/Z ImageClickProc = root:Packages:Module1:ImageClickProc if (SVAR_exists(ImageClickProc)) // その文字列が示す名前のユーザー定義関数が存在するなら if (exists(ImageClickProc)==6) // その関数を呼び出す FUNCREF ImageClickProcProto ImageClickProcRef = $(ImageClickProc) ImageClickProcRef(x, y) endif endif end Module1 を使う側では、 LANG:Igor // 始めに : を付けておくことで Experiment と // 同じフォルダにある Module1.ipf をインクルードする #include ":Module1" // 初期化処理でコールバック関数を登録する function initialize() NewDataFolder/O root:Packages NewDataFolder/O root:Packages:Module1 string/g root:Packages:Module1:ImageClickProc = "ImageClick" end function ImageClick(x, y) variable x, y // 画像がクリックされると呼び出されるので、 // ここで望みの処理を行う end のようにします。 こうすることで、モジュール側のコードをいじることなく、 クリックされたときの動作を差し替えることができるため、 コードの再利用が可能となります。特に、Module1 が ImageClick などのコールバック関数に依存しなくなるため、 モジュールの独立性が向上することがうれしいところです。 *** コールバック関数呼び出し用関数 [#qef16fe8] 以下のような一連の関数を作っておくと、 上記のようなフック関数を呼び出すときに便利に使えます。 動作としては、SVAR の名前(上の例で言えば "root:Packages:Module1:ImageClickProc") を与えると、その SVAR の中身を調べ、そこに関数名が指定されていればそれを呼び出す、 というものになります。 FUNCREF で関数を呼び出すには関数形に合わせてそれぞれコードを書かなければならないですが、 下記のように、~ 引数無しで数値を返す関数を func2N,~ 引数無しで文字列を返す関数を func2S,~ 数値を1つ与えて数値を返す関数を funcN2N,~ 数値を1つ与えて文字列を返す関数を funcN2S,~ 数値を2つ与えて数値を返す関数を funcNN2N,~ 数値を1つ、文字列を2つ与えて数値を返す関数を funcNSS2N,~ などと名前を付けると分かりやすそう。 funcXX2X の形のプロトタイプ宣言だけでもいろいろ使い道がありますし、 また、命名規約さえ決めてしまえばここに無い形の関数も容易に追加できるはずです。 CallbackUtility.ipf LANG:Igor(linenumber) // // svar_name という名の svar で指定される関数があれば呼び出す // default_proc を渡すことで、コールバックが指定されていないときの // 動作を定義可能 // // function CallbackFunc2N(svar_name) // function/S CallbackFunc2S(svar_name) // function CallbackFuncN2N(svar_name, n) // function/S CallbackFuncN2S(svar_name, n) // function CallbackFuncS2N(svar_name, s) // function/S CallbackFuncS2S(svar_name, s) // function CallbackFuncNN2N(svar_name, n1, n2) // function/S CallbackFuncNN2S(svar_name, n1, n2) // // default_proc を渡すときは、 // string svar_name = "root:Packages:ModuleName:SomeCallbackProc" // CallbackFunc2N(svar_name, default_proc=some_proc) // のようにする #pragma rtGlobals=1 // Use modern global access method. #pragma ModuleName= CallbackUtility function func2N() end function/S func2S() end function funcN2N(n) variable n end function/S funcN2S(n) variable n end function funcS2N(s) string s end function/S funcS2S(s) string s end function funcNN2N(n1, n2) variable n1, n2 end function/S funcNN2S(n1, n2) variable n1, n2 end // svar_name という名の svar で指定される関数があれば proc_name に関数名を返す static function CallbackFuncExists(svar_name, proc_name) string svar_name, &proc_name SVAR/Z ProcName = $svar_name if(SVAR_exists(ProcName) && exists(ProcName)==6) proc_name = ProcName return 1 endif return 0 end function CallbackFunc2N(svar_name, [default_proc]) string svar_name FUNCREF func2N default_proc string proc_name if( CallbackFuncExists(svar_name, proc_name) ) FUNCREF func2N ProcRef = $proc_name return ProcRef() elseif (paramisdefault(default_proc)) return default_proc() endif return Nan end function/S CallbackFunc2S(svar_name, [default_proc]) string svar_name FUNCREF func2S default_proc string proc_name if( CallbackFuncExists(svar_name, proc_name) ) FUNCREF func2S ProcRef = $proc_name return ProcRef() elseif (paramisdefault(default_proc)) return default_proc() endif return "" end function CallbackFuncN2N(svar_name, n, [default_proc]) string svar_name variable n FUNCREF funcN2N default_proc string proc_name if( CallbackFuncExists(svar_name, proc_name) ) FUNCREF funcN2N ProcRef = $proc_name return ProcRef(n) elseif (paramisdefault(default_proc)) return default_proc(n) endif return Nan end function/S CallbackFuncN2S(svar_name, n, [default_proc]) string svar_name variable n FUNCREF funcN2S default_proc string proc_name if( CallbackFuncExists(svar_name, proc_name) ) FUNCREF funcN2S ProcRef = $proc_name return ProcRef(n) elseif (paramisdefault(default_proc)) return default_proc(n) endif return "" end function CallbackFuncS2N(svar_name, s, [default_proc]) string svar_name, s FUNCREF funcS2N default_proc string proc_name if( CallbackFuncExists(svar_name, proc_name) ) FUNCREF funcS2N ProcRef = $proc_name return ProcRef(s) elseif (paramisdefault(default_proc)) return default_proc(s) endif return Nan end function/S CallbackFuncS2S(svar_name, s, [default_proc]) string svar_name, s FUNCREF funcS2S default_proc string proc_name if( CallbackFuncExists(svar_name, proc_name) ) FUNCREF funcS2S ProcRef = $proc_name return ProcRef(s) elseif (paramisdefault(default_proc)) return default_proc(s) endif return "" end function CallbackFuncNN2N(svar_name, n1, n2, [default_proc]) string svar_name variable n1, n2 FUNCREF funcNN2N default_proc string proc_name if( CallbackFuncExists(svar_name, proc_name) ) FUNCREF funcNN2N ProcRef = $proc_name return ProcRef(n1, n2) elseif (paramisdefault(default_proc)) return default_proc(n1, n2) endif return Nan end function/S CallbackFuncNN2S(svar_name, n1, n2, [default_proc]) string svar_name variable n1, n2 FUNCREF funcNN2S default_proc string proc_name if( CallbackFuncExists(svar_name, proc_name) ) FUNCREF funcNN2S ProcRef = $proc_name return ProcRef(n1, n2) elseif (paramisdefault(default_proc)) return default_proc(n1, n2) endif return "" end 使用例:上記の例を書き換え LANG:Igor // 画像をクリックしたときにモジュール内で呼び出される。 // 必要に応じてユーザー指定のコールバック関数を呼ぶ static function ImageClicked(x, y) variable x, y // モジュール名と同じ名前のデータフォルダー root:Packages:Module1 に // ImageClickProc という文字列変数が定義されていて // その文字列が示す名前のユーザー定義関数が存在するなら // その関数を呼び出す CallbackFuncNN2N("root:Packages:Module1:ImageClickProc", x, y) end * 質問・コメント [#hd3df86e] #article_kcaptcha **無題 [#q91e0af4] >[[ぺぺぺ]] (&timetag(2021-10-27T12:29:04+09:00, 2021-10-27 (水) 21:29:04);)~ ~ waveの存在を判定するコマンドが知りたいです!~ 例えばwave1が存在すれば「1」を返す等のプログラムが書きたいです!~ // - WaveExists という関数を使うと良いと思います。 -- [[武内(管理人)]] &new{2021-10-27 (水) 22:20:09}; #comment_kcaptcha **igorの悩み [#t4ca04d6] >[[m]] (&timetag(2019-08-08T10:06:03+09:00, 2019-08-08 (木) 19:06:03);)~ ~ 現在,データ処理をigorで行っています。~ マクロの作成で現在悩んでいます。~ 連続するファイルの取り込みはできたのでが,~ 複数のファイル例えば,2019_0806.csvや2019_0402.csvのようにランダム名前のデータを順に読み込む方法はありますか?~ // - IndexedFile という関数を使うと、特定のフォルダにあるファイルのリストを取得することができます。同関数を使ってファイルを一覧し、クリックだけでファイルを読み込めるようにするためにうちで使っているライブラリを公開しました。参考になれば良いですが → [[ソフトウェア/Igor/FileListBox]] -- [[武内(管理人)]] &new{2019-08-09 (金) 00:53:44}; #comment_kcaptcha
Counter: 33595 (from 2010/06/03),
today: 14,
yesterday: 0