如何在 h5py 中使用 HDF5 尺寸刻度

How to use HDF5 dimension scales in h5py

HDF5 具有维度尺度的概念,如 on the HDF5 and h5py 网站所述。然而,这些解释都使用了简洁或通用的例子,所以我不太明白如何使用维度尺度。即,给定某个 HDF5 文件 f = h5py.File('data.h5'):

中的数据集 f['coordinates']
>>> f['coordinates'].value
array([[ 52.60636111,   4.38963889],
   [ 52.57877778,   4.43422222],
   [ 52.58319444,   4.42811111],
   ..., 
   [ 52.62269444,   4.43130556],
   [ 52.62711111,   4.42519444],
   [ 52.63152778,   4.41905556]])

我想说清楚,第一列是纬度,第二列是经度。是否使用维度尺度?或者它们是用来表示单位是度的。或者两者兼而有之?

也许另一个具体的例子可以更好地说明维度尺度的使用?如果你有,请分享它,即使你没有使用 h5py。

专门针对这个问题,最好的答案可能是使用attributes:

f['coordinates'].attrs['columns'] = ['latitude', 'longitude']

但是维度标尺在其他方面也很有用。我将展示它们的用途,如何以类似于属性的方式使用它们,以及如何实际使用 f['coordinates'] 数据集 作为一些规模 其他数据集。

维度尺度

我同意这些文档页面并不像它们应该的那样清晰,因为它们在实际解释基本概念之前就陷入了复杂的可能性和技术细节的泥潭。我认为一些简单的例子应该可以清楚地说明问题。

首先,假设您在一天中一直跟踪室外温度——也许每小时整点测量一次,总共测量 24 次。您可能会将其视为两列数据:一列代表小时,另一列代表温度。您可以将其存储为一个 24x2 形状的数据集。但是时间和温度有不同的单位,而且是不同的数据类型。因此,将时间和温度存储为单独的数据集可能更有意义——可能命名为 "time""temperature",每个形状均为 24。但您还需要更清楚这些是什么以及它们之间的关系。这种关系就是“尺寸尺度”的真正用途。

如果您想象将温度绘制为时间的函数,您可能会将水平轴标记为“时间(一天中的小时)”,并且水平轴的 scale 将是小时本身,告诉您绘制每个温度的水平位置。您可以像这样通过 h5py 存储此信息:

with h5py.File("temperatures.h5", "w") as f:
    time = f.create_dataset("time", data=...)
    time.make_scale("hour of day")
    temp = f.create_dataset("temperature", data=...)
    temp.dims[0].label = "Time"
    temp.dims[0].attach_scale(time)

请注意,make_scale 的参数是关于特定 time 数据集的特定信息——在这种情况下,我们用来测量的单位 time——而 label 是该维度的更一般的概念。另请注意,将单位信息附加为属性实际上更标准,但我更喜欢这种指定比例单位的方法,因为它简单。

现在,假设我们测量了三个不同地方的温度,比如洛杉矶、芝加哥和纽约。现在,我们的温度数组将具有 24x3 的形状。我们仍然需要 dim[0] 的时间尺度,但现在我们还有 dim[1] 需要处理。

with h5py.File("temperatures.h5", "w") as f:
    time = f.create_dataset("time", data=...)
    time.make_scale("hour of day")
    cities = f.create_dataset("cities",
        data=[s.encode() for s in ["Los Angeles", "Chicago", "New York"]]
    )
    cities.make_scale("city")
    temp = f.create_dataset("temperature", data=...)
    temp.dims[0].label = "Time"
    temp.dims[0].attach_scale(time)
    temp.dims[1].label = "Location"
    temp.dims[1].attach_scale(cities)

存储纬度和经度可能比存储城市名称更有用。实际上,您可以将 两种 类型的比例附加到同一维度。只需在最后一个代码块的底部添加这样的代码:

    latlong = f.create_dataset("latlong",
        data=[[34.0522, 118.2437], [41.8781, 87.6298], [40.7128, 74.0060]]
    )
    latlong.make_scale("latitude and longitude (degrees)")
    temp.dims[1].attach_scale(latlong)

最后,您可以像这样访问这些标签和比例:

with h5py.File("temperatures.h5", "r") as f:
    print('Datasets:', list(f))
    print('Temperature dimension labels:', [dim.label for dim in f['temperature'].dims])
    print('Temperature dim[1] scales:', f['temperature'].dims[1].keys())
    latlong = f['temperature'].dims[1]['latitude and longitude (degrees)'][:]
    print(latlong)

输出如下所示:

Datasets: ['cities', 'latlong', 'temperature', 'time']
Temperature dimension labels: ['Time', 'Location']
Temperature dim[1] scales: ['city', 'latitude and longitude (degrees)']
[[ 34.0522 118.2437]
 [ 41.8781  87.6298]
 [ 40.7128  74.006 ]]

@mike的回答对理解很有帮助。 既然你给出了一个带有纬度和经度的地理空间示例,我可能会像这样亲自创建你的数据集,其中“lat”和“lon”都是单独的一维数据集:

import numpy as np
import h5py

# assuming you already have stored the coordinates in that 2-column array...
coords = np.array(
    [[52.60636111, 4.38963889], [52.57877778, 4.43422222], [52.58319444, 4.42811111]]
)
heights = np.random.rand(3, 3)

with h5py.File("dem.h5", "w") as hf:
    lat = hf.create_dataset("lat", data=coords[:, 0])
    lat.make_scale("latitude")
    lat.attrs["units"] = "degrees north"

    lon = hf.create_dataset("lon", data=coords[:, 1])
    lon.make_scale("longitude")
    lon.attrs["units"] = "degrees east"

    h = hf.create_dataset("height", data=heights)
    h.attrs['units'] = "meters"
    h.dims[0].attach_scale(lat)
    h.dims[1].attach_scale(lon)

原因:这将使您更容易将它与 xarray 一起使用:

In [1]: import xarray as xr
In [2]: ds1 = xr.open_dataset("dem.h5")

In [3]: ds1
Out[3]:
<xarray.Dataset>
Dimensions:  (lat: 3, lon: 3)
Coordinates:
  * lat      (lat) float64 52.61 52.58 52.58
  * lon      (lon) float64 4.39 4.434 4.428
Data variables:
    height   (lat, lon) float64 ...

In [4]: ds1['lat']
Out[4]:
<xarray.DataArray 'lat' (lat: 3)>
array([52.606361, 52.578778, 52.583194])
Coordinates:
  * lat      (lat) float64 52.61 52.58 52.58
Attributes:
    units:    degrees north

In [5]: ds1['lon']
Out[5]:
<xarray.DataArray 'lon' (lon: 3)>
array([4.389639, 4.434222, 4.428111])
Coordinates:
  * lon      (lon) float64 4.39 4.434 4.428
Attributes:
    units:    degrees east

In [6]: ds1['height']
Out[6]:
<xarray.DataArray 'height' (lat: 3, lon: 3)>
array([[0.832685, 0.24167 , 0.831189],
       [0.294826, 0.779141, 0.280573],
       [0.980254, 0.593158, 0.634342]])
Coordinates:
  * lat      (lat) float64 52.61 52.58 52.58
  * lon      (lon) float64 4.39 4.434 4.428
Attributes:
    units:    meters

您添加的稍微额外的元数据(包括作为属性的“单位”)在您想要进行计算或绘制数据时得到回报:

In [9]: ds1.height.mean(dim="lat")
Out[9]:
<xarray.DataArray 'height' (lon: 3)>
array([0.70258813, 0.5379896 , 0.58203484])
Coordinates:
  * lon      (lon) float64 4.39 4.434 4.428

In [10]: ds1.height.plot.imshow()