はじめに
MetPyという気象系Pythonライブラリは,'unit-aware calculations'(単位を陽に定義する計算)の精神に基づき作成されており,事前に変数の単位を定義しておけば単位変換を”よろしく”やってくれるので,つまらない計算ミスを減らすことができる(と思っている).
単位変換を簡単にできるのだが,データのクラスによって使い方が異なり困惑したので,まとめておく.
結論から書くと,pint.quantityは.to()
というメソッドを,xarary.DataArrayには.metpy.convert_units()
というメソッドを使う.
unitsの使い方とpint.quantity
metpy自体はpipやcondaやmacportsで簡単にインストールできる.(Install Guide - Metpy User Guide)
metpy.units.unitsをインポートする.fromを使うことが多い気がする.
xarrayも使う.
from metpy.units import units
import xarray as xr
単位は掛け算*
で使う
a = 0. * units.degC
a
0.0 <Unit('degree_Celsius')>
type(a)
pint.quantity.build_quantity_class.<locals>.Quantity
上記により,aに0.0セルシウス度を定義した.pint.quantityというクラスらしい.
unitsは属性的な使い方の他に,メソッド的な使い方もできる
b = 0. * units('degC')
a == b
Ture
dir(units)
で使える単位の一覧が出てくるが,公式のリストは存在しない(見つけてないだけ?)
ものによってはメソッド的な使い方しかできない(units('m/s^2')
とか).
xarray.DataArrayに丸ごと単位をつけることもできる.xarray.Datasetには単位を定義できない(過去の記事)
ds = xr.open_dataset('temperature_sample.nc')
da = ds['temperature']
da_u = da * units.kelvin
da_u
(上部省略)
[[194.72113 194.768 194.78363 ... 193.59613 193.59613 193.59613]
[194.68988 194.72113 194.768 ... 193.65863 193.62738 193.62738]
[194.65863 194.7055 194.81488 ... 193.7055 193.67426 193.67426]
...
[221.90863 221.92426 221.98676 ... 224.12738 224.143 224.143 ]
[221.98676 222.00238 222.03363 ... 224.11176 224.12738 224.143 ]
[222.06488 222.06488 222.09613 ... 224.09613 224.12738 224.143 ]]]], 'kelvin')>
Coordinates:
* time (time) datetime64[ns] 2017-12-31T15:00:00 ... 2018-02-28T09:00:00
* lev (lev) int64 1000 975 950 925 900 850 ... 400 300 250 200 150 100
* lon (lon) float64 120.0 120.1 120.2 120.4 ... 149.6 149.8 149.9 150.0
* lat (lat) float64 22.4 22.5 22.6 22.7 22.8 ... 47.2 47.3 47.4 47.5 47.6
しかし,これはpint.quantityではない
type(da_u)
xarray.core.dataarray.DataArray
単位変換
pint.quantityは.to()
というメソッドを,xarary.DataArrayには.metpy.convert_units()
というメソッドを使う.それぞれのクラスの違いに対応していると思われる.
aをセルシウス度からKelvinに,da_uは逆にKelvinからセルシウス度に変換してみる.
a.to('kelvin')
273.15 <Unit('kelvin')>
da_u.metpy.convert_units('degC')
(途中省略)
[[-7.8428864e+01 -7.8381989e+01 -7.8366364e+01 ... -7.9553864e+01
-7.9553864e+01 -7.9553864e+01]
[-7.8460114e+01 -7.8428864e+01 -7.8381989e+01 ... -7.9491364e+01
-7.9522614e+01 -7.9522614e+01]
[-7.8491364e+01 -7.8444489e+01 -7.8335114e+01 ... -7.9444489e+01
-7.9475739e+01 -7.9475739e+01]
...
[-5.1241364e+01 -5.1225739e+01 -5.1163239e+01 ... -4.9022614e+01
-4.9006989e+01 -4.9006989e+01]
[-5.1163239e+01 -5.1147614e+01 -5.1116364e+01 ... -4.9038239e+01
-4.9022614e+01 -4.9006989e+01]
[-5.1085114e+01 -5.1085114e+01 -5.1053864e+01 ... -4.9053864e+01
-4.9022614e+01 -4.9006989e+01]]]], 'degree_Celsius')>
Coordinates:
* time (time) datetime64[ns] 2017-12-31T15:00:00 ... 2018-02-28T09:00:00
* lev (lev) int64 1000 975 950 925 900 850 ... 400 300 250 200 150 100
* lon (lon) float64 120.0 120.1 120.2 120.4 ... 149.6 149.8 149.9 150.0
* lat (lat) float64 22.4 22.5 22.6 22.7 22.8 ... 47.2 47.3 47.4 47.5 47.6
ちゃんとaはKelvinに,da_uはセルシウス度に変換されていることが分かる.
(da_uは極東域の100 hPa(成層圏下部)の気温なので,-70~-50℃という値)
.ito()
でオブジェクト指向的?にaの単位を書き換える.(.to()
は多分コピーを返す)
a.ito('kelvin')
a
273.15 <Unit('kelvin')>