diff --git a/pyfive/high_level.py b/pyfive/high_level.py index fbc41344..bc52bf5d 100644 --- a/pyfive/high_level.py +++ b/pyfive/high_level.py @@ -1,6 +1,6 @@ """ High-level classes for reading HDF5 files. """ -from collections import Mapping, deque +from collections import Mapping, deque, Sequence from io import open # Python 2.7 requires for a Buffered Reader import numpy as np @@ -299,7 +299,7 @@ def fillvalue(self): @property def dims(self): """ dims attribute. """ - raise NotImplementedError + return DimensionManager(self) @property def attrs(self): @@ -307,3 +307,39 @@ def attrs(self): if self._attrs is None: self._attrs = self._dataobjects.get_attributes() return self._attrs + + +class DimensionManager(Sequence): + """ Represents a collection of dimensions associated with a dataset. """ + def __init__(self, dset): + ndim = len(dset.shape) + dim_list = [[]]*ndim + if 'DIMENSION_LIST' in dset.attrs: + dim_list = dset.attrs['DIMENSION_LIST'] + dim_labels = [b'']*ndim + if 'DIMENSION_LABELS' in dset.attrs: + dim_labels = dset.attrs['DIMENSION_LABELS'] + self._dims = [ + DimensionProxy(dset.file, label, refs) for + label, refs in zip(dim_labels, dim_list)] + + def __len__(self): + return len(self._dims) + + def __getitem__(self, x): + return self._dims[x] + + +class DimensionProxy(Sequence): + """ Represents a HDF5 "dimension". """ + + def __init__(self, dset_file, label, refs): + self.label = label.decode('utf-8') + self._refs = refs + self._file = dset_file + + def __len__(self): + return len(self._refs) + + def __getitem__(self, x): + return self._file[self._refs[x]] diff --git a/tests/dim_scales.hdf5 b/tests/dim_scales.hdf5 new file mode 100644 index 00000000..fc775f37 Binary files /dev/null and b/tests/dim_scales.hdf5 differ diff --git a/tests/make_dim_scales.py b/tests/make_dim_scales.py new file mode 100644 index 00000000..25cccec2 --- /dev/null +++ b/tests/make_dim_scales.py @@ -0,0 +1,39 @@ +#! /usr/bin/env python +""" Create a HDF5 file with dimension scales and labels . """ +import h5py +import numpy as np + +f = h5py.File('dim_scales.hdf5', 'w') + +common_args = { + 'track_times': False, + 'dtype': 'i4', +} + +# dataset with dimension labels and scales +f.create_dataset('dset1', data=np.ones((4, 3, 2)), **common_args) + +# dimension labels +f['dset1'].dims[0].label = 'z' +f['dset1'].dims[1].label = 'y' +f['dset1'].dims[2].label = 'x' + +# dimension scales +f.create_dataset('x1', data=[1, 2], **common_args) +f.create_dataset('y1', data=[3, 4, 5], **common_args) +f.create_dataset('z1', data=[0, 10, 20, 30], **common_args) +f.create_dataset('x2', data=[99, 98], **common_args) + +f['dset1'].dims.create_scale(f['x1'], 'x1_name') +f['dset1'].dims.create_scale(f['y1'], 'y1_name') +f['dset1'].dims.create_scale(f['z1'], 'z1_name') + +f['dset1'].dims[0].attach_scale(f['z1']) +f['dset1'].dims[1].attach_scale(f['y1']) +f['dset1'].dims[2].attach_scale(f['x1']) +f['dset1'].dims[2].attach_scale(f['x2']) + +# dataset with no dimension entries +f.create_dataset('dset2', data=np.ones((4, 3, 2)), **common_args) + +f.close() diff --git a/tests/test_dim_scales.py b/tests/test_dim_scales.py new file mode 100644 index 00000000..ce97b4b5 --- /dev/null +++ b/tests/test_dim_scales.py @@ -0,0 +1,64 @@ +""" Unit tests for pyfive dimension scales. """ + +import os + +from numpy.testing import assert_array_equal + +import pyfive + +DIRNAME = os.path.dirname(__file__) +DIM_SCALES_HDF5_FILE = os.path.join(DIRNAME, './dim_scales.hdf5') + + +def test_dim_labels(): + + hfile = pyfive.File(DIM_SCALES_HDF5_FILE) + + # dataset with dimension labels + dims = hfile['dset1'].dims + assert dims[0].label == 'z' + assert dims[1].label == 'y' + assert dims[2].label == 'x' + + # dataset with no dimension labels + dims = hfile['dset2'].dims + assert dims[0].label == '' + assert dims[1].label == '' + assert dims[2].label == '' + + hfile.close() + + +def test_dim_scales(): + + hfile = pyfive.File(DIM_SCALES_HDF5_FILE) + + # dataset with dimension scales + dims = hfile['dset1'].dims + + assert len(dims) == 3 + + assert len(dims[0]) == 1 + assert len(dims[1]) == 1 + assert len(dims[2]) == 2 + + assert dims[0][0].name == '/z1' + assert dims[1][0].name == '/y1' + assert dims[2][0].name == '/x1' + assert dims[2][1].name == '/x2' + + assert_array_equal(dims[0][0][:], [0, 10, 20, 30]) + assert_array_equal(dims[1][0][:], [3, 4, 5]) + assert_array_equal(dims[2][0][:], [1, 2]) + assert_array_equal(dims[2][1][:], [99, 98]) + + # dataset with no dimension scales + dims = hfile['dset2'].dims + + assert len(dims) == 3 + + assert len(dims[0]) == 0 + assert len(dims[1]) == 0 + assert len(dims[2]) == 0 + + hfile.close()