2016-02-16 3 views
11

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

Я не могу прочитать этот файл сразу, так как его размеры (1200 х 720 х 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

chunksize является 1

  1. Я могу прочитать целый ряд лет: nc_file.variables [имя_переменной] [0: 100,:,:]

  2. Есть несколько сценариев использования:

    за год в годы:

    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), :, :]) 
+0

Вы уверены, что читаете какие-либо другие вопросы (например, весь файл за раз), будет быстрее? Можете ли вы попробовать с обрезанным файлом? –

+0

Любое [существенное профилирование] (http://stackoverflow.com/questions/582336/how-can-you-profile-a-python-script) сделано? –

+0

Вы делаете что-либо с данными года, как только вы его читаете? Можете ли вы прочитать несколько лет, например. '[1997: 2007,:,:]'? – hpaulj

ответ

17

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

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

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

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

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

Поскольку другие координаты не появляются в chunks Словаре, то один блок будет использоваться для них. (См. Более подробную информацию в документации here.). Это будет полезно для вашего первого требования, где вы хотите умножать каждый год 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) 

Я представил полностью рабочий пример использования a dataset available online.

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 ... 
+0

благодарит @Pedro, будет смотреть ближе к xarray – user308827

2

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

+0

thanks @ kakk11, chunksize = 1 – user308827

+0

1 во времени измерение и что это в других измерениях? Можете ли вы также уточнить, как медленный «медленный» в вашем случае! – kakk11

+0

Размер корпуса - 720 и 1440 для других размеров. Для каждой итерации цикла требуется доля секунды. Но это добавляется, когда вам нужно перебирать более 1200 лет. – user308827

1

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

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

+0

Весьма вероятно, что запись/чтение hdf5 с blosc-сжатием, как в PyTables, на самом деле быстрее, чем cPickle. Не говоря уже о размере файла, который может стать очень большим для несжатых числовых данных! – kakk11

Смежные вопросы