Ускорение чтения очень большого файла netcdf в python

У меня есть очень большой файл netCDF, который я читаю с помощью netCDF4 в python

Я не могу прочитать этот файл сразу, так как его размеры (1200 x 720 x 1440) слишком велики для того, чтобы весь файл находился в памяти сразу. Первый размер представляет собой время, а следующие 2 представляют собой широту и долготу соответственно.

import netCDF4 
nc_file = netCDF4.Dataset(path_file, 'r', format='NETCDF4')
for yr in years:
    nc_file.variables[variable_name][int(yr), :, :]

Однако чтение одного года за один раз мучительно медленное. Как ускорить это для использования ниже?

- EDIT

Чанкиз - 1

  • Я могу читать несколько лет: nc_file.variables [variable_name] [0: 100,:,:]

  • Существует несколько вариантов использования:

    для лет в годах:

    numpy.ma.sum(nc_file.variables[variable_name][int(yr), :, :])
    

# Multiply each year by a 2D array of shape (720 x 1440)
for yr in years:
    numpy.ma.sum(nc_file.variables[variable_name][int(yr), :, :] * arr_2d)

# Add 2 netcdf files together 
for yr in years:
    numpy.ma.sum(nc_file.variables[variable_name][int(yr), :, :] + 
                 nc_file2.variables[variable_name][int(yr), :, :])

Ответ 1

Я очень рекомендую вам взглянуть на проекты xarray и dask. Использование этих мощных инструментов позволит вам легко разделить вычисления на куски. Это дает два преимущества: вы можете рассчитывать на данные, которые не подходят в памяти, и вы можете использовать все ядра на вашем компьютере для лучшей производительности. Вы можете оптимизировать производительность, соответствующим выбору размера блока (см. документация).

Вы можете загружать свои данные из netCDF, делая что-то так же просто, как

import xarray as xr
ds = xr.open_dataset(path_file)

Если вы хотите вырезать свои данные в годах по измерению времени, тогда вы указываете параметр chunks (при условии, что координата года называется "год" ):

ds = xr.open_dataset(path_file, chunks={'year': 10})

Так как другие координаты не отображаются в chunks dict, то для них будет использоваться один кусок. (Подробнее см. В документации здесь.). Это будет полезно для вашего первого требования, где вы хотите умножать каждый год 2D-массив. Вы просто выполните:

ds['new_var'] = ds['var_name'] * arr_2d

Теперь xarray и dask лениво вычисляют ваш результат. Чтобы вызвать фактическое вычисление, вы можете просто попросить xarray сохранить ваш результат обратно в netCDF:

ds.to_netcdf(new_file)

Вычисление запускается через dask, который позаботится о разделении обработки на куски и, таким образом, позволяет работать с данными, которые не помещаются в память. Кроме того, dask позаботится о том, чтобы использовать все процессорные ядра для вычислительных блоков.

Проекты xarray и dask по-прежнему не обрабатывают приятные ситуации, когда куски не "выравниваются" хорошо для параллельных вычислений. Поскольку в этом случае мы размещались только в "годичном" измерении, мы не ожидаем никаких проблем.

Если вы хотите добавить два разных файла netCDF вместе, это будет так же просто, как:

ds1 = xr.open_dataset(path_file1, chunks={'year': 10})
ds2 = xr.open_dataset(path_file2, chunks={'year': 10})
(ds1 + ds2).to_netcdf(new_file)

Я предоставил полностью рабочий пример, используя набор данных, доступный в Интернете.

In [1]:

import xarray as xr
import numpy as np

# Load sample data and strip out most of it:
ds = xr.open_dataset('ECMWF_ERA-40_subset.nc', chunks = {'time': 4})
ds.attrs = {}
ds = ds[['latitude', 'longitude', 'time', 'tcw']]
ds

Out[1]:

<xarray.Dataset>
Dimensions:    (latitude: 73, longitude: 144, time: 62)
Coordinates:
  * latitude   (latitude) float32 90.0 87.5 85.0 82.5 80.0 77.5 75.0 72.5 ...
  * longitude  (longitude) float32 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 ...
  * time       (time) datetime64[ns] 2002-07-01T12:00:00 2002-07-01T18:00:00 ...
Data variables:
    tcw        (time, latitude, longitude) float64 10.15 10.15 10.15 10.15 ...

In [2]:

arr2d = np.ones((73, 144)) * 3.
arr2d.shape

Out[2]:

(73, 144)

In [3]:

myds = ds
myds['new_var'] = ds['tcw'] * arr2d

In [4]:

myds

Out[4]:

<xarray.Dataset>
Dimensions:    (latitude: 73, longitude: 144, time: 62)
Coordinates:
  * latitude   (latitude) float32 90.0 87.5 85.0 82.5 80.0 77.5 75.0 72.5 ...
  * longitude  (longitude) float32 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 ...
  * time       (time) datetime64[ns] 2002-07-01T12:00:00 2002-07-01T18:00:00 ...
Data variables:
    tcw        (time, latitude, longitude) float64 10.15 10.15 10.15 10.15 ...
    new_var    (time, latitude, longitude) float64 30.46 30.46 30.46 30.46 ...

In [5]:

myds.to_netcdf('myds.nc')
xr.open_dataset('myds.nc')

Out[5]:

<xarray.Dataset>
Dimensions:    (latitude: 73, longitude: 144, time: 62)
Coordinates:
  * latitude   (latitude) float32 90.0 87.5 85.0 82.5 80.0 77.5 75.0 72.5 ...
  * longitude  (longitude) float32 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 ...
  * time       (time) datetime64[ns] 2002-07-01T12:00:00 2002-07-01T18:00:00 ...
Data variables:
    tcw        (time, latitude, longitude) float64 10.15 10.15 10.15 10.15 ...
    new_var    (time, latitude, longitude) float64 30.46 30.46 30.46 30.46 ...

In [6]:

(myds + myds).to_netcdf('myds2.nc')
xr.open_dataset('myds2.nc')

Out[6]:

<xarray.Dataset>
Dimensions:    (latitude: 73, longitude: 144, time: 62)
Coordinates:
  * time       (time) datetime64[ns] 2002-07-01T12:00:00 2002-07-01T18:00:00 ...
  * latitude   (latitude) float32 90.0 87.5 85.0 82.5 80.0 77.5 75.0 72.5 ...
  * longitude  (longitude) float32 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 ...
Data variables:
    tcw        (time, latitude, longitude) float64 20.31 20.31 20.31 20.31 ...
    new_var    (time, latitude, longitude) float64 60.92 60.92 60.92 60.92 ...

Ответ 2

Проверьте фрагментацию файла. ncdump -s <infile> даст ответ. Если размер фрагмента во временном измерении больше единицы, вы должны прочитать то же количество лет одновременно, в противном случае вы читаете несколько лет одновременно с диска и используете только по одному. Как медленно идет медленно? Максимальное количество секунд за один временной интервал звучит разумно для массива такого размера. Предоставление дополнительной информации о том, что вы делаете с данными позже, может дать нам больше рекомендаций о том, где может быть проблема.

Ответ 3

Это Kinda Hacky, но может быть самым простым решением:

Прочитайте подмножества файла в памяти, затем cPickle (https://docs.python.org/3/library/pickle.html) файл обратно на диск для дальнейшего использования. Загрузка ваших данных из маринованной структуры данных, вероятно, будет быстрее, чем разбор netCDF каждый раз.