ソフトウェア/Igor/グラフ関連

(4061d) 更新


ソフトウェア/Igor

グラフに表示されたウェーブの一覧を取得する

TraceNameList という関数で読み取った値をリスト関数で処理します。

LANG:Igor(linenumber)
function TestTraceNameList(graph_name)
   string graph_name
   string traces = TraceNameList(graph_name, ";", 1)
   variable i
   for(i = 0; i < ItemsInList(traces); i+=1)
       print StringFromList(i, traces)
   endfor
end

コマンドラインから

LANG:Igor
make wave1, wave2, wave3, wave4
display wave1, wave2, wave3, wave4
TestTraceNameList("Graph0")
// wave1
// wave2
// wave3
// wave4

Igor には他にも XxxxNameList という名前の関数がたくさんあって、 同じパターンを使い回せます。

例えば、画像の一覧なら、TraceNameList ではなく ImageNameList を呼べばいいわけです。

LANG:Igor(linenumber)
string graph_name = "Graph1"
string images = ImageNameList(graph_name, ";", 1)
variable i
for(i = 0; i < ItemsInList(images); i+=1)
    print StringFromList(i, images)
endfor

当然、名前が手に入れば何でもできますね。

例えばあるグラフから名前が "wave1?" の形のトレース、
例えば "wave10", "wave11", ... をすべて消去するには、

LANG:Igor(linenumber)
function test()
    string graph_name = "Graph0"
    string traces = TraceNameList(graph_name, ";", 1)
    variable i
    for(i = ItemsInList(traces) - 1; i>=0; i-=1)
        string trace_name = StringFromList(i, traces)
        if(GrepString(trace_name, "^wave1.(#\d*)?$"))
            RemoveFromGraph $trace_name
        endif
    endfor
end

// コマンドラインから

// いくつもウェーブを表示した中から
make wave0, wave1, wave10, wave11
display wave0, wave1, wave10, wave11, wave1, wave11
// wave10, wave11 だけを取り除く
test()

などとします。

for を逆順に回しているのは、トレースを Remove すると 残されたトレースの名前が変化することがあるためです。

例えば、wave10 が2回 AppendToGraph されていると、wave10 と wave10#1 という2つのトレースができます。ここで、先に wave10 を消してしまうと wave10#1 の名前が wave10 になってしまい、後から wave10#1 を消そうと したときに見付からずエラーになります。

逆順に回すとこの問題を回避できます。

"wave1?" という形のウェーブを探すのに GrepString を使いました。 正規表現で検索できるので、strsearch に比べると凝ったことが簡単にできますね。

この部分を strsearch(trace_name, "wave1", 0) >= 0 としてしまうと、 wave10 などだけでなく、wave1 まで引っかかってしまいます。

グラフ上の選択領域を得る

グラフ上をマウスで斜めにドラッグすると矩形領域を選べますが、 この範囲を Igor では Marquee と呼んでいます。

使用例

LANG:Igor(linenumber)
// 与えられた座標がグラフの選択領域に含まれているかどうか返す
function IsPointInSelectedRegion(x, y)
variable x, y

    string graph_name = "Graph0"

    struct Rectangle selected_region
    GetMarqueeRegion(graph_name, selected_region)

    return IsPointInRect(x, y, selected_region)
end

コマンドラインから

LANG:Igor
make/N=100 wave0 = x
display wave0

// 適当な領域をマウスで選択してから
print IsPointInSelectedRegion(20,20)

コード

LANG:Igor(linenumber)
// 矩形領域を格納するための構造体
Structure Rectangle
    double left
    double right
    double top
    double bottom
EndStructure

// 指定された名前のグラフから選択範囲を得る
// 選択されていなければ全範囲 (left, bottom 軸で) を返す
function GetMarqueeRegion(graph_name, region)
string graph_name
Struct Rectangle &region

    GetMarquee/W=$graph_name left, bottom
    if(!V_Flag)
        GetAxis/q/W=$graph_name bottom
        V_Left   = V_min
        V_Right  = V_max
        GetAxis/q/W=$graph_name left
        V_Top    = V_min
        V_Bottom = V_max
    endif

    region.left = V_Left
    region.right = V_Right
    region.top = V_Top
    region.bottom = V_Bottom
end

// x, y で指定された点が rect 領域に入っているかどうかを返す 
function IsPointInRect(x, y, rect)
variable x, y
Struct Rectangle &rect
    variable result
    result = (x-rect.left)*(x-rect.right) <= 0
    result = (y-rect.top)*(y-rect.bottom) <= 0 && result
    return result
end

IsPointInRect は、left < right, top < bottom を期待せず、 right < left や bottom < top でも、 内部か外部かを正しく判別できるようにしてあります。

グラフ上の選択範囲を設定する

SetMarquee を使えばいいのですが、単位をポイントに直さなければならないので、 PixelFromAxisVal と ScreenResolution を呼んで変換します。

また、普通にやると上下逆さまに指定することになって正しく選択されないため、 数値の大小で指定を並べ替えています。

コード

LANG:Igor
// 下軸、左軸の値を指定してグラフの選択範囲を設定する
function SetMarqueeByAxisVal(graph_name, x1, y1, x2, y2)
string graph_name
variable x1, y1, x2, y2
    x1 = PixelFromAxisVal(graph_name, "bottom", x1) / ScreenResolution * 72
    x2 = PixelFromAxisVal(graph_name, "bottom", x2) / ScreenResolution * 72
    y1 = PixelFromAxisVal(graph_name, "left",   y1) / ScreenResolution * 72
    y2 = PixelFromAxisVal(graph_name, "left",   y2) / ScreenResolution * 72
    variable t
    if (x2<x1)
    	t=x2; x2=x1; x1=t;
    endif
    if (y2<y1)
    	t=y2; y2=y1; y1=t;
    endif
    SetMarquee/w=$graph_name x1, y1, x2, y2
end

グラフ上への DrawAction について

DrawLine, DrawArc, DrawRect など、一連の DrawXXX 関数によってグラフ上に様々な図形を描画できます。

しかし、注釈を付ける際の TextBox 等と違って DrawXXX で書いた図形には名前が付かないため、 一旦描いた DrawXXX 図形をスクリプトから変更する方法が分かりづらいです。

例えば、一旦書いた DrawXXX 図形を後から消去したいような場合には、

  1. SetDrawEnv gstart, gname = the_group_name で一連の描画を入れる名前付きグループを作成する
  2. DrawXXX を使って描画する
  3. SetDrawEnv gstop で名前付きグループを閉じる
  4. あとから、DrawAction getgroup=the_group_name, delete のようにグループを消去する

という手順を取ります。

コード例:(コマンドラインに貼り付けられます)

LANG:Igor
// 適当なグラフを作成
Make wave0 = {0, 1}
Display wave0
// グループを指定して四角形を描画
SetDrawEnv gstart, gname=my_group_name
SetDrawEnv xcoord=bottom, ycoord=left
SetDrawEnv linefgc= (0,65280,0), fillpat=0, dash=1
DrawRect 0.1, 0.1, 0.3, 0.4
SetDrawEnv gstop
// グループを指定せずに描画 (色指定などは持ち越されない)
DrawRect 0.4, 0.5, 0.6, 0.7
DoAlert 0, "1つ目に書いた図形を消去します"
DrawAction getgroup=my_group_name, delete
DoAlert 0, "残っている2つ目に書いた図形も含めて全部消去します"
DrawAction delete

棒グラフや都市景観グラフの罠

Igor で棒グラフや都市景観グラフを書くと、

“対応する x 座標値の「右側」にグラフが書かれます”

これ、注意しないと結果を見間違います。
次のコードをコマンドラインに貼り付けてみて下さい。

LANG:Igor
make wave0 = {0,1,2,1,0}
display wave0
ModifyGraph mode=3,marker=19
DoAlert 0, "x=2 をピークとするグラフになっていることを確認して下さい"
DoAlert 0, "このデータを棒グラフで表示します"
ModifyGraph mode=5
DoAlert 0, "ピークが x=2.5 にずれてしまいました"
DoAlert 0, "このデータを都市景観グラフで表示します"
ModifyGraph mode=6
DoAlert 0, "やはり同じです"
DoAlert 0, "オフセットを付けて無理矢理合わせます"
ModifyGraph offset={ - DeltaX(wave0) / 2, 0 }

以下説明です。

LANG:Igor
*make wave0 = {0,1,2,1,0}
*display wave0
*ModifyGraph mode=3,marker=19

mode3.png

ここまでは問題ないですが、上のグラフのデータを棒グラフにすると、

LANG:Igor
*ModifyGraph mode=5

mode5.png

このように、見事にずれてしまいます。

上のグラフでは x=2 が頂点だったのに、 このグラフでは x=2.5 が頂点に見えてしまいます。

都市景観でも同様です。

LANG:Igor
*ModifyGraph mode=6

mode6.png

棒を本来の値の真上に持ってくるには、 グラフに柱の半分だけオフセットを履かせる必要があります。

LANG:Igor
*ModifyGraph offset={ - DeltaX(wave0) / 2, 0 }

mode6offset.png

ようやく合いました。

Histogram との組み合わせ

上記と似たような話で Histogram も注意です。

新しく Igor を立ち上げて、コマンドウィンドに以下のコマンドを貼り付けてみて下さい

LANG:Igor
Make/N=1000 wave0=gnoise(1)
Make/N=10 wave0_Hist
Histogram/B={-5,1,10} wave0, wave0_Hist
Display wave0_Hist
ModifyGraph grid=1
CurveFit/NTHR=0/W=0 gauss wave0_Hist /D 
ModifyGraph rgb(fit_wave0_Hist)=(0,0,0)
DoAlert 0, "次へ進みます"
ModifyGraph mode(wave0_Hist)=5
ModifyGraph hbFill(wave0_Hist)=5
DoAlert 0, "次へ進みます"
Histogram/B={-5,1,10}/C wave0, wave0_Hist
CurveFit/NTHR=0/W=0 gauss wave0_Hist /D 
DoAlert 0, "次へ進みます"
ModifyGraph offset(wave0_Hist)={ - DeltaX(wave0_Hist)/2, 0 }

以下、説明です。

始めに、期待値 0、標準偏差 1 のガウス分布のランダム値 1000 個を生成し、 そのヒストグラムを作成して、結果をガウシアンでフィッティングしています。

LANG:Igor
*Make/N=1000 wave0=gnoise(1)
*Make/N=10 wave0_Hist
*Histogram/B={-5,1,10} wave0, wave0_Hist
*Display wave0_Hist
*ModifyGraph grid=1
*CurveFit/NTHR=0/W=0 gauss wave0_Hist /D 
 適切に収束しました
 fit_wave0_Hist= W_coef[0]+W_coef[1]*exp(-((x-W_coef[2])/W_coef[3])^2)
 W_coef={2.6318,390.96,-0.50822,1.4051}
 V_chisq= 76.6007;V_npnts= 10;V_numNaNs= 0;V_numINFs= 0;
 V_startRow= 0;V_endRow= 9;
 W_sigma={1.65,3.5,0.00967,0.0162}
 係数値 ±標準偏差
 	y0   	=2.6318 ± 1.65
 	A    	=390.96 ± 3.5
 	x0   	=-0.50822 ± 0.00967
 	width	=1.4051 ± 0.0162
*ModifyGraph rgb(fit_wave0_Hist)=(0,0,0)

HistogramTrap1.png

ただこの結果、ご覧の通りなぜか期待値がゼロになりません。

この理由は、ヒストグラムを棒グラフ表示してみると分かります。

LANG:Igor
*ModifyGraph mode(wave0_Hist)=5
*ModifyGraph hbFill(wave0_Hist)=5

HistogramTrap2.png

上で述べた「棒グラフの罠」のおかげで、棒グラフの方はちゃんと中央がゼロになりました。
ヒストグラムの結果はこのように棒グラフ表示することを前提としたもので、 恐らく棒グラフ表示の方もこのようにヒストグラムと組み合わせて使うことを前提としているのだと思います。

が、、、これは完全に初見殺しの罠になってしまってます。

ヒストグラムの結果を正しくフィッティングするには、 ヒストグラム作成時に /C を付ける必要があります。

LANG:Igor
*Histogram/B={-5,1,10}/C wave0, wave0_Hist
*CurveFit/NTHR=0/W=0 gauss wave0_Hist /D 
 適切に収束しました
 fit_wave0_Hist= W_coef[0]+W_coef[1]*exp(-((x-W_coef[2])/W_coef[3])^2)
 W_coef={2.6318,390.96,-0.0082209,1.4051}
 V_chisq= 76.6007;V_npnts= 10;V_numNaNs= 0;V_numINFs= 0;
 V_startRow= 0;V_endRow= 9;
 W_sigma={1.65,3.5,0.00967,0.0162}
 係数値 ±標準偏差
 	y0   	=2.6318 ア 1.65
 	A    	=390.96 ア 3.5
 	x0   	=-0.0082209 ア 0.00967
 	width	=1.4051 ア 0.0162

HistogramTrap3.png

こんどはちゃんと期待値がほぼゼロになってくれました。
が、ヒストグラムの棒グラフ表示がずれてしまっていますので、 結局オフセットを使って姑息に合わせなければなりません。

LANG:Igor
*ModifyGraph offset(wave0_Hist)={ - DeltaX(wave0_Hist)/2, 0 }

HistogramTrap4.png

ようやく期待する結果が得られました。


添付ファイル: fileHistogramTrap4.png 1677件 [詳細] fileHistogramTrap3.png 1752件 [詳細] fileHistogramTrap2.png 1621件 [詳細] fileHistogramTrap1.png 1544件 [詳細] filemode6offset.png 1694件 [詳細] filemode6.png 1717件 [詳細] filemode5.png 1614件 [詳細] filemode3.png 1628件 [詳細]

Counter: 17471 (from 2010/06/03), today: 2, yesterday: 2