一个库,用于以高效的方式在对象存储中存储netCDF文件。
项目描述
S3netCDF4
netCDF4-python的扩展包,用于通过S3 HTTP接口从/到对象存储和公共云读取和写入netCDF文件和CFA-netcdf文件,到磁盘或到OPeNDAP。
内容
需求
S3-netCDF4需要Python 3.7或更高版本。
它还需要以下包
- numpy==1.19.4
- Cython==0.29.21
- netCDF4==1.5.5.1
- botocore==1.19.20
- aiobotocore==1.1.2
- psutil==5.7.3
(这些通过pip安装满足,因此如果您通过以下方式安装包,则无需安装它们。)
安装
S3netCDF4设计为安装在用户空间中,无需用户拥有root
或sudo
权限。也支持全局安装。建议将S3netCDF4安装到虚拟环境中,而不是使用系统Python。S3netCDF4不依赖于任何外部服务器,除了存储系统,它完全在主机机器上运行。
s3netCDF4可以从PyPi或直接从GitHub仓库安装。
从PyPi
- 创建一个Python 3虚拟环境
python3 -m venv /path/to/venv
- 激活虚拟环境
source /path/to/venv/bin/activate
- 安装S3netCDF4需要版本大于10.0的
pip
。要将在虚拟环境中安装pip的最新版本,请使用以下命令
pip install --upgrade pip
- 从PyPi安装
pip安装S3netCDF4
-
将配置模板文件从
config/.s3nc.json.template
复制到~/.s3nc.json
,并填写变量的值。参见配置部分。 -
运行测试以确保软件包已正确安装
python test/test_s3Dataset.py
从GitHub
-
STFC/NERC JASMIN系统上的用户必须通过使用命令来激活Python 3.7
module load jaspy
-
创建一个Python 3虚拟环境
python3 -m venv /path/to/venv
- 激活虚拟环境
source /path/to/venv/bin/activate
- 安装S3netCDF4需要版本大于10.0的
pip
。要将在虚拟环境中安装pip的最新版本,请使用以下命令
pip install --upgrade pip
- 直接从GitHub仓库安装S3netCDF4库
pip install -e git+https://github.com/cedadev/S3-netcdf-python.git#egg=S3netCDF4
-
将配置模板文件从
config/.s3nc.json.template
复制到~/.s3nc.json
,并填写变量的值。参见配置部分。 -
运行测试以确保软件包已正确安装
python test/test_s3Dataset.py
- STFC/NERC JASMIN系统上的用户每次想要通过虚拟环境使用S3netCDF4时都必须重复步骤0。
配置
S3netCDF4依赖于配置文件来解析S3服务的端点,并控制软件包操作的各个方面。此配置文件是一个JSON文件,位于用户的家目录中
~/.s3nc.json
在git仓库中提供了一个此配置文件的模板示例
config/.s3nc.json.template
可以将其复制到用户的家目录中,并将模板重命名为~/.s3nc.json
。
或者,可以设置环境变量S3_NC_CONFIG
来定义配置文件的位置和名称。这也可以在导入S3netCDF4模块之前在代码中设置
import os
os.environ["S3_NC_CONFIG"] = "/Users/neil/.s3nc_different_config.json"
from S3netCDF4._s3netCDF4 import s3Dataset
配置文件复制后,应填写模板中的变量。此文件是JSON文件的jinja2模板,因此可以在ansible部署中使用。
文件中的每个条目都有一个键:值对。以下是一个文件的示例
{
"version": "9",
"hosts": {
"s3://tenancy-0": {
"alias": "tenancy-0",
"url": "http://tenancy-0.jc.rl.ac.uk",
"credentials": {
"accessKey": "blank",
"secretKey": "blank"
},
"backend": "s3aioFileObject",
"api": "S3v4"
}
},
"backends": {
"s3aioFileObject" : {
"maximum_part_size": "50MB",
"maximum_parts": 8,
"enable_multipart_download": true,
"enable_multipart_upload": true,
"connect_timeout": 30.0,
"read_timeout": 30.0
},
"s3FileObject" : {
"maximum_part_size": "50MB",
"maximum_parts": 4,
"enable_multipart_download": false,
"enable_multipart_upload": false,
"connect_timeout": 30.0,
"read_timeout": 30.0
}
},
"cache_location": "/cache_location/.cache",
"resource_allocation" : {
"memory": "1GB",
"filehandles": 20
}
}
version
指示这是配置文件的哪个版本。hosts
包含一组命名主机及其相应的配置详细信息。backends
包含用于写入S3服务器的每个可能使用(如果包含在host
定义中)的后端的本地区置配置信息。请参阅后端部分以获取有关后端的更多详细信息。enable_multipart_download
允许后端在下载时将从S3获取的文件拆分为多个部分。enable_multipart_upload
允许后端在上传时拆分文件。将文件拆分为部分的优势是,当后端支持异步传输时,它们可以异步上传或下载。maximum_part_size
文件上传前每个部分的最大大小,或下载文件时每个部分的大小。maximum_parts
在上传或下载之前保留在内存中的文件部分的最大数量,或对于异步后端,一次下载的文件部分数量。connect_timeout
连接尝试超时的秒数。read_timeout
读取尝试超时的秒数。
cache_location
S3netCDF4可以读取和写入非常大的数组分块到子数组。为了使非常大的数组能够被读取,S3netCDF4使用NumPy内存映射数组。cache_location
包含这些内存映射数组文件的位置。请参见下面的缓存部分。resource_allocation
包含有关S3netCDF4每个实例在主机机器上应使用多少资源的本地化信息。请参见下面的资源使用部分。它包含两个键memory
为此S3netCDF4实例分配的RAM量。file_handles
为此S3netCDF4实例分配的文件句柄数量
请注意,大小可以用字节以外的单位表示,方法是在大小后附加一个量级标识符:千字节(kB
)、兆字节(MB
)、吉字节(GB
)、太字节(TB
)、艾字节(EB
)、泽字节(ZB
)或尧字节(YB
)。
别名
为了使S3netCDF4能够写入磁盘,使用OPeNDAP和S3对象存储,别名用于识别S3服务器。它们为用户提供了一个容易记住(并输入)的简写,因此用户不需要为每次S3对象访问使用DNS解析的URL和端口号。在创建用于读取或写入的netCDF4 s3Dataset
对象时,用户提供文件名。为了指示该文件应写入或读取到S3服务器,字符串必须以s3://
开头。之后必须跟上面配置文件中定义的别名服务器名称。之后是桶名称,例如,从s3://tenancy-0
服务器上的test
桶读取名为test2.nc
的netCDF文件,用户将使用以下代码
示例1:使用别名"tenancy-0"从S3存储打开netCDF文件
from S3netCDF4._s3netCDF4 import s3Dataset as Dataset
test_dataset = Dataset("s3://tenancy-0/test/test2.nc", "r")
在创建s3Dataset
对象时,S3netCDF4包读取文件名,确定文件名以s3://
开头,读取字符串的下一个部分,直到下一个/
(在本例中等于tenancy-0
),然后搜索在~/.s3nc.json
文件中定义的别名。如果没有找到,将返回错误消息;如果找到,则将建立与该S3服务器的连接,使用该服务器定义的url
、accessKey
和secretKey
。所有此s3Dataset
的数据传输都通过此连接进行。
缓存
如果用户请求读取一个变量或变量的一个切片,该变量的大小大于主机机器的物理内存或~/.s3nc.json
中的resource_allocation: memory
设置,则S3netCDF4将使用两种策略来启用读取非常大的数组
- 使用NumPy内存映射数组作为"目标数组",该数组将包含用户请求的数据。这些数据存储在本地缓存的文件中,在
cache_location
根目录下。这些文件在S3netCDF4析构函数中被删除——即在程序退出或S3netCDF4对象超出作用域时。然而,在处理过程中,这个目录可能会变得相当大,因此在磁盘上应为此提供足够的存储空间。 - 如果正在读取的文件是引用 子数组 文件的 CFA-netCDF 文件,则这些 子数组 文件会被流式传输到内存中(对于存储在S3上的文件)或从磁盘读取。如果使用的内存量超过
resource_allocation: memory
配置设置,或者打开的文件数量超过resource_allocation: filehandles
配置设置,则最后访问的 子数组 文件会被关闭。这意味着它将从内存中移除,或者文件句柄将被释放,以便读取另一个 子数组 文件。
有关此“内存和文件交换”行为的更多信息,请参阅下面的资源使用部分。
后端
在S3-netCDF4中,后端是指处理存储系统接口的一组例程。该接口包括读取和写入,还包括收集文件信息和文件列表。S3-netCDF4具有可插拔的后端架构,因此可以通过编写新的后端插件与新的存储系统交互。后端插件是Python类 io.BufferedIOBase
的扩展,并实现了Python文件对象方法,如 tell
、seek
、read
和 write
。这使得可以像POSIX磁盘一样与后端交互。这些后端必须通过在 ~/.s3nc.json
配置文件中设置 host: backend
值来在主机上进行主机特定的配置。目前有两个后端
_s3aioFileObject
:此后端允许异步传输到兼容S3的存储系统。它是S3最快的后端,应该优先于_s3FileObject
使用。_s3FileObject
:这是一个更简单、同步的S3存储系统接口。如果使用_s3aioFileObject
出现问题,可以使用它。
资源使用
S3netCDF4具有读取和写入非常大型文件的能力,这些文件的大小远大于机器上可用或分配的内存。它还具有读取和写入许多文件到和从磁盘的能力,这意味着打开的文件数量可能会超过文件系统设置的限制或 ulimit
设置。
当打开数据集时以及当在 CFA-netCDF 文件上使用切片运算符([x,y,z]
)时访问文件。
为了能够读取和写入非常大和非常多的文件,S3netCDF4采用了一种策略,即文件会被“交换”出内存(以释放内存)或关闭(以释放磁盘句柄)。此交换的触发器配置在 .s3nc.json
配置文件中的 "resource_allocation"
部分
-
resource_allocation: memory
:触发交换之前S3netCDF4允许使用的内存量。这适用于从/到远程存储(如S3对象存储)读取或写入文件。当读取时,S3netCDF4将整个netCDF文件或整个 子数组 文件流式传输到内存中。当写入时,它将在内存中创建整个netCDF文件或 子数组 文件,并在关闭文件时将其写入远程存储。 -
resource_allocation: disk_handles
:S3netCDF4在任何时候允许打开的磁盘文件数量。这适用于将文件写入磁盘。S3netCDF4使用底层的netCDF4库读取和写入磁盘文件,但它会跟踪打开的文件数量。
请注意,S3netCDF4允许对CFA-netCDF文件的master-array和sub-array文件的位置有完全的灵活性。它允许两者都存储在磁盘或S3存储上。例如,出于性能原因,master-array文件可以存储在磁盘上,而sub-array文件存储在S3上。
或者,sub-array文件的第一时间步也可以存储在磁盘上,以便用户能够快速执行测试分析。
文件洗牌过程由内部文件管理器执行,该管理器记录在任何时候打开的文件或过去打开的文件以及它们最后访问的时间。用户看不到任何这种交互,他们只是与S3Dataset、S3Group、S3Variable和S3Dimension对象交互。
- 当文件最初打开时,会记录模式以及文件是否在磁盘或远程存储上。它们被标记为“OPEN_NEW”,然后在成功打开后标记为“OPEN_EXISTS”。
- 对于从远程存储读取,文件被流式传输到内存中,然后从读取的数据创建一个netCDF数据集。
- 对于写入远程存储,netCDF数据集在内存中创建。
- 对于从磁盘读取,使用底层netCDF4库打开文件,并返回netCDF数据集。
- 对于写入磁盘,使用netCDF4库创建文件,并返回数据集。
- 如果文件再次被访问(例如,通过切片运算符),则返回netCDF数据集。文件管理器知道这些文件已经打开或存在于内存中,因为它们被标记为“OPEN_EXISTS”。
- 步骤1和2会继续,直到使用的内存量超过
resource_allocation: memory
或打开的文件数量超过resource_allocation: disk_handles
。 - 如果使用的内存量超过
resource_allocation: memory
- 确定(读取)或计算(写入)下一个文件的大小。
关闭文件,并使用Python垃圾收集器释放它们占用的内存,直到有足够的内存读取或创建下一个文件。 - 以“写入”模式打开的文件被关闭,标记为“KNOWN_EXISTS”,并写入远程存储(S3)或磁盘。
- 以“读取”模式打开的文件只是简单地关闭,并从文件管理器中删除它们的条目。
- 关闭文件的优先级是先关闭最后访问的文件。文件管理器会记录每个文件最后访问的时间。
- 如果以“写入”模式再次访问文件,并且文件在文件管理器中被标记为“KNOWN_EXISTS”,则它以“追加”模式打开。这样,文件可以创建、在内存中洗牌,并仍然写入,以便最终结果是如果它在整个操作过程中都在内存中,结果相同。
- 如果打开的文件数量超过
resource_allocation: disk_handles
- 遵循第4点的程序,除了不是关闭文件直到有足够的内存可用,而是关闭文件直到有足够的文件句柄。
- 文件被标记为“KNOWN_EXISTS”,如第4点所述。
这种文件洗牌过程是S3netCDF4性能的基本要素,因为它最大限度地减少了文件从远程存储流式传输或从磁盘打开的次数。文件管理器也有优化,例如,如果一个文件已被写入然后读取,它将使用内存中的副本进行所有操作,而不是保留两个副本,或反复从远程存储中流式传输。
写入文件
S3netCDF4具有将netCDF3、netCDF4、CFA-netCDF3和CFA-netCDF4文件写入POSIX文件系统、Amazon S3对象存储(或公共云)或OPeNDAP的能力。文件以与标准netCDF4-python包相同的方式创建,通过创建一个s3Dataset
对象。然而,s3Dataset
构造函数的参数有两种变化方式
filename
可以是一个S3端点,即它以s3://
开头- 除了netCDF4-python允许的格式外,
format
关键字还可以是CFA3
,以创建一个CFA-netCDF3数据集,或CFA4
,以创建一个CFA-netCDF4数据集。 - 如果创建一个
CFA3
或CFA4
数据集,则可以设置一个可选的关键字参数:cfa_version
。这可以是"0.4"
或"0.5"
。请参阅下面的CFA-netCDF文件部分。
from S3netCDF4._s3netCDF4 import s3Dataset as Dataset
test_dataset = Dataset("/Users/neil/test_dataset_nc4.nc", 'w',
format='NETCDF4')
示例 3:使用 CFA 版本 0.5(默认)在文件系统中创建 CFA-netCDF4 文件
from S3netCDF4._s3netCDF4 import s3Dataset as Dataset
cfa_dataset = Dataset("/Users/neil/test_dataset_cfa4.nc", 'w',format='CFA4')
示例 4:在 S3 存储上创建 CFA 版本 0.4 的 CFA-netCDF3 文件
from S3netCDF4._s3netCDF4 import s3Dataset as Dataset
cfa_dataset = Dataset("s3://tenancy-0/test_bucket/test_dataset_s3_cfa3.nc",
'w', format='CFA3', cfa_version="0.4")
CFA-netCDF文件
在创建文件时选择 format="CFA3"
或 format="CFA4"
将创建一个符合 CFA 规范的 netCDF 文件。这包括一个 主数组 文件和多个 子数组 文件。还可以指定使用的 CFA 版本,即 cfa_version="0.4"
或 cfa_version="0.5"
。 "0.4"
遵循 CFA 规范,其中 子数组 元数据写入 netCDF 变量的属性中。"0.5"
将 子数组 元数据重构为主数组文件中的额外组和变量。"0.5"
是首选格式,因为它更节省内存,依赖于 netCDF 切片和文件的局部读取,并且更快,因为首次读取主数组文件时不需要解析。由于它使用 netCDF4 的功能,因此 cfa_version="0.5"
只与 format="CFA4"
兼容。
请注意,cfa_version="0.5"
和 format="CFA3"
不兼容,因为 NETCDF3 不允许使用组。
主数组 文件包含
- 维度定义
- 维度变量
- 标量变量定义:不参考其跨越的域的变量定义
- 变量元数据
- 全局元数据
- 它不包含任何字段数据,但它 确实 包含维度变量的数据,因此每个变量的域。
- 主数组 文件可能包含一个字段变量或多个字段变量。
子数组 文件包含 主数组 中单个变量的子域。它们包含
- 子域的维度定义
- 子域的维度变量
- 一个完整的变量定义,包括对维度的引用
- 变量的元数据
因此,每个 子数组 文件都是一个自描述的 netCDF 文件。如果 主数组 文件丢失,可以从 子数组 文件中重建它。
在 CFA v0.4 中,变量元数据(netCDF 属性)在 主数组 文件中的每个变量中包含一个 分区矩阵。该分区矩阵包含有关如何从相关的 子数组 中重建 主数组 变量的信息,因此也包含读取或写入 主数组 变量切片的必要信息。
在 CFA v0.5 中,分区矩阵 存储在一个组中。此组具有与变量相同的名称,但前缀为 cfa_
。该组包含维度和变量,用于存储 分区矩阵 和 分区 的信息。CFA v0.5 的完整文档将随后提供。
分区矩阵 包含
- 分区矩阵 作用的 netCDF 文件中的维度(例如,
["time", "latitude", "longitude"]
) - 分区矩阵 的形状(例如,
[4,2,2]
) - 分区列表
分区矩阵 中的每个 分区 包含
- 分区在分区矩阵中的索引 - 维度的数量的长度列表(例如,
[3, 1, 0]
) - 分区在 主数组 中的位置 - 长度等于维数的列表,每对给出该维度的 主数组 索引范围(例如,
[[0, 10], [20, 40], [0, 45]]
) - 子数组 的定义
- 子数组的路径或URI。这可能位于文件系统、OPeNDAP文件或S3 URI上。
- 子数组文件中netCDF变量的名称
- 文件的格式(对于S3netCDF4始终为
netCDF
) - 变量的形状,即子域在每个维度上的长度
有关更多信息,请参阅[CFA约定0.4网站](http:// www.met.reading.ac.uk/~david/cfa/0.4/)。在S3netCDF4源代码中的_CFAClasses.pyx
文件头中也有一个有用的摘要。CFA的"0.5"
版本文档将随后提供。
请注意,分区矩阵中的索引从零开始索引,但索引对于主数组中分区位置是包含的。
这与Python不同,Python中的索引是非包含的。两种索引方法之间的转换由_CFAnetCDFParser实现处理,因此用户无需担心索引转换
创建维度和变量
在netCDF或CFA-netCDF4数据集中创建维度和变量遵循与在标准netCDF4-python库中创建变量相同的方法
from S3netCDF4._s3netCDF4 import s3Dataset as Dataset
cfa_dataset = Dataset("s3://minio/test_bucket/test_dataset_s3_cfa3.nc", 'w',
format='CFA3')
timed = cfa_dataset.createDimension("time", None)
times = cfa_dataset.createVariable("time", "f4", ("time",))
在创建变量时,会根据文件名路径(S3
、文件系统或OPeNDAP)和格式(CFA3
和CFA4
或NETCDF4
和NETCDF3_CLASSIC
)的组合出现多种不同的工作流程。这些工作流程可以总结如下
-
format=NETCDF4
或format=NETCDF3_CLASSIC
。这两个选项将创建一个标准的netCDF文件。- 如果文件名位于远程系统上,(即它包含
s3://
),则netCDF文件将在内存中创建,并在调用s3Dataset.close()
或文件“洗牌”出内存时上传(PUT)到S3文件系统。(有关更多详细信息,请参阅资源使用)。 - 如果文件名不包含
s3://
,则netCDF文件将写入文件系统或OPeNDAP,其行为遵循标准netCDF4-python库。
- 如果文件名位于远程系统上,(即它包含
-
format=CFA3
或format=CFA4
。这两个选项将创建一个CFA-netCDF文件
。- 最初只创建并写入
master-array
文件。当向master-array
变量写入数据时,将创建并写入sub-array
文件。 - 创建变量时,会提供维度,这允许生成
partition matrix
元数据- 文件分割算法确定如何将变量分割成
sub-array
,或者用户可以提供sub-array
的形状 - 根据这些信息创建
partition matrix
的形状和partition matrix
维度列表。partition matrix
由一个netCDF数据集内部表示,这也被创建。
- 文件分割算法确定如何将变量分割成
- 只有当通过变量上的切片操作写入变量时,每个单独的
partition
才会写入到partition matrix
中。sub-array
文件被创建,对于远程文件系统(S3)是在内存中,对于本地文件系统(POSIX)是在磁盘上。sub-array
的文件名通过程序确定。- 每个
sub-array
在master-array
中的位置(及其形状)由切片确定,以及由文件分割算法或用户提供的sub-array
形状确定。 - 将单个
partition
信息写入到partition-matrix
- 字段数据写入到
sub-array
文件。 - 在后续对同一
sub-array
的切片中,使用partition
信息,而不是重新写入。
- 当
master-array
文件关闭时(用户调用s3Dataset.close()
)partition matrix
元数据被写入到master-array
- 如果文件位于远程文件系统(S3)上,则它们目前仅在内存中存在(除非它们已被“洗牌”到存储)。它们现在是关闭的(在内存中),然后上传到远程存储。
任何附加的文件也会上传到远程存储。 - 如果文件不在远程文件系统上,则先关闭文件,然后依次关闭子数组文件,最后关闭主数组文件。
- 最初只创建并写入
CFA文件的文件名和文件层次结构
如上所述,CFA文件实际上由一个单一的主数组文件和许多子数组文件组成。这些子数组文件通过分区矩阵中的文件路径或URI来引用。为了便于将子数组文件与主数组文件关联起来,使用了一种命名约定和文件结构。
-
CFA约定规定,CFA-netCDF文件的文件扩展名应为
.nca
。 -
在主数组文件相同目录/相同根URI下创建一个目录。此目录与不带
.nca
扩展名的主数组文件具有相同名称。 -
在此目录中包含所有子数组文件。这些子数组文件遵循以下命名约定
<主数组文件名>.<变量名>.[<分区矩阵中的位置>].nc
主数组文件示例 a7tzga.pdl4feb.nca
├── a7tzga.pdl4feb.nca
├── a7tzga.pdl4feb
│ ├── a7tzga.pdl4feb.field16.0.nc
│ ├── a7tzga.pdl4feb.field16.1.nc
│ ├── a7tzga.pdl4feb.field186.0.nc
│ ├── a7tzga.pdl4feb.field186.1.nc
│ ├── a7tzga.pdl4feb.field1.0.0.nc
│ ├── a7tzga.pdl4feb.field1.0.1.nc
│ ├── a7tzga.pdl4feb.field1.1.0.nc
│ ├── a7tzga.pdl4feb.field1.1.1.nc
在S3存储系统上,主数组目录将形成子数组对象的前缀的一部分,因为在S3存储系统中,目录在字面上不存在,只有前缀。
注意,主数组文件中的元数据通知S3netCDF4子数组文件的位置。上述文件结构定义了默认行为,但S3netCDF4的规范允许子数组文件位于任何位置,无论是S3、POSIX磁盘还是OpenDAP。
编写元数据
可以像标准netCDF4库一样,通过在变量或Dataset(全局元数据)对象上创建成员变量来将元数据写入变量和Dataset。
from S3netCDF4._s3netCDF4 import s3Dataset as Dataset
with Dataset("/Users/neil/test_dataset_cfa3.nca", mode='w', diskless=True,
format="CFA3") as s3_data:
# create the dimensions
latd = s3_data.createDimension("lat", 196)
lond = s3_data.createDimension("lon", 256)
# create the dimension variables
latitudes = s3_data.createVariable("lat", "f4", ("lat",))
longitudes = s3_data.createVariable("lon", "f4", ("lon",))
# create the field variable
temp = s3_data.createVariable("tmp", "f4", ("lat", "lon"))
# add some attributes - variable metadata
s3_data.source = "s3netCDF4 python module tutorial"
s3_data.units = "degrees C"
latitudes.units = "degrees north"
longitudes.units = "degrees east"
# add some global metadata
temp.author = "Neil Massey"
写入字段数据
对于具有format=NETCDF3_CLASSIC
或format=NETCDF4
格式的netCDF文件,当在s3Dataset
对象上调用createVariable
时,会创建变量并将字段数据写入文件(作为缺失值)。当调用[]
运算符(即切片数组)时,将数据写入变量和文件。这与netCDF4-python的行为相同。如果指定了S3 URI(文件路径以s3://
开头),则文件首先在内存中创建,然后在关闭文件时流式传输到S3。
对于在s3Dataset
构造函数中指定了format=CFA3
或format=CFA4
的netCDF文件,只有当在s3Dataset
对象上调用createDimension
、createVariable
等时,才会写入主数组文件。当调用createVariable
时,会创建一个标量字段变量(即没有维度的变量),计算分区矩阵(参见文件拆分算法)并将其写入标量字段变量。只有当在从s3Dataset.createVariable
方法返回的Variable
对象上调用[]
运算符时,才会创建子数组文件。此运算符在S3netCDF中作为s3Variable
类的__setitem__
成员函数实现,并对应于切片数组。
通过__setitem__
将字段数据的切片写入主数组文件包括五个操作
-
确定哪些子数组与切片重叠。这目前是通过超立方体重叠方法完成的,即可以通过将维度索引除以分区矩阵中该维度的长度来确定子数组的位置。这假设子数组的大小是均匀的(按维度)。
-
如果子数组文件的大小将导致当前使用的内存量超过
~/.s3nc.json
中的resource_allocation: memory
设置,那么一些文件可能被移出内存。请参阅上面的资源使用部分。这可能会导致一些文件被写入远程存储,这意味着它们在下次写入时将以追加模式打开。如果在文件交换后,子数组的大小仍然不能包含在内存中,则会出现内存错误。 -
根据分区信息中的文件路径或URI打开或创建子数组文件。如果指定了S3 URI(文件路径以
s3://
开头),则文件在内存中打开或创建,并在对s3Dataset
调用.close()
时上传。文件将以创建模式(w
)打开。 -
为子数组文件创建维度和变量,并写入元数据。
-
计算源切片和目标切片。这计算了在主数组中每个索引与每个子数组之间的映射。由于允许用户为主数组选择任何切片,因此必须正确地将此转换为子数组索引。
-
将源切片的数据复制到目标切片。
对于具有S3 URI的文件,在调用s3Dataset
上的.close()
时执行上传到S3对象存储。
字段数据的部分写入
当s3Dataset处于“写入”模式并且用户将切片到被该分区覆盖的主数组的部分时,才将分区信息写入分区矩阵。因此,只有在将分区写入分区矩阵时,才会创建子数组文件。
这导致分区矩阵的大部分可能包含未定义的数据,并且可能存在大量不存在的子数组文件。
这使得s3netCDF4非常适合稀疏数据,因为可以优化子数组的大小,以便稀疏数据占据最小的空间。
如果在“读取”模式下,用户指定了一个包含未定义子数组的切片,则返回主数组的子域的缺失值
(_FillValue
)。
文件分割算法
要分割主数组到其构成子数组,使用将大netCDF文件分割成小netCDF文件的方法。高级算法如下
-
将字段变量分割,使每个文件有一个字段变量。
netCDF允许单个文件中有多个字段变量,因此这是一种明显且简单的方法来分区文件。请注意,这仅分割字段变量,所有维度变量都保留在主数组文件中。 -
对于每个字段变量文件,沿
时间
、层级
、纬度
或经度
维度进行分割。请注意,在netCDF文件中,维度的顺序是任意的,例如顺序可以是[时间,层级,纬度,经度]
或[经度,纬度,层级,时间]
,甚至是[纬度,时间,经度,层级]
。
S3netCDF4使用每个维度变量的元数据和名称来确定维度的顺序,以便可以正确地分割它们。请注意,任何其他维度(集合
或实验
)的长度始终为1,即维度将分割成与其长度相等的字段数。
对象的(子数组文件)最大大小可以作为关键字参数传递给s3Dataset.createVariable
或s3Group.createVariable
:max_subarray_size=
。如果没有提供max_subarray_size
关键字,则默认为50MB。为了在不超过此最大大小约束的同时确定time
、latitude
或longitude
维度的最佳分割数,考虑了以下两种情况
- 用户希望读取单个纬度-经度数据点的所有时间步。
- 用户希望读取单个时间步中所有纬度-经度数据点。
对于情况1,最佳解决方案是将主数组分割成长度为1的longitude
和latitude
维度的子数组,以及等于时间维度的步数长度的子数组。对于情况2,最佳解决方案是不分割longitude
和latitude
维度,但将每个时间步分割,使时间维度的长度为1。然而,这两种情况对于另一种情况都是最坏的情况。
在保持在max_subarray_size
约束的同时平衡执行这两种用例所需的操作数量,导致了一个优化问题,其中以下两个等式必须平衡
- 用例1 = nT / dT
- 用例2 = nlat / dlat × nlon / dlon
其中nT是time
维度的长度,dT是沿time
维度的分割数。nlat是latitude
维度的长度,dlat是沿latitude
维度的分割数。nlon是longitude
维度的长度,dlon是沿longitude dimension
的分割数。
使用以下算法
- 计算当前对象大小 Os = nT / dT × nlat / dlat × nlon / dlon
- while Os >
max_subarray_size
,分割一个维度- if dlat × dlon <= dT
- if dlat <= dlon:再次分割纬度维度:dlat += 1
- else:再次分割经度维度:dlon += 1
- else:再次分割时间维度:dT += 1
- if dlat × dlon <= dT
使用这种简单的分而治之算法可以确保满足max_subarray_size
约束,并且这两种用例需要相等数量的操作。
请注意,在S3netCDF4的2.0版本中,用户可以在s3Dataset.createVariable方法中指定子数组形状。这绕过了文件分割算法,并使用用户指定的子数组形状。
读取文件
S3netCDF4具有从POSIX文件系统、Amazon S3对象存储和OPeNDAP读取正常netCDF4和netCDF3文件、CFA-netCDF4和CFA-netCDF3文件的能力。
对于远程存储上的文件,在读取文件之前,S3netCDF4将查询文件大小,并确定它是否大于~/.s3nc.json
配置中的resource_allocation: memory
设置或大于当前可用内存。如果是,则将一些文件“洗牌”出内存,直到有足够的分配内存。有关详细信息,请参阅资源使用。如果它小于resource_allocation: memory
设置,则将直接将文件流式传输到内存。本地磁盘(POSIX)上的文件将以与标准netCDF4库相同的方式进行打开,即读取头信息、变量、维度信息和元数据,但不读取字段数据。
从用户的角度来看,通过创建一个s3Dataset
对象以与标准netCDF4-python包相同的方式读取文件。与写入文件一样,s3Dataset
构造函数的参数可以以多种方式变化。
- 文件名
filename
可以是S3端点,即以s3://
开头,也可以是磁盘上的文件,或者OpenDAP URL。 格式
可以是CFA3
或CFA4
,用于读取CFA-netCDF3
或CFA-netCDF4
数据集。但是,如果用户想读取CFA文件,则不需要指定此关键字,因为S3netCDF4将从元数据中确定文件是常规的netCDF文件还是CFA-netCDF文件。
S3netCDF4还将从文件头中确定netCDF文件是netCDF4还是netCDF3文件。如果文件位于S3存储系统上,则仅读取文件的前6个字节,以确定文件是netCDF4、netCDF3文件还是无效文件。由于CFA-netCDF文件只是netCDF文件,因此确定是否为CFA-netCDF文件将在读取文件后进行,即在解析头之后。- 远程存储上的文件将流式传输到内存中。在读取文件时,如果当前使用的内存超过
~/.s3nc.json
配置文件中resource_allocation: memory
设置,则其他文件可能会被“淘汰”出内存。有关更多信息,请参阅资源使用。
from S3netCDF4._s3netCDF4 import s3Dataset as Dataset
with Dataset("/Users/neil/test_dataset_nc4.nc", 'r') as nc_data:
print(nc_data.variables)
from S3netCDF4._s3netCDF4 import s3Dataset as Dataset
from S3netCDF4 import s3Dataset as Dataset
with Dataset("s3://tenancy-0/test_bucket/test_dataset_s3_cfa3.nc", 'r') as nc_data:
print(nc_data.variables)
在读取CFA-netCDF文件时,主数组文件被解析以将文件中的元数据(对于CFA "v0.4"
)或变量的CFA组信息(对于CFA "v0.5"
)转换为分区矩阵
。有关更多信息,请参阅CFA-netCDF文件。
这种转换的一部分涉及到为CFA-netCDF文件中的每个变量创建一个s3Variable
类的实例。该s3Variable
类包含以下内容:_nc_var
:标准netCDF4.Variable
对象的实例;_cfa_var
:包含与该变量关联的CFA 子数组信息的CFAVariable
实例;_cfa
:包含包含此变量的CFA 主数组文件信息的CFADataset
实例。通过解析主数组文件中的元数据或CFA组,生成这两个对象。这两个对象将在用户在s3Variable对象上调用切片操作时使用。
读取变量
在v2.0.x版本中,s3netCDF4 API现在在读取变量名称和变量方面与标准netCDF4 python API相匹配。以前,使用了两个额外函数(variables()
和getVariable()
)。在重构过程中,找到了提供100%兼容netCDF4 python API的方法。这在处理变量的方法中得到了反映。
s3Dataset.variables
或s3Group.variables
:返回数据集中的变量列表。s3Dataset.variables[<variable_name>]
或s3Group.variables[<variable_name>]
:如果变量是CFA-netCDF文件中的master-array
,则返回<variable_name>
的s3netCDF4.s3Variable
实例;如果是维度变量或标准netCDF文件中的变量,则返回netCDF4.Variable
实例。
示例9:从磁盘读取netCDF文件并获取"field8"变量
from S3netCDF4 import s3Dataset as Dataset
with Dataset("/Users/neil/test_dataset_nc4.nc") as src_file:
print(src_file.variables)
src_var = src_file.variables["field8"]
print(type(src_var))
读取元数据
从变量或数据集(全局元数据)读取元数据的方式与在标准netCDF4 python包中完全相同,即通过查询变量或数据集的成员变量。还支持Dataset
和Variable
类的ncattrs
和getncattr
成员函数。
from S3netCDF4 import s3Dataset as Dataset
with Dataset("/Users/neil/test_dataset_nc4.nc") as src_file:
print(src_file.ncattrs())
src_var = src_file.getVariable["field8"]
print(src_var.ncattrs())
print(src_var.units)
print(src_var.getncattr("units"))
print(src_file.author)
print(src_file.getncattr("author"))
读取字段数据
S3netCDF读取字段数据遵循与写入数据相同的原则
- 如果文件被确定具有
format=NETCDF3_CLASSIC
或format=NETCDF4
格式,则将其读取,字段数据以与标准 netCDF4-python 包相同的方式进行提供。如果文件位于 S3 存储上,则整个文件将流式传输到内存,如果它比~/ .s3nc.json
中的resource_allocation: memory
设置更大,或者比可用内存更大,则将返回内存错误。 - 如果文件被确定具有
format=CFA3
或format=CFA4
格式,则只读取 主数组 文件,任何字段数据只有在调用s3Variable
实例上的[]
操作符 (__getitem__
) 时才会读取。
在打开 主数组 文件时 - 如果文件是 CFA 规范的
"v0.4"
版本,则从变量元数据中获取 CFA 元数据。内部将 分区矩阵 构建为一个包含维度和包含分区信息的变量 netCDF 组(参见 文件分割算法)。 - 如果文件是 CFA 的
"v0.5"
版本,则直接从文件的组、维度和变量中读取 分区矩阵,无需进行任何解析。 - 创建
_cfa
、_cfa_grp
、_cfa_dim
和_cfa_var
对象,分别作为s3Dataset
、s3Group
、s3Dimension
和s3Variable
对象的成员变量。这些是CFADataset
、CFAGroup
、CFADimension
和CFAVariable
的实例。分区矩阵包含在CFAVariable
的_cfa_var
实例的netCDF4.Group
中。
内部,分区矩阵由一个包含分区矩阵维度和包含分区信息变量的 netCDF 组组成。在 s3Dataset、s3Variable 和 s3Group 对象中,存在包含更高层次 CFA 数据的对象以及操作该数据的方法。当用户切割字段数据以确定读取哪些 子数组 文件以及哪些 子数组 文件的部分包含在切片中时,使用此信息。
- 作为顶层容器的
CFADataset
- 多个
CFAGroup
:文件中组的信息。始终至少有一个组:根组在CFADataset
中的表示是明确的。在CFAGroup
中包含- 多个
CFADim
:数据集中维度的信息 - 多个
CFAVariable
:数据集中变量的信息,其中包含-
包含以下内容的 分区矩阵:一个包含
- 标量维度,没有单位或相关维度变量
- 包含分区信息的变量
pmshape
:分区矩阵的形状pmdimensions
:分区矩阵作用的 主数组 文件中的维度index
:分区矩阵中的索引。这由分区矩阵中的位置隐含,但保留以检测切片算法的错误查找。location
:在 主数组文件 中的位置ncvar
:子数组 文件中的变量名。file
:子数组 文件的 URL 或路径。format
:子数组 文件的格式。shape
:子数组 文件的形状。
-
操作变量及其 分区矩阵 的方法,包括
__getitem__
:返回读取和写入 子数组 文件所需的信息。getPartition
:返回分区(分区矩阵中的单个元素)的用户可读版本,而不是 netCDF 组或变量。
-
- 多个
- 多个
通过 getitem 从 主数组 文件中的变量读取字段数据的切片包括五个操作
-
如果请求的切片总大小大于
resource_allocation: memory
(或可用内存),则在~/.s3nc.json
配置文件中由cache_location:
设置指示的位置创建一个 Numpy 内存映射数组。 -
通过查询 分区矩阵 确定哪个 子数组 与切片重叠。这目前是通过依赖于所有 分区 都具有相同大小的简单算术运算来完成的。
-
计算源切片和目标切片,源是 子数组,目标是形状等于用户提供的切片的内存映射 Numpy 数组(主数组)。此 子数组 在 主数组 中的位置由包含 子数组 的 分区 给出,从而给出对 主数组 的切片。然而,如果用户提供的切片不包含整个 子数组,例如如果取一系列时间步长,则可能需要更改 子数组 和 主数组 的切片。
-
对于每个 子数组,都会打开分区信息中由
file
变量指定的文件。如果文件在磁盘上,它将以与标准 netCDF4 python 文件相同的方式打开。如果它在远程文件系统上,例如 S3,则将其流式传输到内存中。如果 子数组 文件的大小会导致当前使用的内存量超过~/.s3nc.json
中的resource_allocation: memory
设置,则某些文件可能会从内存中移除。请参阅上面的 资源使用 部分。如果在文件移动后,子数组 的大小仍然无法包含在内存中,则将发生内存错误。 -
从下载的文件或流式传输的内存中打开 netCDF4-python
Dataset
对象。 -
使用源(子数组)切片和目标(主数组)切片将 子数组 中的值复制到 主数组(内存映射的 Numpy 数组)。
目前,数据读取是异步执行的,使用 aiobotocore。S3netCDF4 允许使用存储在 CFADataset、CFAGroup、CFADimension 和 CFAVariable 类中的 CFA 信息并行工作流,使用多进程或 Dask。以下将提供示例。
示例列表
项目详情
S3netCDF4-2.1rc1.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 47a322cc8fc7883e79537653103a5faeedc92ce946cb69e9b924a5834b11f893 |
|
MD5 | e3aa1bbe6f4a9fd286ac953c1271e097 |
|
BLAKE2b-256 | 3d222cdd83d398396a6780685bf24d4519c3da5dbd6b48606a4a588ba6222efe |
S3netCDF4-2.1rc1-cp39-cp39-macosx_10_15_x86_64.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | a251b7f7eedf4344759bd383409838fb06be9ffecf059ff359b45bc1ac7ebf4e |
|
MD5 | 2e9027d67bfda4c31c0dce33c263a74e |
|
BLAKE2b-256 | fb27220f3eb896d7278055373ac12060d39f5e2039994edac78b09c2bbf5015a |