前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >xarray | 序列化及输入输出

xarray | 序列化及输入输出

作者头像
bugsuse
发布2020-04-21 17:22:52
6.1K0
发布2020-04-21 17:22:52
举报
文章被收录于专栏:气象杂货铺气象杂货铺

xarray 支持多种文件格式(从 pickle文件到 netCDF格式文件)的序列化和输入输出。

Pickle

序列化 xarray 数组最简单的方法就是利用 python 内置的 pickle 模块。

代码语言:javascript
复制
>> import pickle
>> import xarray as xr

>> ds = xr.Dataset({'foo': (('x', 'y'), np.random.rand(4, 5))}, coords={'x': [10, 20, 30, 40],
'y': pd.date_range('2000-01-01', periods=5),
'z': ('x', list('abcd'))})

# 设置 protocol = -1 比默认的基于文本的 pickle 格式要快
>> pkl = pickle.dumps(ds, protocol=-1)
>> pickle.loads(pkl)
<xarray.Dataset>
Dimensions:  (x: 4, y: 5)
Coordinates:
  * y        (y) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 ...
  * x        (x) int32 10 20 30 40
    z        (x) <U1 'a' 'b' 'c' 'd'
Data variables:
    foo      (x, y) float64 0.7336 0.8575 0.3533 0.2408 0.06464 0.1491 ...

支持 Pcikle 是非常重要的,因为这可以无需安装额外的库就能让你用其他python 模块(比如 multiprocessing) 使用 xarray 对象。但有两点要注意:

  • 为了简化序列化操作, xarray 在 dumping 对象之前会将数组中的所有值加载到内存中。因此这种方式不适用于大数据集。比如 netCDF 或 OPeNDAP
  • 只要 xarray 对象的内部数据结构不变, Pickle 就能工作。因为 xarray 的内部设计是重新定义的,所以无法保证能够适用于所有版本。

字典

使用 to_dict 方法可以将 Dataset (DataArray) 转换为 字典:

代码语言:javascript
复制
>> d = ds.to_dict()
>> d
{'attrs': {},
 'coords': {'x': {'attrs': {}, 'data': [10, 20, 30, 40], 'dims': ('x',)},
  'y': {'attrs': {},
   'data': [datetime.datetime(2000, 1, 1, 0, 0),
        ...
    datetime.datetime(2000, 1, 5, 0, 0)],
   'dims': ('y',)},
  'z': {'attrs': {}, 'data': ['a', 'b', 'c', 'd'], 'dims': ('x',)}},
 'data_vars': {'foo': {'attrs': {},
   'data': [[0.7336403118672791,
        ...
     0.06463679057178773],
    [0.14912269066889416,
     ...
     0.6386273347534372],
    [0.2930828836692492,
     ...
     0.5314383607667001],
    [0.7881755926175252,
     ...
     0.8322477167318335]],
   'dims': ('x', 'y')}},
 'dims': {'x': 4, 'y': 5}}

也可以使用 from_dict 方法创建 xarray 对象:

代码语言:javascript
复制
>> ds_dict = xr.Dataset.from_dict(d)

字典支持非常灵活的使用 xarray 对象。无需外部的库即可很容易的转换为 pickle,json 或 geojson。所有的值都会转换为列表,因此字典可以很大。

netCDF

推荐使用 netCDF 存储 xarray 数据结构。netCDF是源于地理科学的自描述二进制数据格式。 xarray 基于 netCDF 数据模式,因此磁盘中的 netCDF文件和 Dataset 对象是对应的。

netCDF在大多数平台上都支持,因此科学程序语言几乎都支持解析 netCDF 文件。最近的 netCDF 版本基于更广泛使用的 HDF-5 文件格式。了解更多netCDF文件格式 [注1]

为了读取或写入 netCDF 文件,需要安装 scipy 或 netcdf4-python。

使用 to_netcdf 方法可以存储 Dataset 到磁盘中:

代码语言:javascript
复制
>> ds.to_netcdf('save.nc')

默认存储为 netCDF4 格式。通过 format 和 engine 参数控制文件写入。

使用 open_dataset 方法可以从 netCDF 文件加载数据,并创建 Dataset:

代码语言:javascript
复制
>> ds_disk = xr.open_dataset('save.nc')

DataArray 对象也可以使用相同的方式存储和读取 netCDF文件。但是在操作之前都会先将 DataArray 转换为 Dataset,从而保证数据的准确性。

一个数据集可以加载或写入netCDF 文件的特定组中。传入 group 关键词参数给 open_dateset 函数可以从特定组加载数据。也可以通过类路径方式指定组。比如:获取 foo 组中的 bar 组,可以传递 '/foo/bar/' 给 group 参数。当要在一个文件中写入多个组时,传入 mode = 'a' 给 to_netcdf ,从而确保每一次调用都不会删除文件。

除非执行一系列计算操作,否则 netCDF 文件中的值是不会加载到内存中的。更为重要的一点是:当你改变数据集的值时,如果只是改变了内存中 xarray,那么源文件是不会被改变的。

技巧:

xarray 对服务器或本地磁盘文件的延迟加载并不总是有利的。当你要执行高强度计算之前,应先执行 load 方法将数据加载到内存中。

虽然 Dataset 有 close 方法可以关闭 netCDF 文件,但是通常还是利用 with 来处理,因为这会自动关闭文件。

代码语言:javascript
复制
with xr.open_dataset('saved_on_disk.nc') as ds:
     print(ds.keys())

尽管 xarray 提供了递增文件读取,但是并不支持这种形式的写入操作。对于文件太大而无法适应内存的数据集来说,这是非常有效的策略。xarray 整合了 dask.array 来提供完整的流计算。

读取编码数据

NetCDF 文件遵循一些编码 datetime 数组 (作为具有 'units' 属性的数字) 以及打包和解包数据约定。如果 open_dataset 方法设置了 decode_cf = True (默认值),xarray 会根据CF规则(一般只需要知道此解码过程即可)试图自动解码 netCDF 文件中的数值。如果变量有一个无效的 'units' 或 'calendar' 属性的话,此转换过程会失败。此时,可以手动关闭解码过程。

DataArray.encoding 属性可以查看解码信息:

代码语言:javascript
复制
ds_disk['y'].encoding

注意:除了索引外,管理变量的所有操作都会移除编码信息。

写入编码数据

你也可以自定义 xarray 如何为 netCDF 文件中的每个数据集变量提供编码信息。encoding 参数接收包含编码信息的键值对字典。这些信息会保存为 netCDF 变量的编码信息,从而使得 xarray 能够更准确的读取编码数据。

注意:

是否使用编码选项是可选的。如果不指定编码信息的话,xarray 会使用默认的编码属性信息;如果指定的话,这会更有利于额外的处理操作,尤其是压缩操作。

当存储文件时,这些属性信息会保存为每一个变量的属性。从而允许xarray 以及其它工具能够正确的读取 netCDF 文件。

缩放系数及类型转换

以下选项对于任何 netCDF 版本均适用:

  • dtype:任何有效的 numpy 类型或字符串都可转换为 dtype。控制写入文件的数据类型。
  • _FillValue:当保存 xarray 对象到文件时,xarray 变量中的 Nan 会映射为此属性包含的值。这在转换具有缺省值的浮点数为整数时就显得非常重要了。因为 Nan 对于整数来说不是有效值。默认情况下,对于包含浮点值的变量在存储时 _FillValue 为 Nan。
  • scale_factor 和 add_offset:使用公式: decode = scale_factor * encoded + add_offset 将编码数据转换为解码数据。

数据块压缩

zlib,complevel,fletcher32,continguous 和 chunksizes 均可用于 netCDF/HDF5 数据块压缩。这只对 netCDF4 文件有效,即 format = 'netCDF4',engine = 'netcdf4' 或 'h5netcdf'。

基于 gzip 的数据块压缩可以有效的节省空间,尤其是稀疏数据。当然这会产生很大的性能开销。HDF5 可以完全将块读入内存,其解码速度是 50-100 MB/s。但是HDF5压缩和解压缩操作目前不能并行处理。

时间单位

'units' 和 ‘calendar’ 属性控制 xarray 如何将 datetime64 和 timedelta64 数组序列化为数值数组。'units' 编码是类似 datetime64 数据的 'days since 1900-01-01' 字符串或 timedelta64 的 'day' 字符串。'calendar' 应该是 netcdf4-python 支持的日历形式:'standard','gregorian',‘proleptic_gregorian’ ‘noleap’, ‘365_day’, ‘360_day’, ‘julian’, ‘all_leap’, ‘366_day’。

默认情况下,xarray 使用 'proleptic_gregorian' 作为日历,两个值之间的最小时间差作为单位。第一个时间值作为标准时间。

OPeNDAP

xarray 对 OPeNDAP 的支持可以让我们通过 HTTP 获取大数据集。

例如,可以 PRISM 项目的 GB 级天气数据产品:

代码语言:javascript
复制
>> remote_data = xr.open_dataset('http://iridl.ldeo.columbia.edu/SOURCES/.OSU/.PRISM/.monthly/dods', decode_times=False)

>> remote_data
<xarray.Dataset>
Dimensions:  (T: 1420, X: 1405, Y: 621)
Coordinates:
  * X        (X) float32 -125.0 -124.958 -124.917 -124.875 -124.833 -124.792 ...
  * T        (T) float32 -779.5 -778.5 -777.5 -776.5 -775.5 -774.5 -773.5 ...
  * Y        (Y) float32 49.9167 49.875 49.8333 49.7917 49.75 49.7083 ...
Data variables:
    ppt      (T, Y, X) float64 ...
    tdmean   (T, Y, X) float64 ...
    tmax     (T, Y, X) float64 ...
    tmin     (T, Y, X) float64 ...
Attributes:
    Conventions:  IRIDL
    expires:      1370044800

注意:

很多数据集可能不会遵循CF规则,当遇到这种情况时,设置 open_dataset 方法的 decode_cf 参数为 False。

我们可以选择任意时间的数据,并对数据进行切片操作。除非查看特定的值,否则不会加载。

代码语言:javascript
复制
>> tmax = remote_data['tmax'][:500, ::3, ::3]
>> tmax
<xarray.DataArray 'tmax' (T: 500, Y: 207, X: 469)>
[48541500 values with dtype=float64]
Coordinates:
  * X        (X) float32 -125.0 -124.875 -124.75 -124.625 -124.5 -124.375 ...
  * T        (T) float32 -779.5 -778.5 -777.5 -776.5 -775.5 -774.5 -773.5 ...
  * Y        (Y) float32 49.9167 49.7917 49.6667 49.5417 49.4167 49.2917 ...
Attributes:
    pointwidth:     120
    standard_name:  air_temperature
    units:          Celsius_scale
    expires:        1370044800
>> tmax[0].plot

Rasterio

如果安装了 rasterio,可以使用 rasterio 打开GeoTiff以及其它栅格数据集。比如:

代码语言:javascript
复制
>> rio = xr.open_rasterio('RGB.byte.tif')
代码语言:javascript
复制
<xarray.DataArray (band: 3, y: 718, x: 791)>
[1703814 values with dtype=uint8]
Coordinates:
  * band     (band) int64 1 2 3
  * y        (y) float64 2.827e+06 2.827e+06 2.826e+06 2.826e+06 2.826e+06 ...
  * x        (x) float64 1.02e+05 1.023e+05 1.026e+05 1.029e+05 1.032e+05 ...
Attributes:
    crs:      +init=epsg:32618

目前这是个实验性功能,0.9.6以后的版本才支持。

使用 PyNIO 处理

xarray 可以处理 PyNIO 支持的所有格式文件,只需要在使用 open_dateset 方法时指定 engine 参数为 'pynio' 即可。

使用 pandas 处理

目前 pandas 已经支持了很多文件格式的处理。比如:

Format Type

Data Description

Reader

Writer

text

CSV

read_csv

to_csv

text

JSON

read_json

to_json

text

HTML

read_html

to_html

text

Local clipboard

read_clipboard

to_clipboard

binary

MS Excel

read_excel

to_excel

binary

HDF5 Format

read_hdf

to_hdf

binary

Feather Format

read_feather

to_feather

binary

Msgpack

read_msgpack

to_msgpack

binary

Stata

read_stata

to_stata

binary

SAS

read_sas

binary

Python Pickle Format

read_pickle

to_pickle

SQL

SQL

read_sql

to_sql

SQL

Google Big Query

read_gbq

to_gbq

多文件合并

netCDF 文件通常是一个集合,比如,不同模式运行输出的不同文件。利用 concat 方法可以将多个文件合并为单个文件。

注意:

如果你安装了 dask 的话,可以使用 open_mfdataset 合并多个文件:

代码语言:javascript
复制
xr.open_mfdataset('../*.nc')

此函数会自动合并并连接多个文件为一个 xarray 数据集。

下面是 netCDF4-python 中 MFDataset 类似版:

代码语言:javascript
复制
from glob import glob
import xarray as xr

def read_netcdfs(files, dim):
    # glob expands paths with * to a list of files, like the unix shell
    paths = sorted(glob(files))
    datasets = [xr.open_dataset(p) for p in paths]
    combined = xr.concat(dataset, dim)
    return combined

combined = read_netcdfs('/all/my/files/*.nc', dim='time')

上述示例虽然适用于大多数情况,但并不稳定。首先,没有关闭文件,当加载很多文件时就会失败;其次,假设读去文件中的所有数据,会填满内存。

下面是增强版:

代码语言:javascript
复制
def read_netcdfs(files, dim, transform_func=None):
    def process_one_path(path):
        # 使用上下文管理器,确保文件使用后被关闭
        with xr.open_dataset(path) as ds:
            # transform_func 可以执行一些选择操作
            if transform_func is not None:
                ds = transform_func(ds)
            # 从转换数据集中加载所有数据
            ds.load()
            return ds

    paths = sorted(glob(files))
    datasets = [process_one_path(p) for p in paths]
    combined = xr.concat(datasets, dim)
    return combined

# 这里只计算每个文件的平均值
# 你也可以使用 .sel 方法选取子集
combined = read_netcdfs('/all/my/files/*.nc', dim='time',
                        transform_func=lambda ds: ds.mean())

增强版非常稳定,可以处理GB级数据。


注1:https://www.unidata.ucar.edu/software/netcdf/docs/faq.html#What-Is-netCDF

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-08-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 气象杂货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Rasterio
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档