从 netCDF 更快地读取时间序列?

Posted

技术标签:

【中文标题】从 netCDF 更快地读取时间序列?【英文标题】:Faster reading of time series from netCDF? 【发布时间】:2013-11-25 00:43:31 【问题描述】:

我有一些大型 netCDF 文件,其中包含 0.5 度分辨率下地球的 6 小时数据。

每年有360个纬度点、720个经度点和1420个时间点。我有两个年度文件(12 GB ea)和一个包含 110 年数据(1.3 TB)的文件存储为 netCDF-4(这是 1901 年数据的示例,1901.nc,它的use policy,和@987654323 @)。

据我了解,从一个 netCDF 文件中读取应该比循环遍历一组文件 separated by year and variable originally provided 更快。

我想为每个网格点提取一个时间序列,例如距特定纬度和经度 10 年或 30 年。但是,我发现这非常慢。例如,尽管我可以在 0.002 秒内从单个时间点读取 10000 个值的全局切片(维度的顺序是 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 operators) software)

> 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 

如何优化在某个时间点读取时间序列,而不是在一个时间点优化大面积层?例如,如果文件以不同的方式编写,例如时间、纬度、经度,会更快吗?有没有一种“简单”的方法来转换 netCDF-4 文件中的维度顺序?

更新

(@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.3TB 文件(2.5 秒内 0.5 度分辨率)中读取 100 年、6 小时的时间序列:

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 

注意:维度的顺序已经改变,如下所述:How can I specify dimension order when using ncdf4::ncvar_get?)

【问题讨论】:

count 表示从一开始就有很多单元格,所以你不是说 count = c(1, 1, 10) 为第一次读取吗? @mdsumner 感谢您指出这一点。我已经更新了我的问题(使用新的、更快的时间)。 【参考方案1】:

我认为这个问题的答案不会是对数据重新排序,而是对数据进行分块。有关对 netCDF 文件分块的影响的完整讨论,请参阅 Unidata 首席 netCDF 开发人员 Russ Rew 的以下博客文章:

Chunking Data: Why it Matters Chunking Data: Choosing Shapes

结果是,虽然采用不同的分块策略可以大幅提高访问速度,但选择正确的策略并非易事。

在较小的样本数据集sst.wkmean.1990-present.nc 上,我在使用您的基准测试命令时看到了以下结果:

1) 未分块:

## 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) 天真的分块:

## 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

nccopy 实用程序的 Unidata 文档可以在 here 找到。

我希望我可以推荐一种特定的策略来分块您的数据集,但它高度依赖于数据。希望上面链接的文章能让您深入了解如何对数据进行分块以获得所需的结果!

更新

Marcos Hermida 的以下博客文章展示了在读取特定 netCDF 文件的时间序列时不同的分块策略如何影响速度。这只能作为一个起点。

Netcdf-4 Chunking Performance Results on AR-4 3D Data File

关于通过nccopy 重新分块显然挂起;该问题似乎与 4MB 的默认块缓存大小有关。通过将其增加到 4GB(或更多),您可以将大文件的复制时间从 24 小时以上减少到 11 分钟以下!

Nccopy extremly slow/hangs Unidata JIRA Trouble Ticket System: NCF-85, Improve use of chunk cache in nccopy utility, making it practical for rechunking large files.

有一点我不确定;在第一个链接中,讨论是关于 chunk cache,但传递给 nccopy 的参数 -m 指定了复制缓冲区中的字节数。 nccopy 的 -m 参数控制块缓存的大小。

【讨论】:

我的错误,我刚刚注意到较小的文件 sst.wkmean.1990-present.nc 是由另一个答案建议的,而不是原始海报。然而,它说明了分块可以带来的不同。 我通过所有链接完成了它。听起来我可以从重新排列文件以支持时间序列中受益。但我认为这就是 nvpdq -a time,lat,lon 会做的事情,但我没有得到预期的加速。 nccopy -c"time/1420" 会这样做吗?并且使用 -u 删除时间的无限属性也会使其更快吗?谢谢! 我一直在尝试nccopy -c 'time/1500,lat/10,lon/10' 1901.nc 1901ts.nc,它似乎被挂起 - 它在一个小时左右的时间内创建了一个类似大小的输出文件,但该文件在过去 2 中没有改变大小小时。有什么建议吗? @David 我对 nvpdq 不熟悉,尽管使用 nccopy 使time 维度变化最快也没有为我带来任何加速。 哇哦。这是一个更新:出于某种原因,即使指定-m 6GB was kind of slow. But specifying -w` 而是只指定一个时间维度块-c time/1460,现在我每个年度文件的时间减少到 2 分钟(+时间花在学习和实验上)。感谢您的帮助和指点。【参考方案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

【讨论】:

感谢您的建议;我已经用基准更新了我的问题 我意识到,第一次之后,速度变得更快,因为信息保留在内存中。因此,如果我重复 ncvar_get 命令,第一次可能是 10 秒,下一次可能是 0.001。但感谢您指出这种计时方法。【参考方案3】:

不确定您是否考虑过 cdo 提取点?

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

有时 CDO 会耗尽内存,如果发生这种情况,您可能需要遍历年度文件,然后使用

对单独的点文件进行分类
cdo mergetime point_$yyyy.nc point_series.nc 

【讨论】:

问题不在于提取点本身,而是从单个多年文件中有效地提取。最终的解决方案是为这个用例重新组织文件。

以上是关于从 netCDF 更快地读取时间序列?的主要内容,如果未能解决你的问题,请参考以下文章

Python - 从netCDF文件中读取数据,时间为“自测量开始以来的秒数”

如何在 selenium 中更快地从动态网站读取数据

无法读取 NetCDF 结构变量数组子范围

python 从netcdf文件中读取数据点。

在 R 中更快地读取 SQL 表

使用 psycopg2 copy_expert 从 postgreSQL 表中更快地读取数据