[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[dennou-ruby:003075] Re: Fw: 電脳Rubyで2次元gribファイル読み込み



栗田様

2009/3/5 Naoyuki Kurita <nkurita@xxxxxxxxxxxxx>:
> 栗田です
>
> 堀之内さんの以下のご助言に従って改良をしてみました。
> 行ったことは、以下にまとめます。
>
> 1)grid_copyを使って、軸情報をコピーする
> 2)insert_axisを使って、時間軸を追加する
> 3)shapeを使って変数を取り出す
> 4)Dir.globを使って、存在するファイルだけ
>   上書きする
>
> できあがったプログラムは、取り込むファイルが1つ
> (例 tmp_2008010100.grib)だけであれば、正しく
> NetCDF変換できるのですが、ファイルが複数個
> (例 tmp_2008010100.grib, tmp_2008010200.grib)に
> なると、3次元データvalueを4次元のArrayに代入する
> 37行目で以下のエラーが出ます。
>
> line 37: array[false,jday] = value #jday<-time変数
>
> grib2nc.rb:37: undefined method `[]=' for nil:NilClass (NoMethodError)
>        from grib2nc.rb:19:in `each'
>        from grib2nc.rb:19
>
このエラーは nil に対して []= というメソッドを呼んでいるというものですので、
array が nil であるために起こっています。
この原因は非常にわかりにくく、ほとんどの人がはまるところですね^^;

変数のスコープを気にする必要があります。
ブロックで初めて出てきた変数は、ブロック内のローカル変数となります。
したがって、
array = NArray.sfloat(....
といって初期化しているつもりでも、
Dir.glob(..).each の次のファイルにいくと array の値は nil になってしまいます。

解決策は、
array = nil
Dir.glob(...).each{
というようにブロックに入る前にその変数を初期化しておくことです。
long, unit, name も同様です。

なぜ for の時は問題なかったかというと、
for の時はブロックは使っていなかったからです。

変数のスコープの問題は、
ruby のもっともわかりにくい問題の1つであり、
ruby 開発者の中でもいろいろと議論があるみたいです。

西澤

> 代入するデータや、jdayがおかしいのかと思い出力させてみても
> データはそのまま入っています。
>
> $ ruby grib2nc.rb
> time -> 0
> NArray.float(288,145,23):  <- value
> [ [ [ 241.052, 241.052, 241.052, 241.052, 241.052, 241.052, 241.052, ... ],
>    [ 242.271, 242.239, 242.239, 242.208, 242.177, 242.177, 242.146, ... ],
>    [ 244.958, 244.927, 244.896, 244.896, 244.833, 244.802, 244.771, ... ],
>    [ 247.583, 247.614, 247.646, 247.677, 247.677, 247.708, 247.708, ... ],
>    [ 251.677, 251.552, 251.489, 251.521, 251.552, 251.614, 251.646, ... ],
>    [ 261.271, 260.958, 260.739, 260.552, 260.427, 260.271, 260.114, ... ],
>    [ 270.521, 270.271, 270.083, 269.927, 269.771, 269.614, 269.458, ... ],
>    [ 273.489, 273.427, 273.364, 273.239, 273.021, 272.771, 272.396, ... ],
>    [ 273.896, 274.114, 274.208, 274.146, 273.896, 273.427, 272.802, ... ],
>    [ 273.708, 274.208, 274.614, 274.896, 274.958, 274.771, 274.302, ... ],
>  ...
> time->1
> NArray.float(288,145,23):  <-value
> [ [ [ 241.052, 241.052, 241.052, 241.052, 241.052, 241.052, 241.052, ... ],
>    [ 242.271, 242.239, 242.239, 242.208, 242.177, 242.177, 242.146, ... ],
>    [ 244.958, 244.927, 244.896, 244.896, 244.833, 244.802, 244.771, ... ],
>    [ 247.583, 247.614, 247.646, 247.677, 247.677, 247.708, 247.708, ... ],
>    [ 251.677, 251.552, 251.489, 251.521, 251.552, 251.614, 251.646, ... ],
>    [ 261.271, 260.958, 260.739, 260.552, 260.427, 260.271, 260.114, ... ],
>    [ 270.521, 270.271, 270.083, 269.927, 269.771, 269.614, 269.458, ... ],
>    [ 273.489, 273.427, 273.364, 273.239, 273.021, 272.771, 272.396, ... ],
>    [ 273.896, 274.114, 274.208, 274.146, 273.896, 273.427, 272.802, ... ],
>    [ 273.708, 274.208, 274.614, 274.896, 274.958, 274.771, 274.302, ... ],
>  ...
> grib2nc.rb:37: undefined method `[]=' for nil:NilClass (NoMethodError)
>        from grib2nc.rb:19:in `each'
>        from grib2nc.rb:19
>
> 時間軸をforではなく、Dir.globを使って、ファイル名から時間情報を取り
> 出したところからこのようなエラーが出ましたので、何かそれに関係して
> いると思うのですが、ちょっと検討がつきません。ご助言をお願いでき
> ませんでしょうか?
>
> また、プログラム内で、tmp_yyyymmddhh.gribというファイルから、
> 時間情報を取り出しているのですが、
>
> fname=tmp_2008010100.grib
> p fname.slice(-15..-6)
>  => "2008010100"
>
> p fname.slice(-15..-6).split(/(....)(..)(..)(..)/)
>  => ["", "2008", "01", "01", "00"]
>
> と最初に""が入ってしまいます。どうもPerlと勝手が違うようで、
> 正規表現が間違っているのが大きな原因だと思いますが、、、、
>
> お忙しいところお手数おかけして申し訳ございませんが
> お助け願います。余計な事に手を出して失敗するなんて
> 情けない。。。。
>
>
>
>
>
> On 2009/03/04, at 9:26, Takeshi Horinouchi wrote:
>
>> 栗田さま
>>
>> 動いてよかったです.プログラムはだいぶ短くなりましたね.
>>
>> ここから先は趣味の問題みたいなもんですが,
>> さらに短く,かつ見通しもよりよく(これが大事)できると思います.
>> なにせ,形式変換プログラムが簡単にかけるのは GPhys の
>> 売りのひとつなので,ついついベストを追求したくなります.
>>
>> データが手元になく確認できないので方針だけになりますが.
>> GRIBファイルをあらわす gphys の格子コピーを作成して
>> insert_axis 等を使います.すると,lon = gphys.axis(0)
>> 等は不要になります.また,GPhysやNArray等の shape
>> メソッドを活用するともっと簡単になるところもあります.
>> あと,年間のループをまわした上でファイルがあるときだけ
>> 対応する代わりに Dir.glob で存在するふぁいるだけ
>> 拾ってループをまわすこともできます.
>>
>> # なにせ趣味の領域なので,栗田さんにやってみてくださいという
>>  つもりはありませんが.
>>
>> 堀之内
>>
>>> ご助言に従って、複数の変数が日付毎に格納されているJRA25
>>> データを、モデル出力と同じように、1変数ずつの4次元
>>> NetCDF
>>> データに変換するプログラムをリバイスしてみました
>>> (grib2nc.rb)。
>>>
>>> 使用しているデータは、JRA25データのanal_p/anl_p25
>>> と同じ
>>> 要素が入った、水平1.25度格子データです。
>>>
>>> http://jra.kishou.go.jp/JRA-25/elements.html
>>>
>>> これは、6時間値毎に1ファイルになっておりますので、各
>>> ファイル
>>> から指定する要素を抜き出し、1ファイル1要素のgrib
>>> ファイルを
>>> 1年分つくり、それをRubyプログラムで4次元データに
>>> するという流れ
>>> になっています(添付したgrib2nc.sh参照)。
>>>
>>> Rubyプログラムでは、例えば
>>> TMP2008010100.grib
>>> TMP2008010200.grib
>>> :
>>> :
>>>
>>> といったように複数個に分けられているGrib形式の気温デー
>>> タを読み込み
>>> これを4次元Arrayにした後に、NetCDFで書き出
>>> すということをしてい
>>> ます。また、たまに欠損時間がありますので、それを考慮して、最初に、
>>> 365日分の全データに欠損値を入れておき、
>>>
>>> Line 38:  array = NArray.sfloat(x,y,z,nd).fill!(rmiss)
>>>
>>> そこにデータがある日にちだけデータを上書きして行くことを行ってい
>>> ます。ちょっとTMP2008010100.grib, TMP2008010200.gribと
>>> いうファ
>>> イルを作って
>>>
>>> ruby grib2nc.rb
>>>
>>> としてみたところ、出力データもちゃんと出力できました。
>>>
>>> <GPhys grid=<4D grid <axis pos=<'lon' in 'TMP'  288>>
>>>        <axis pos=<'lat' in 'TMP'  145>>
>>>        <axis pos=<'level' in 'TMP'  23>>
>>>        <axis pos=<'time' sfloat[1] val=[0.0,]>>>
>>>  data=<'TMP' sfloat[288, 145, 23, 1]
>>> val
>>> =
>>> [241.051971435547,241.051971435547,241.051971435547,241.051971435547
>>> ,...]>>
>>>
>>> HGTを登録できれば、Rubyを使ってJRAデータもGrib->NetCDF
>>> が便利に
>>> できそうです。以前は、Gradsでも、lats4dコマンドを
>>> 使って、
>>> Grib->NetCDF変換が便利にできたのですが、最新のGrads2.0
>>> ではサポート
>>> されなくなってしまいまいましたので、電脳rubyで簡単にで
>>> きると非常に嬉
>>> しいです。
>>>
>>> また、NetCDFデータの可視化ですが、現在いるゴダード宇宙
>>> 科学研究所
>>> では、panoplyというJavaソフトがあり、コンパイルせ
>>> ずに、ただファイル
>>> を置いた場所にパスを通すだけで全球データのクイックルックがで
>>> きます。
>>>
>>> http://www.giss.nasa.gov/tools/panoply/
>>>
>>> 上記のサイトからfreeでダウンロードできますので、興味が
>>> あればのぞいてみ
>>> てください。また、制作者がすぐ近くにおりますので、質問があれ
>>> ば、こちら
>>> で情報を仕入れることもできます。
>>>
>>
>>
>
>
>



-- 
Seiya Nishizawa
Department of Earth and Planetary Atmospheric Sciences, Kobe University