여러 nc 파일을 빠르게 읽으려면?
- 개요
nc파일은 NetCDF(Network Common Data Form) 파일로 대기과학 분야에서 많이 사용하는 파일 형식입니다.
특히 위도, 경도, 연직층의 좌표계를 갖는 자료는 보통 nc 파일로 저장되어 있습니다.
예전에 저는 ERA5 재분석 자료 1달치를 하나의 nc파일로 저장했었는데요.
이 nc 파일에는 특정 변수가 [30일 x 24시간 x 위도격자 x 경도격자 x 연직층]의 차원으로 저장되므로 용량이 꽤 큽니다.
물론 좁은 영익인 한반도 근처 자료만 받았지만 30년치라 그냥 이 자료를 모두 읽는 것만 해도 시간이 꽤 걸립니다.
그럼 읽는 속도를 비교하며 nc 파일을 가장 빨리 읽는 방법을 알아보겠습니다.
결론을 먼저 말하자면 xarray에서 병렬처리로 파일을 읽는 것이 제일 빠릅니다.
- 사용할 라이브러리
nc 파일을 대표적인 라이브러리로 netCDF4와 xarray가 있습니다.
가상환경에 해당 라이브러리를 설치합시다.
그리고 xarray의 병렬처리 위해 dask와 dask[distributed] 라이브러리를 설치합니다.
pip install netCDF4
pip install xarray
pip install dask
pip install dask[distributed]
- netCDF4 라이브러리 사용
4년치(2020~2023년)의 ERA5 자료에서 z 변수의 연직층 1개를 읽겠습니다.
코드를 간단히 설명하면
파일 하나씩 읽어서 vars.append()를 이용해 list에 저장한 다음
np.concatenate()로 list에 있는 변수를 시간 축(axis=0)으로 연결합니다.
time.time()으로 stime(시작시간), etime(끝나는 시간)의 차이를 계산합니다.
import netCDF4 as nc
import time
import os
inpath = '{nc파일 저장 폴더}'
varname = 'z'
stime = time.time()
vars = []
for year in range(syr, fyr+1):
for month in range(1, 12+1):
ds = nc.Dataset(os.path.join(inpath, f'download_{year}_{month:02d}.nc'))
var = ds.variables[varname][:][:,0,:,:]
vars.append(var)
result_nc = np.concatenate(vars, axis=0)
etime = time.time()
print(f'{etime-stime:.3f}')
위의 코드를 총 5번 돌려보았고, 결과는 아래와 같습니다.
33.214초, 31.920초, 31.026초, 31.663초, 31.2070초
첫번째 run에서 시간이 좀 걸리긴 했는데
netCDF4 라이브러리는 파일을 읽는데 약 30초 걸린다고 보면 될듯합니다.
참고로 results_nc의 차원은 (35064, 121, 121)입니다.
- xarray 사용
1. 하나씩 읽고 저장
위의 코드에서 netCDF4 대신 xarray 라이브러리를 사용했습니다.
syr = 2020
fyr = 2023
varname = 'z'
stime = time.time()
vars = []
for year in range(syr, fyr+1):
for month in range(1, 12+1):
ds = xr.open_dataset(os.path.join(inpath, f'download_{year}_{month:02d}.nc'))
var = ds[varname].isel(level=0)
vars.append(var)
result_xr = np.concatenate(vars, axis=0)
etime = time.time()
print(f'{etime-stime:.3f}')
마찬가지로 위의 코드를 총 5번 돌려보았고, 결과는 아래와 같습니다.
23.306초, 23.491초, 23.039초, 22.886초, 23.024초
xarray 라이브러리는 파일을 읽는데 약 23초 걸f립니다.
2. open_mfdataset을 이용하여 한 번에 읽기
xr.open_mfdataset()은 여러 파일을 한번에 읽을 때 쓰는 메서드로
사용할 때 dask.distributed의 Client 설정을 해야합니다.
from dask.distributed import Client
syr = 2020
fyr = 2023
varname = 'z'
stime = time.time()
client = Client(n_workers=2, threads_per_worker=1) # Client로 사용할 CPU 수(num_workers)와 threads 수 선언
file_paths = [os.path.join(inpath, f'download_{year}_{month:02d}.nc')
for year in range(syr, fyr + 1)
for month in range(1, 13)]
ds = xr.open_mfdataset(file_paths, concat_dim='time', combine='nested', parallel=True)
var = ds[varname].isel(level=0)
etime = time.time()
client.close()
print(f'{etime-stime:.3f}')
5번 돌린 결과는
2.001초, 1.960초, 2.054초, 1.921초, 1.915초
xarray의 병렬처리를 사용하면 약 2초가 걸립니다.
- 결론
netCDF4: 30초
xarray(싱글): 23초
xarray(병렬): 2초
xarray 병렬처리가 netCDF4에 비해 10배는 빠르네요.