(Tips) gpview の operation オプションを使いこなす
作成者: 納多 哲史
gpview の --operation オプションの活用例です。 マニュアル通りの「基本」と --operation オプションの実装を利用した 「応用」から成ります。
「応用」は本来想定されていない使い方であるうえ, 複雑になるので,初心者にはおすすめしません。 ジョークのつもりで書いている tips もあります。
準備
ここではサンプルとして GPhys, GGraphチュートリアル (その1) で使われている air.2012-01.nc を使うことにします。
応用へ向けての基礎知識
2014/02/18 時点の CVS 版の gpview のソースでは,--operation オプションの動作の本質は以下の部分です
## operation of a mathematical function if ($OPT_operation) eval "g = g.#{$OPT_operation}" end
ここで g は引数で指定したデータ (gphys オブジェクトとして扱われています), $OPT_operation は --operation の引数の中身です。 eval は Ruby の組み込みメソッドで, 引数に取った文字列を Ruby のコードとして実行します。
つまり,--operation の引数の中身が eval によって Ruby のコードとして実行されていることになります。
具体例を挙げますと,--operation log10 は
g = g.log10
を実行しているのと同じです。
この原理を理解すれば,--operation を通じて 任意の Ruby コードを挿入することができます。
逆に言えば,もし gpview を Web サーバなどで動作させたい場合は,--operation を通じた クロスサイトスクリプティングが出来ないよう,--operation が 動作しないように書き換えなければなりません。
応用
加減乗除
演算子の前に半角スペースを空けるのがポイントです。
$ gpview air.2012-01.nc@air --operation " *2"
複数の操作
Ruby で ";" を使って,1行に複数命令を記述できることを応用します。 また,gphys オブジェクトが入っている変数が "g" だと覚えている必要があります。
$ gpview air.2012-01.nc@air --operation " -200.0; g=g*2"
最初の次元をフーリエ変換して絶対値を取る
フーリエ変換の方法の詳細は リファレンス・マニュアル などを参照してください。
欠損値を含まないデータ (g.val が NArray オブジェクト) の場合
$ gpview air.2012-01.nc@air --operation "fft(nil,0).abs"
欠損値を含むデータ (g.val が NArrayMiss オブジェクト) の場合
$ gpview air.2012-01.nc@air --operation " * 1; GPhys::fft_ignore_missing; g=g.fft(nil,0).abs"
GPhys::fft_ignore_missing を挿入するために, 最初に " *1" としておく(+0.0 でもいいです)のがポイントです。
operation 後の cut, mean など
上記のフーリエ変換のサンプルで,波数 1-10 だけ取り出して可視化したい場合
$ gpview air.2012-01.nc@air --operation " * 1; GPhys::fft_ignore_missing; g=g.fft(nil,0).abs.cut(\'lon\'=>1..10)"
cut の引数内の引用符をエスケープするのがポイントです。
値の上書き
新たに変数を定義する場合は,gpview 内で定義されている変数と名前が重複しないようにしてください。
$ gpview air.2012-01.nc@air --operation " * 1; na=g.val; na[0..30,false]=300.0; g.val=na"
値のマスク
280 K 以下の場所を欠損値にする場合。
g.val が NArrayMiss の場合は以下でできます。
$ gpview air.2012-01.nc@air --operation " * 1; na=g.val; mask=na.le(280); na[mask]=DCL.glrget(\'rmiss\'); g.val=na"
DCL.glrget('rmiss') は欠損値扱いする値を返すメソッドです。
g.val が NArray の場合は NArrayMiss に変換してから行う必要があります(動作未確認)。
$ gpview air.2012-01.nc@air --operation " * 1; na=g.val; shape=na.shape; na=NArrayMiss.to_nam(na).reshape(*shape); mask=na.le(280); na[mask]=DCL.glrget(\'rmiss\'); g.val=na"
他のデータとの演算
他のデータと引き算する場合。 この例では air.2012-01.nc の 1000 hPa 面と 500 hPa 面の差を取ることにします。 ファイルが異なる場合でも同様です。
下記の場合,mean などの操作は1番目のデータにしか作用しないので, 平均などを取りたい場合は --operation の引数として陽に書く必要があることに注意してください。
2番目以降のデータを open 系メソッドの引数として指定する場合
$ gpview air.2012-01.nc@air,level=1000 --operation " - GPhys::IO.open_gturl(\'air.2012-01.nc@air,level=500\')"
open_gturl は open で書くようにしても動作しますが, cut メソッドを陽に書く必要がありますし, エスケープする文字が増えて煩雑になるのでおすすめしません。
2番目以降のデータを gpview の引数として指定する場合
$ gpview --operation " - GPhys::IO.open_gturl(ARGV[0]); ARGV = []" air.2012-01.nc@air,level=1000 air.2012-01.nc@air,level=500
ARGV の使い方について説明します。
gpview の中では ARGV 内の要素数だけループが回る仕様になっています。
最初のループでは1番めの引数の内容 g に代入して ARGV.shift しているので,--operation の引数が実行される段階では ARGV の内容は
["air.2012-01.nc@air,level=500"]
になっています(--operation の引数の最初のほうに "p ARGV" を挿入して出力すればわかります)。 そのため,2番めの引数を読むには open_gturl の中に ARGV[0] を書く必要があります(ARGV[1] ではない)。
しかし,このままだと2周目のループが周り, 再び ARGV[0] を参照してエラーで落ちてしまうので, それを回避するために最後に ARGV = [] で ARGV を上書きしています。
モニタリング
指定した時間おきにデータを読み直すようにすることで, たとえば実行中のモデルの出力の最新の状態をモニターすることができます。 モデルのデモなどに使えます。
GPhys の,軸の範囲外の値を指定すると,それに近い値を読み込む仕様を利用して, 非常に大きな time の値を指定すれば, 「最新の時刻」として読むことができます。
$ gpview --operation " * 1; ARGV.push(gturl); sleep 1" air.2012-01.nc@air --smooth --wsn 4 --noannotate
上の例では,データは更新されないので, 図に変化はないのですが,標準出力に
Reading air.2012-01.nc@air,level=1000
が増えていくことで,データを読み直していることが分かります。
デフォルトでは ARGV が空になってループを抜けてしまうので, "air.2012-01.nc@air" が入っている gturl を push することで 同じデータを読み続ける無限ループを作っています。
--smooth を付加しない場合は, 図をクリックしないとデータの読み直しが行われません。 あえてそうしたい場合は --operation 内の sleep は不要です。
現状のではアノテーションを入れると 再描画するたびに図が上にシフトしていってしまうので, --noannotate を付加しています。
図のシフトしていくのは gpview 内で
GGraph.margin_info($0, gturl) if $annotate
が毎回実行される影響のようですが, 逆方向にシフトするような命令を入れれば, アノテーションありの場合でもシフトすることなく動作すると思われます。
ファイルへの書き出し
$ gpview --operation " *1; outfile = NetCDF.create(\'gphys.nc\'); GPhys::IO.write(outfile, g); outfile.close" air.2012-01.nc@air,level=1000
ここでは air.2012-01.nc@air,level=1000 の結果が gphys.nc に出力されます。
ちなみに,当たり前ですが,
$ gpview --operation " *1; outfile = NetCDF.create(\'gphys.nc\'); GPhys::IO.write(outfile, g); outfile.close; exit" air.2012-01.nc@air,level=1000
と,exit を挿入すれば描画することなく終了します (operation が行われるのは DCL.gropn の後なので,一瞬だけ描画の窓が見えます)。
キーワード:
参照: