Более быстрое считывание временных рядов из netCDF?

У меня есть несколько больших файлов netCDF, которые содержат 6 ежечасных данных для земли с разрешением 0,5 градуса.

Существует 360 точек широты, 720 точек долготы и 1420 временных точек в год. У меня есть как годовые файлы (12 ГБ ea), так и один файл с 110-летними данными (1,3 ТБ), хранящийся как netCDF-4 (вот пример данных 1901, 1901.nc, использовать политику, а исходные, общедоступные файлы, с которых я начал с).

Из того, что я понял, должно быть быстрее читать из одного файла netCDF, а не зацикливаться на наборе файлов разделенных по году и первоначально предоставленной переменной.

Я хочу извлечь временные ряды для каждой точки сетки, например. 10 или 30 лет с определенной широты и долготы. Однако я нахожу это очень медленным. В качестве примера мне понадобится 0,01 секунды для чтения в 10 значениях с течением времени из местоположения точки, хотя я могу читать в глобальном срезе 10000 значений из одной временной точки за 0,002 секунды (порядок измерений - это lat, lon, время):

## a time series of 10 points from one location:
library(ncdf4)
met.nc <- nc_open("1901.nc")
system.time(a <- ncvar_get(met.nc, "lwdown", start = c(100,100,1), 
                                             count = c(1,1,10)))
   user  system elapsed 
  0.001   0.000   0.090 

## close down session

## a global slice of 10k points from one time
library(ncdf4)
system.time(met.nc <- nc_open("1901.nc"))
system.time(a <- ncvar_get(met.nc, "lwdown", start = c(100,100,1), 
                                             count = c(100,100,1)))
   user  system elapsed 
  0.002   0.000   0.002 

Я подозреваю, что эти файлы были написаны для оптимизации чтения пространственных слоев, потому что: a) порядок переменных - lat, lon, time, b), который будет логическим порядком для климатических моделей, которые генерировали эти файлы, и c) потому что глобальные экстенты являются наиболее распространенной визуализацией.

Я попытался переупорядочить переменные, чтобы время было первым:

ncpdq -a time,lon,lat 1901.nc 1901_time.nc

(ncpdq из программное обеспечение NCO (netCDF))

> library(ncdf4)

## first with the original data set:
> system.time(met.nc <- nc_open("test/1901.nc"))
   user  system elapsed 
  0.024   0.045  22.334 
> system.time(a <- ncvar_get(met.nc, "lwdown", start = c(100,100,1), count = c(1, 1, 1000))
+ )
   user  system elapsed 
  0.005   0.027  14.958 

## now with the rearranged dimensions:
> system.time(met_time.nc <- nc_open("test/1901_time.nc"))
   user  system elapsed 
  0.025   0.041  16.704 
> system.time(a <- ncvar_get(met_time.nc, "lwdown", start = c(100,100,1), count = c(1, 1, 1000)))
   user  system elapsed 
  0.001   0.019   9.660 

Как я могу оптимизировать временные ряды чтения в точке, а не слоях больших площадей за один раз? Например, было бы быстрее, если бы файлы были написаны по-разному, например, время, lat, lon? Есть ли "простой" способ преобразования порядка размеров в файле netCDF-4?

Update

(тесты, запрошенные @mdsumner)

library(rbenchmark)
library(ncdf4)
nc <- nc_open("1901.nc")
benchmark(timeseries = ncvar_get(nc, "lwdown", 
                                 start = c(1, 1, 50), 
                                 count = c(10, 10, 100)), 
          spacechunk = ncvar_get(nc, "lwdown", 
                                  start = c(1, 1, 50), 
                                  count = c(100, 100, 1)),           
          replications = 1000)

        test replications elapsed relative user.self sys.self user.child
2 spacechunk         1000   0.909    1.000     0.843    0.066          0
1 timeseries         1000   2.211    2.432     1.103    1.105          0
  sys.child
2         0
1         0

Обновление 2:

Я начал разрабатывать решение здесь. Биты и фрагменты находятся в наборе сценариев в github.com/ebimodeling/model-drivers/tree/master/met/cruncep

Сценарии по-прежнему нуждаются в некоторой работе и организации - не все сценарии полезны. Но чтение молниеносно. Не совсем сопоставимо с приведенными выше результатами, но в конце дня я могу мгновенно прочитать шестичасовую временную серию из 1,3-мегабайтного файла (разрешение 0,5 градуса в 2,5 секунды):

system.time(ts <- ncvar_get(met.nc, "lwdown", start = c(50, 1, 1), count = c(160000, 1, 1)))
   user  system elapsed 
  0.004   0.000   0.004 

(примечание: порядок измерений изменился, как описано здесь: Как указать порядок размеров при использовании ncdf4:: ncvar_get?)

Ответ 1

Я думаю, что ответ на эту проблему будет не столько переупорядочивать данные, сколько разбивать данные. Для полной дискуссии о последствиях chunking netCDF файлов см. Следующие сообщения в блоге от Russ Rew, ведущего разработчика netCDF в Unidata:

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

В меньшем наборе данных образца sst.wkmean.1990-present.nc я увидел следующие результаты при использовании вашей тестовой команды:

1) Unchunked:

## test replications elapsed relative user.self sys.self user.child sys.child
## 2 spacechunk         1000   0.841    1.000     0.812    0.029          0         0
## 1 timeseries         1000   1.325    1.576     0.944    0.381          0         0

2) Наивный Chunked:

## test replications elapsed relative user.self sys.self user.child sys.child
## 2 spacechunk         1000   0.788    1.000     0.788    0.000          0         0
## 1 timeseries         1000   0.814    1.033     0.814    0.001          0         0

Наивный кусок был просто выстрелом в темноте; Я использовал утилиту nccopy следующим образом:

$nccopy -c "lat/100, lon/100, time/100, nbnds/" sst.wkmean.1990-present.nc chunked.nc

Документацию Unidata для утилиты nccopy можно найти здесь.

Я бы хотел рекомендовать конкретную стратегию для разбивки набора данных, но она сильно зависит от данных. Надеемся, что статьи, приведенные выше, дадут вам некоторое представление о том, как вы можете фрагментировать свои данные, чтобы достичь результатов, которые вы ищете!

Update

Следующее сообщение в блоге Marcos Hermida показывает, как различные стратегии фрагментации влияли на скорость при чтении временных рядов для конкретного файла netCDF. Это должно использоваться только как возможно прыгающая точка.

Что касается повторной записи через nccopy, видимо, висит; проблема связана с размером кеша по умолчанию размером 4 МБ. Увеличивая это до 4 ГБ (или более), вы можете сократить время копирования более 24 часов для большого файла до 11 минут!

Один момент, о котором я не уверен; в первой ссылке обсуждение относится к chunk cache, но аргумент, переданный nccopy, -m, указывает количество байтов в буфере копирования. Аргумент -m для nccopy управляет размером кеша блока.

Ответ 2

РЕДАКТИРОВАТЬ: исходный вопрос ошибся, но для начала чтения могут быть разные накладные расходы, поэтому можно сделать несколько повторений. rbenchmark делает это легко.

Пример файла немного массивный, поэтому я использовал меньший, вы можете сделать то же самое сравнение с вашим файлом?

Более доступный пример файла: ftp://ftp.cdc.noaa.gov/Datasets/noaa.oisst.v2/sst.wkmean.1990-present.nc

Я получаю больше вдвое больше времени, затраченного на временные ряды:

library(ncdf4)

nc <- nc_open("sst.wkmean.1990-present.nc")

library(rbenchmark)
benchmark(timeseries = ncvar_get(nc, "sst", start = c(1, 1, 50), count = c(10, 10, 100)), 
spacechunk = ncvar_get(nc, "sst", start = c(1, 1, 50), count = c(100, 100, 1)),           
replications = 1000)
##        test replications elapsed relative user.self sys.self user.child sys.child
##2 spacechunk         1000    0.47    1.000      0.43     0.03         NA        NA
##1 timeseries         1000    1.04    2.213      0.58     0.47         NA        NA

Ответ 3

Не уверен, что вы рассмотрели cdo для извлечения точки?

cdo remapnn,lon=x/lat=y in.nc point.nc 

Иногда у CDO заканчивается память, если это происходит, вам может потребоваться цикл над годовыми файлами, а затем cat отдельные файлы точек с помощью

cdo mergetime point_${yyyy}.nc point_series.nc