気象データを長期間に渡って異なる気圧面でプロットしようとすると、for文によるネストやnetCDFファイルの読み込みが自然と多くなる。
そこでfor文のネストの構造を変えることで、データの配列を得るのにかかる時間がどのように変わるのかを調べてみた。
検証コード1
検証コードはこちらを参考にした。
データはNCAR's RDAから得た。データにはジオポテンシャルで、[時間, 気圧面, 緯度, 経度]の4次元配列である。なお、時間は0~23の24時間、気圧面は37となっている。
func_1
はデータを1回読み込み、3次元配列をaに格納している。その後、各気圧面におけるジオポテンシャルをbに代入している。
func_2
は各気圧面ごとにデータを読み込み、2次元配列をその都度得ている。
import timeit
from netCDF4 import Dataset
def func_1():
nc = Dataset('../data/era5/Z_2020070100_2020070123.nc', 'r')
a = nc.variables['Z'][0, :]
for i in range(len(a)):
b = a[i, :]
return 0
def func_2():
nc = Dataset('../data/era5/Z_2020070100_2020070123.nc', 'r')
lev = nc.variables['level'][:]
for i in range(len(lev)):
b = nc.variables['Z'][0, i, :]
return 0
loop = 20
result_1 = timeit.timeit(lambda: func_1(), number=loop)
result_2 = timeit.timeit(lambda: func_2(), number=loop)
print('1: ', result_1 / loop)
print('2: ', result_2 / loop)
結果は
1: 0.009774951753206551
2: 0.018051519710570573
のようになった。なんと2倍もの速度差がある。
これより、3次元配列を1度に読み込む速度と2次元配列をその都度読み込む速度では、3次元配列を読み込む速度の方が優位であることが分かった。
検証コード2
扱いたいのデータは4次元配列なので4次元配列の場合も調べる。func_1
とfunc_2
は検証コード1同様、func_1
が読み込み1回、func_2
がその都度読み込みとする。
import timeit
from netCDF4 import Dataset
def func_1():
nc = Dataset('../data/era5/Z_2020070100_2020070123.nc', 'r')
a = nc.variables['Z'][0, :]
for i in range(len(a)):
b = a[i, :]
return 0
def func_2():
nc = Dataset('../data/era5/Z_2020070100_2020070123.nc', 'r')
lev = nc.variables['level'][:]
for i in range(len(lev)):
b = nc.variables['Z'][0, i, :]
return 0
loop = 10
result_1 = timeit.timeit(lambda: func_1(), number=loop)
result_2 = timeit.timeit(lambda: func_2(), number=loop)
print('1: ', result_1 / loop)
print('2: ', result_2 / loop)
すると、結果は以下のようになった。
1: 1.4546271565370261
2: 1.3412013622000813
なんと、3次元の時は速かった一気に配列を読み込む方法が、4次元の時はその都度読み込む方法よりも遅いことが分かった。配列も多次元になるとfor文以上に処理速度が遅くなってしまうということであろうか。興味深い結果である。
またこれから、その二つのいいとこ取りをすればもっとも速くデータを読み込むことのできるプログラムになるのではと考え、次のコードの速度比較をした。
検証コード3
新たにfunc_3
を作成し速度を比較した。func_3
は時間はfor文で回し、その時間ごとの3次元配列[気圧面, 緯度, 経度]を読み込む関数である。
from netCDF4 import Dataset
import timeit
def func_1():
nc = Dataset('../data/era5/Z_2020070100_2020070123.nc', 'r')
a = nc.variables['Z'][:]
for i in range(len(a)):
b = a[i, :]
for j in range(len(b)):
c = b[j, :]
return 0
def func_2():
nc = Dataset('../data/era5/Z_2020070100_2020070123.nc', 'r')
time = nc.variables['time'][:]
lev = nc.variables['level'][:]
for j in range(len(time)):
for i in range(len(lev)):
b = nc.variables['Z'][j, i, :]
return 0
def func_3():
nc = Dataset('../data/era5/Z_2020070100_2020070123.nc', 'r')
time = nc.variables['time'][:]
for j in range(len(time)):
a = nc.variables['Z'][j, :]
for i in range(len(a)):
b = a[i, :]
return 0
loop = 10
result_1 = timeit.timeit(lambda: func_1(), number=loop)
result_2 = timeit.timeit(lambda: func_2(), number=loop)
result_3 = timeit.timeit(lambda: func_3(), number=loop)
print('1: ', result_1 / loop)
print('2: ', result_2 / loop)
print('3: ', result_3 / loop)
結果は以下の通り。
1: 1.4101094176992774
2: 1.344068780587986
3: 1.0753227178938687
見立てどおり、func_3
が最速となった。
結論
3つの検証により分かったことは、
- 3次元配列[気圧面, 緯度, 経度]まではまとめて読み込んだ方が速く、
- 4次元配列[時間, 気圧面, 緯度, 経度]はfor文で回して3次元配列として扱った方が速い
ということである。
気象データの可視化は時間のかかる作業である。この検証が時間の節約に役立てば幸いである。