MetPyの単位変換

# はじめに

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](https://unidata.github.io/MetPy/latest/userguide/installguide.html))

metpy.units.unitsをインポートする．fromを使うことが多い気がする．
xarrayも使う．

```python3
from metpy.units import units 
import xarray as xr
```
単位は掛け算`*`で使う
```python3
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は属性的な使い方の他に，メソッド的な使い方もできる
```python3
b = 0. * units('degC')

a == b
Ture
```
`dir(units)`で使える単位の一覧が出てくるが，公式のリストは存在しない（見つけてないだけ？）
ものによってはメソッド的な使い方しかできない（`units('m/s^2')`とか）．

xarray.DataArrayに丸ごと単位をつけることもできる．xarray.Datasetには単位を定義できない（[過去の記事](https://qiita.com/kasugab3621/items/cb484ecedf451425efc9)）
```python3
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ではない


```python3
type(da_u)
xarray.core.dataarray.DataArray
```


# 単位変換

pint.quantityは`.to()`というメソッドを，xarary.DataArrayには`.metpy.convert_units()`というメソッドを使う．それぞれのクラスの違いに対応していると思われる．

aをセルシウス度からKelvinに，da_uは逆にKelvinからセルシウス度に変換してみる．


```python3
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()`は多分コピーを返す）

```python3
a.ito('kelvin')
a
273.15 <Unit('kelvin')>
```

# 参考文献

[Converting units - Working With Units - Metpy Tutorials](https://unidata.github.io/MetPy/latest/tutorials/unit_tutorial.html?highlight=ito#converting-units)

[Units - xarray with MetPy Tutorial - Metpy Tutorials](https://unidata.github.io/MetPy/latest/tutorials/xarray_tutorial.html#units)