自然現象を扱う理科系の計算では単位は必須。ただ、単位を考えながらPythonコードを考えるのがかなり面倒。
当然ながら単位換算を間違えると最終的な結果がおかしくなるし、どこで計算ミスしたのかを見つけるために処理を追っていく必要がある。
今回はastropy
の単位付与ライブラリastropy.units
を使って、数値に単位を付与して計算・単位換算する方法を紹介する。
これまで単位換算で計算ミスしていた人・単位換算をもっと楽にしたい人は読み進めてほしい。
Python環境は以下。
- Python 3.10.8
- astropy 5.2.2
参考になるサイト
astropy公式
本記事のコード全文
下準備のimport
from astropy import units as u
まずは下準備のimport
から。今回はastropy.units
しか使用しないのでastropy.units
をimport
。
astropy公式がas u
で短縮使用しているが、他のサイトでは短縮していない場合もあるので注意。
数値計算の単位換算が面倒
数値計算で単位換算を考えるのは面倒だし何よりも手間だ。1つひとつの計算でちゃんと合っているのか確認しつつ最終的な結果を求める必要がある。
M(メガ、10^6)やG(ギガ、10^9)なら普段から目にする機会が多いから計算しやすいが、h(ヘクト、$10^2$)といった普段使わない接頭辞だと計算間違いが起きやすい。
SI接頭語(補助単位)はまだ簡単
from astropy import units as u
# 1kmをm単位に換算
one_kilo_meter = 1 # 1km
# m単位になるように計算
one_meter = one_kilo_meter * 10**3
print(one_meter)
# 1000
k(キロ)やM(メガ)といったSI接頭語はまだシンプルで計算しやすくミスも少ない。
例えば上の例だと1kmを1mに換算しているが、この時はk(キロ)を1,000に換算したらいいので、1,000倍してあげればいい。
また、以下の例では距離distance
と時間second
から速さvelocity
を計算し、その結果をkm/s単位に換算している。
from astropy import units as u
# 距離と時間から速さを計算
distance = 10 # 10m
second = 2 # 2秒
# m/s単位で速さを計算
velocity = distance / second
print(velocity)
# 5.0
# 速さをkm単位に換算
kilo_velocity = velocity / (10**3)
print(kilo_velocity)
# 0.005
これらのレベルだと10の何乗したいいのか、という問題だけなので面倒ではあるがそこまで面倒でもない。
単位換算は複雑で面倒
from astropy import units as u
# 1mをインチに換算
one_meter = 1 # 1m
# 1インチは25.4mm
one_inch = one_meter / (25.4 * 10**(-3))
print(one_inch)
# 39.37007874015748
しかし、メートルとインチのように、同じ「距離」という単位だが単位自体が変わってしまう場合は面倒。
それぞれの単位の定義を確認して計算式に盛り込む必要がある。例えば1インチは25.4mmなので、1mをインチに換算する際には25.4 * 0.001mで割らなければいけない。
さすがにこれは面倒だし計算ミスも多発する。
astropy.unitsで単位付きの数値を作成
from astropy import units as u
print(u.m)
# m
print(type(u.m))
# <class 'astropy.units.core.IrreducibleUnit'>
print(u.km)
# km
print(type(u.km))
# <class 'astropy.units.core.PrefixUnit'>
そこで使えるのがastropy.units
だ。astropy
は天文学系のライブラリだが、その中のunits
を使うことで数値に単位を付与することができる。
単位は一般的なものであれば上のようにu.m
やu.km
といったそのままの記法で記述できる。
単位名を掛け算するだけで単位をつけられる
from astropy import units as u
# astropy.unitsで数値に単位を付与する
# astropy.unitsで1という数値にkmの単位をつける
one_kilo_meter = 1 * u.km
print(one_kilo_meter)
# 1.0 km
print(type(one_kilo_meter))
# <class 'astropy.units.quantity.Quantity'>
使い方は簡単で、数値の後ろにastropy.units
の単位を掛け算してあげるだけ。上の例だと数値1
にkm
を表すu.km
を掛け算した。これで1kmを作成できる。
あとは後述する計算や単位換算をするだけだ。単位同士の換算は自動で行ってくれるので、人為的なミスが格段に減る。
astropy.unitsに帝国単位は含まれない
inch = 1 * u.inch
# AttributeError: module 'astropy.units' has no attribute 'inch'.
なお、astropy.units
は元々、天文学系のライブラリastropy
をベースにしているので、天文学であまり使われない単位はサポートしていない時がある。
例えば帝国単位である長さの単位「inch(インチ)」はastropy.units
ではサポートしていない。
このような単位を使用する場合は次のu.imperial
を使う。
帝国単位にはu.imperialを使う
from astropy import units as u
# インチを計算
one_inch = 1 * u.imperial.inch
print(one_inch)
# inch
print(type(one_inch))
# <class 'astropy.units.quantity.Quantity'>
帝国単位であるインチをastropy
で使用するにはu.imperial.inch
を使う。あとは通常のastropy.units
と同じように数値をかけるだけだ。
ちょっとした使い分けは必要だが、一度定義してしまえばのちの計算では自動で単位換算してくれるから使わない手はない。
astropy.unitsの単位付きの数値で計算
astropy.units
で単位を付与した数値を定義したら、あとはこれらを使って普段通りに計算するだけだ。
計算は単に掛け算などをするだけ
from astropy import units as u
# 距離と時間から速さを計算
distance = 10 * u.m # 10m
second = 2 * u.s # 2秒
# m/s単位で速さを計算
velocity = distance / second
print(velocity)
# 5.0 m / s
例えば距離と時間から速さを求める場合は、距離と時間それぞれに単位を付与して速さを計算するだけだ。
自動で速さの単位m / s
が付与される。間違えてs / m
になったらこの時点で気づくことができる。
数値と単位を分けて取得
from astropy import units as u
# 数値と単位を分けて取得
distance = 10 * u.m # 10m
second = 2 * u.s # 2秒
# m/s単位で速さを計算
velocity = distance / second
print(velocity)
# 5.0 m / s
print(type(velocity))
# <class 'astropy.units.quantity.Quantity'>
# 値だけ取り出し
value = velocity.value
print(value)
# 5.0
print(type(value))
# <class 'numpy.float64'>
# 単位だけ取り出し
unit = velocity.unit
print(unit)
# m / s
print(type(unit))
# <class 'astropy.units.core.CompositeUnit'>
ただし、astropy.units
の単位を付与した値の型はastropy.units.quantity.Quantity
なので、このままだと他のライブラリでの計算などでエラーになるかもしれない。
得られた値に.value
を使うことでnumpy.float64
形式で、.unit
でastropy
の単位の型で値を分離することができる。
.value
で値だけ取り出してから他のライブラリで使用して欲しい。
単位付きの数値を文字列に変換することも可能
from astropy import units as u
# 単位付きの数値を文字列に変換
distance = 10 * u.m # 10m
second = 2 * u.s # 2秒
velocity = distance / second
# 文字列に変換
string = velocity.to_string()
print(string)
# 5.0 m / s
print(type(string))
# <class 'str'>
また、計算結果を文字列として出力することも可能だ。.to_string()
を使うことで文字列の状態で取得できる。
単位付きの文字列措定ファイル保存する際などは使用してほしい。
astropy.unitsで別の単位に単位換算する
数値に単位を付与して計算することができたが、ここでは単位付きの数値を他の単位に変換する方法を紹介する。
1つの単位を別の単位に単位換算する
from astropy import units as u
# 1kmをm単位に換算
one_kilo_meter = 1 * u.km # 1km
# .toでm単位になるように計算
one_meter = one_kilo_meter.to(u.m)
print(one_meter)
# 1000.0 m
# 文字列でも単位換算の指定が可能
print(one_kilo_meter.to('m'))
# 1000.0 m
別の単位に変換する際には.to()
を使う。引数に換算後の単位を入れることで単位換算することが可能だ。
上の例では1kmを1mに単位換算するために.to(u.m)
をした。なお、引数は文字列でも指定が可能。mの場合はそのまま’m’
で指定できる。
複数の単位の組み合わせを単位換算する
from astropy import units as u
# 複数の単位の組み合わせで単位換算
distance = 10 * u.m # 10m
second = 2 * u.s # 2秒
velocity = distance / second
print(velocity)
# 5.0 m / s
# m/2の単位をkm/2に変換
kilo_velocity = velocity.to(u.km / u.s)
print(kilo_velocity)
# 0.005 km / s
# 文字列での指定も可能
kilo_velocity = velocity.to('km / s')
print(kilo_velocity)
# 0.005 km / s
# 同時に複数の単位の換算も可能
kilo_micro_velocity = velocity.to('km / us')
print(kilo_micro_velocity)
# 5e-09 km / us
複数の単位が混ざった値でも同様に単位換算できる。上の例だと速さの単位m/s
をkm/s
とkm/μs
にそれぞれ換算した。
マイクロ(μ)はastropy.units
ではu
と書くので注意。
別の単位系への換算も可能
from astropy import units as u
# 別の単位系への換算も可能
# mをインチに換算
one_meter = 1 * u.m
print(one_meter)
# 1.0 m
one_inch = one_meter.to(u.imperial.inch)
print(one_inch)
# 39.370078740157474 inch
また、メートルをインチに変換することも可能。こちらも同様に.to()
の引数にインチを表すu.imperial.inchを
指定するだけだ。
なお、inch
は文字列で指定することができない。あくまでもimperial
経由での使用となる。
# 帝国単位は文字列指定できない
one_meter.to('inch')
# ValueError: 'inch' did not parse as unit: At col 0, inch is not a valid unit. If this is meant to be a custom unit, define it with 'u.def_unit'. To have it recognized inside a file reader or other code, enable it with 'u.add_enabled_units'. For details, see <https://docs.astropy.org/en/latest/units/combining_and_defining.html>
実現しない単位換算はエラー
one_meter = 1 * u.m
# 1mをgに変換できないのでエラー
one_meter.to('g')
# astropy.units.core.UnitConversionError: 'm' (length) and 'g' (mass) are not convertible
当然だが実現しない単位への換算はエラーとなる。上の例では長さの単位であるmを重さの単位であるgに換算した場合だ。長さから重さへは換算できないのでエラーとなる。
単位を定義して使う
from astropy import units as u
# titterという単位を定義
titter = u.def_unit('titter')
# titterの5倍をchuckleとして定義
chuckle = u.def_unit('chuckle', 5 * titter)
# chuckleの4倍をlaughとして定義
laugh = u.def_unit('laugh', 4 * chuckle)
# laughの3倍をguffawとして定義
guffaw = u.def_unit('guffaw', 3 * laugh)
# guffawの4倍をroflとして定義
rofl = u.def_unit('rofl', 4 * guffaw)
# death_by_laughingをroflの10として定義
death_by_laughing = u.def_unit('death_by_laughing', 10 * rofl)
# death_by_laughingは最終的にtitterの5*4*3*4*10=2,400倍
print((1. * death_by_laughing).to(titter))
# 2400.0 titter
astropy.units
で定義されていなかったり同時の単位を作り活用したい場合は、単位を定義することが可能だ。
例えば上の例ではtitter
という単位をはじめに定義し、これを起点にその他の単位を定義していった。
また、下の例では2mを新たな単位として値を計算、それをm単位に換算した。
from astropy import units as u
# 2mを新たな単位として定義
double_meter = u.def_unit('dm', 2 * u.m)
print(double_meter)
# dm
# m単位に換算
print((double_meter * 2).to(u.m))
単位を定義することでより自由度高く単位付きの値を計算することが可能となる。
単位付きの数値計算はみんな使うべき
今回はastropy
の単位付与ライブラリastropy.units
を使って単位を付与した値の計算と単位換算の方法を紹介した。
これまで単位間違いをしていた人はぜひastropy.unitsで計算ミスがなくなるようにしてほしい。
単位ミスという小さな部分でつまずかず、より本質的な問題に時間を割けることを願う。