国立研究開発法人防災科学技術研究所 水・土砂防災研究部門
国立研究開発法人防災科学技術研究所 水・土砂防災研究部門
トップ 一覧 検索 ヘルプ RSS ログイン

Python-NetCDF出力(1次元から2次元変数の出力)

pythonを使ったNetCDFの書き込みについて
サンプルを示しながら、解説する

サンプルプログラムのダウンロードはこちら.
sample.py(279)
このプログラムは、とあるプログラムから書き込みの部分だけを切り取ったものです。
そのままでは動きません。TDATAやIDATAに具体的な値を入れると動きます。

------

import netCDF4
from numpy import dtype
def main():
  

下に記載しているのが、ハッシュへのハッシュを使い、変数名に対して、まとめてunitやlong_nameを定義しています。

   # define netcdf attribute #### 
   varinfo = {'IWV':{'units':'mm','long_name':'Integrated Water Vapor','missing_value':-999}}
   varinfo['LWP']= {'units':'mm','long_name':'Liquid Water Path','missing_value':-999}
   varinfo['BRT']= {'units':'K','long_name':'Black Body Temperature','missing_value':-999}
   varinfo['CMP.TPC']= {'units':'K','long_name':'Temperature Profile','missing_value':-999}
   varinfo['HPC']= {'units':'g/kg','long_name':'Humidity Profile','missing_value':-999}

詳しい理由はわからないが、missing_valueとしては整数しか与えられないようです。-999.9とすると
cannot be safely cast というエラーが出ます。私は、-999としました。

変数のアトリビュートを追加しております。

   # for surface observation ##  # 
   varinfo['SP']= {'units':'hPa','long_name':'surface pressure','missing_value':-999}
   varinfo['ST']= {'units':'K','long_name':'surface temperature','missing_value':-999}
   varinfo['SRH']= {'units':r'%','long_name':'surface relative humidity','missing_value':-999}
   varinfo['SU']= {'units':'m/s','long_name':'x-comp of surface wind','missing_value':-999}
   varinfo['SV']= {'units':'m/s','long_name':'y-comp of surface wind','missing_value':-999}
   varinfo['RR']= {'units':'mm/h','long_name':'rainrate','missing_value':-999}

NetCDFを書き込みとしてopenします。

   # open NetCDF as output ###########    
   nc = netCDF4.Dataset(outfile,"w",format="NETCDF4")

UNLIMITED の場合、Noneとします。そうじゃない場合、具体的な時間数を入れます

   nc.createDimension("TIME",None)   

ここでは、次元変数のTIMEに対して、変数を定義しています。CやFortranでは次元を定義するときに、
次元の型(doubleなのか、floatなのか)を定義しましたが、
Pythonでは、上のように、次元の定義は、次元数のみだけ。変数として定義するときに、型を定義します。

   time = nc.createVariable('TIME', dtype('float64').char, ('TIME'))
   time.long_name = "TIME"
   time.units = "seconds since 1970-01-01 00:00:00"

nc.createVariableでは、第1引数に変数名、第2引数に型、第3引数にその変数を構成する次元 を記載します。
上の例では、次元変数のTIMEを変数として定義しているので、次元は1つでTIMEとなります。
なお、dtypeについては、以下のページを参照してください。
https://note.nkmk.me/python-numpy-dtype-astype/
timeという変数に対して、.を付けて、アトリビュートを足していきます。ここでは、long_nameとunitsを足しています。
次元変数なので、missing_valueは無いはずなので、足していません。

以下は、一次元配列の場合の書き込みと2次元配列の書き込みを説明します。
他のサイトでは、1次元配列の例はあるのですが、2次元配列の場合の例が全く無く、困りました。

   if datatype == 1: ## 1 dimensional
       var  = nc.createVariable(vname, dtype('float32').char, ('TIME'))
       var.long_name= varinfo[vname]['long_name']
       var.units    = varinfo[vname]['units']
       var.missing_value   = varinfo[vname]['missing_value']

上記に記載しているのは、変数名がvnameに入っていて、vnameが例えば、IWVの場合には、
プログラムの最初に書いた、long_nameやunitsやmissing_valueが入るようにしました。
vnameだけを変更するだけで、同じプログラムを使いまわせるからです。この変数はdoubleではなく、
floatの精度で十分だったため、float32を選びました。IWV(可降水量)は時間の1次元変数なので、
TIMEが次元として張っています。

次に、TDATAもデータ数だけUNIXTIMEを入れておき、その時刻に対応するデータをIDATAとして保存しておきます
(ほかのプログラムで。ここではTDATA,IDATAはもう収録されていると仮定しています)

       time[:] = TDATA[:]
       var[:]  = IDATA[:]

これを、先ほどnc.createVariableで生成したオブジェクトに1次元配列として入れるだけ。最後にnc.closeすると書き込み完了。

次に2次元配列の場合の例を示します。他のサイト(日本語では他になかった。)では紹介していない事項です。

   elif datatype == 2: ## 2 dimensional

まずは、2次元なので、既に定義した時間ともう一つの次元を定義します。ここでは、高さ(LEV)を定義します。

       nc.createDimension("LEV",NX)
       LEV = nc.createVariable('LEV', dtype('float64').char, ('LEV'))
       LEV.long_name = "altitude"
       LEV.units = "m"

次元変数にもlong_nameやunitsを記載します。今度の次元は、LEVとなります。LEVについては、NX個の層があるとしています。
時間が12個で高さが50個の次元があるとすると、収録しようとするデータは2次元配列で、12×50の配列になっている必要があります。

次に収録しようとする変数を定義します。違うのは、最後のTIME,LEVだけ。2次元で、それぞれの次元がTIME,LEVであるということ。
左側にある次元が上位の次元。右側が早く回る下位の次元。高さ方向にデータが50個入ると、次の時間の50個が入っているという順番。

       var  = nc.createVariable(vname, dtype('float32').char, ('TIME','LEV'))
       var.long_name= varinfo[vname]['long_name']
       var.units    = varinfo[vname]['units']
       var.missing_value   = varinfo[vname]['missing_value']

createVariable以外は同じ。
最後に書き込み。これは、IDATAは2次元なのに対して、createVariableのオブジェクトのvarは1次元配列だということに注意!!
これが本家のマニュアルにも書いてなくて、試行錯誤でようやく成功しました。3次元配列でも、1次元としてNetCDFのオブジェクトには移し替えることが必要。

       time[:] = TDATA[:]
       LEV[:]  = X_ARRAY[:]
       var[:]   = IDATA[:][:]

書き込みは、最後にcloseするだけ。

   nc.close()

C言語やFortranとの違い


  • 編集モード、書き込みモード というものがない!!
  • したがって、enddefもしくはredefが無く、シンプル。
  • ncidに対応して、上記ではnetcdf4.datasetから出てくるオブジェクト(nc)が対応している。
  • varidに対応して、nc.createVariableから出てくるオブジェクト(var)が対応している。
  • オブジェクト指向のプログラムで、ncやvarの下に.を付けていろんなことができるようになっている。
  • 処理スピードだけが問題ではあるが、CやFortranに比べて、圧倒的にプログラミングが楽。これはいい。