likes
comments
collection
share

像访问本地文件系统一样访问云存储

作者站长头像
站长
· 阅读数 3

微信公众号:测度空间

1. 什么是Zarr?

Zarr官方网站将Zarr描述为一个提供了分块(chunked),压缩(compressed),N维数组功能的Python包。 Chunked表示Zarr可以处理非常大的数据集和快速数据访问。compressed意味着Zarr可以使用合理的文件大小来保存文件,这也意味着更低的成本。 N维数组功能说明Zarr可以像Netcdf一样处理多维数据集,比如,有时间,x,y和z四维的地球科学类数据集。

Zarr的一些亮点如下:

  • 可以使用NumPy创建N维数组。
  • 沿任何维度的块阵列。
  • 使用任何NumCodecs编解码器压缩和/或过滤块阵列。
  • 将数组存储在内存中,磁盘上,Zip文件中,云存储上(例如AWS S3),...
  • 从多个线程或进程并发读取数组。
  • 从多个线程或进程并发写入数组。
  • 通过组将数组组织到层次结构中。

Zarr最关键的组件是它可以让你您的本地文件系统一样读取和写入文件到云存储系统(例如AWS S3),而且保持了Netcdf的数据组织格式。

2. 读取netcdf文件

在这里,我们将使用NCEP Reanalysis Dataset的地面气温数据作为示例。我首先将2019年地表气温文件air.sig995.2019.nc下载到本地电脑上。然后,利用xarray读取文件中的数据。

import xarray as xr

ds = xr.open_dataset('air.sig995.2019.nc')
ds
    <xarray.Dataset>
    Dimensions:    (lat: 73, lon: 144, nbnds: 2, time: 116)
    Coordinates:
      * lat        (lat) float32 90.0 87.5 85.0 82.5 ... -82.5 -85.0 -87.5 -90.0
      * lon        (lon) float32 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
      * time       (time) datetime64[ns] 2019-01-01 2019-01-02 ... 2019-04-26
    Dimensions without coordinates: nbnds
    Data variables:
        air        (time, lat, lon) float32 ...
        time_bnds  (time, nbnds) float64 ...
    Attributes:
        Conventions:    COARDS
        title:          mean daily NMC reanalysis (2014)
        history:        created 2017/12 by Hoop (netCDF2.3)
        description:    Data is from NMC initialized reanalysis\n(4x/day).  These...
        platform:       Model
        References:     http://www.esrl.noaa.gov/psd/data/gridded/data.ncep.reana...
        dataset_title:  NCEP-NCAR Reanalysis 1

ds.air
    <xarray.DataArray 'air' (time: 116, lat: 73, lon: 144)>
    [1219392 values with dtype=float32]
    Coordinates:
      * lat      (lat) float32 90.0 87.5 85.0 82.5 80.0 ... -82.5 -85.0 -87.5 -90.0
      * lon      (lon) float32 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
      * time     (time) datetime64[ns] 2019-01-01 2019-01-02 ... 2019-04-26
    Attributes:
        long_name:     mean Daily Air temperature at sigma level 995
        units:         degK
        precision:     2
        GRIB_id:       11
        GRIB_name:     TMP
        var_desc:      Air temperature
        dataset:       NCEP Reanalysis Daily Averages
        level_desc:    Surface
        statistic:     Mean
        parent_stat:   Individual Obs
        valid_range:   [185.16 331.16]
        actual_range:  [198.4 314. ]

从上面的结果,我们可以看到air变量是平均日气温,单位为degK。 这个变量有三个维度-时间,经度(lon)和纬度(lat)。

3. 将数据保存为Zarr格式

现在我们将把上面的数据保存为Zarr格式。 因为我没有AWS帐户,所以我要将它保存到我的笔记本电脑中。 请注意,如果你有AWS账户的话,你可以在s3fs包的帮助下直接将其保存到AWS S3上。 s3fs是S3的Python文件接口,它构建于boto3之上,boto3是适用于Python的Amazon Web Services(AWS)SDK。

import zarr
import s3fs


# Compare the data if needed
compressor = zarr.Blosc(cname='zstd', clevel=3)
encoding = {vname: {'compressor': compressor} for vname in ds.data_vars}
# Save to zarr
ds.to_zarr(store='zarr_example', encoding=encoding, consolidated=True)
<xarray.backends.zarr.ZarrStore at 0x31a4d1ef0>

现在,我们已将数据保存为本地zarr文件。

以下代码可用于将数据保存到AWS S3 zarr格式。

import zarr
import s3fs

# AWS S3 path
s3_path = 's3://your_data_path/zarr_example'
# Initilize the S3 file system
s3 = s3fs.S3FileSystem()
store = s3fs.S3Map(root=s3_path, s3=s3, check=False)
# Compare the data if needed
compressor = zarr.Blosc(cname='zstd', clelve=3)
encoding = {vname: {'compressor': compressor} for vname in ds.data_vars}
# Save to zarr
ds.to_zarr(store=store, encoding=encoding, consolidated=True)

4. 读取Zarr文件

读取Zarr文件也很容易。 你可以直接从云存储系统(例如AWS S3)读取zarr文件,这对于地球科学类的数据尤为重要。 用Zarr我们可以直接访问整个或部分数据集。 想象一下,所有天气气候模型和卫星的数据(例如NCEP再分析数据)都保存在云存储上(这可能是数千TB或PB),我们可以轻轻松松使用几行代码直接从AWS S3读取和下载文件,而无需经常经理那些痛苦、耗时的数据下载过程。这会让我们的生活变得多么美好!

因为我没有AWS S3账户,我将使用本地zarr文件作为示例。

# Read Zarr file
zarr_ds = xr.open_zarr(store='zarr_example', consolidated=True)
zarr_ds
<xarray.Dataset>
Dimensions:    (lat: 73, lon: 144, nbnds: 2, time: 116)
Coordinates:
  * lat        (lat) float32 90.0 87.5 85.0 82.5 ... -82.5 -85.0 -87.5 -90.0
  * lon        (lon) float32 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
  * time       (time) datetime64[ns] 2019-01-01 2019-01-02 ... 2019-04-26
Dimensions without coordinates: nbnds
Data variables:
    air        (time, lat, lon) float32 dask.array<shape=(116, 73, 144), chunksize=(58, 37, 72)>
    time_bnds  (time, nbnds) float64 dask.array<shape=(116, 2), chunksize=(116, 2)>
Attributes:
    Conventions:    COARDS
    References:     http://www.esrl.noaa.gov/psd/data/gridded/data.ncep.reana...
    dataset_title:  NCEP-NCAR Reanalysis 1
    description:    Data is from NMC initialized reanalysis\n(4x/day).  These...
    history:        created 2017/12 by Hoop (netCDF2.3)
    platform:       Model
    title:          mean daily NMC reanalysis (2014)

很简单吧? 实际上,此处zarr只读取了数据文件的元数据而不是加载了所有真实的数据。当数据量很大时,这个功能非常有用。因为通常情况下,我们并不会用到所有的数据,用到的只是数据集中的一部分。例如,我们可以只读取2019年1月的气温数据。

import pandas as pd

# We'd like the read data in Januray 2019
time_period = pd.date_range('2019-01-01', '2019-01-31')
# Select part of the zarr data
zarr_Jan = zarr_ds.sel(time=time_period)
zarr_Jan
<xarray.Dataset>
Dimensions:    (lat: 73, lon: 144, nbnds: 2, time: 31)
Coordinates:
  * lat        (lat) float32 90.0 87.5 85.0 82.5 ... -82.5 -85.0 -87.5 -90.0
  * lon        (lon) float32 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
  * time       (time) datetime64[ns] 2019-01-01 2019-01-02 ... 2019-01-31
Dimensions without coordinates: nbnds
Data variables:
    air        (time, lat, lon) float32 dask.array<shape=(31, 73, 144), chunksize=(31, 37, 72)>
    time_bnds  (time, nbnds) float64 dask.array<shape=(31, 2), chunksize=(31, 2)>
Attributes:
    Conventions:    COARDS
    References:     http://www.esrl.noaa.gov/psd/data/gridded/data.ncep.reana...
    dataset_title:  NCEP-NCAR Reanalysis 1
    description:    Data is from NMC initialized reanalysis\n(4x/day).  These...
    history:        created 2017/12 by Hoop (netCDF2.3)
    platform:       Model
    title:          mean daily NMC reanalysis (2014)

以上我们仅选取了2019年一月的数据。简单吧?

以下代码可用于访问AWS S3 zarr文件。

# AWS S3 path
s3_path = 's3://your_data_path/zarr_example'
# Initilize the S3 file system
s3 = s3fs.S3FileSystem()
sotre = s3fs.S3Map(root=s3_path, s3=s3, check=False)
# Read Zarr file
ds = xr.open_zarr(store=store, consolidated=True)

5. 快速访问的秘诀:"consolidated = True"

一旦zarr数据是固定下来并且可以被视为只读,我们就可以通过参数consolidated = True将许多元数据对象合并为单个对象。这样做就可以大大提高读取数据集元数据的速度,让读取数据变得非常快捷!

6. 总结

利用Zarr,我们可以轻松地将文件读取和写入云存储系统(例如AWS S3),这对使用云存储系统存储和访问大数据非常有用。 Zarr的compressedconsolidated功能还可以帮助我们节省存储成本并提高数据访问速度。云端N维数据的读写是一个非常热门的话题。 除了Zarr之外,现在还有其他一些包。 不过到目前为止,Zarr是这方面的领先者。