(Tips) gpview の operation オプションを使いこなす

作成者: 納多 哲史

gpview の --operation オプションの活用例です。 マニュアル通りの「基本」と --operation オプションの実装を利用した 「応用」から成ります。

「応用」は本来想定されていない使い方であるうえ, 複雑になるので,初心者にはおすすめしません。 ジョークのつもりで書いている tips もあります。

準備

ここではサンプルとして GPhys, GGraphチュートリアル (その1) で使われている air.2012-01.nc を使うことにします。

基本

常用対数

$ gpview air.2012-01.nc@air --operation log10

平方根

$ gpview air.2012-01.nc@air --operation sqrt

sin を作用

$ gpview air.2012-01.nc@air --operation sin

応用へ向けての基礎知識

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 の後なので,一瞬だけ描画の窓が見えます)。

更新日時:2014/03/10 03:38:43
キーワード:
参照: