| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- # This file is part of h5py, a Python interface to the HDF5 library.
- #
- # http://www.h5py.org
- #
- # Copyright 2008-2013 Andrew Collette and contributors
- #
- # License: Standard 3-clause BSD; see "license.txt" for full license terms
- # and contributor agreement.
- """
- Tests the h5py.Dataset.__getitem__ method.
- This module does not specifically test type conversion. The "type" axis
- therefore only tests objects which interact with the slicing system in
- unreliable ways; for example, compound and array types.
- See test_dataset_getitem_types for type-conversion tests.
- Tests are organized into TestCases by dataset shape and type. Test
- methods vary by slicing arg type.
- 1. Dataset shape:
- Empty
- Scalar
- 1D
- 3D
- 2. Type:
- Float
- Compound
- Array
- 3. Slicing arg types:
- Ellipsis
- Empty tuple
- Regular slice
- MultiBlockSlice
- Indexing
- Index list
- Boolean mask
- Field names
- """
- import sys
- import numpy as np
- import pytest
- import h5py
- from .common import ut, TestCase, make_name
- class TestEmpty(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- sid = h5py.h5s.create(h5py.h5s.NULL)
- tid = h5py.h5t.C_S1.copy()
- tid.set_size(10)
- dsid = h5py.h5d.create(self.f.id, b'x', tid, sid)
- self.dset = h5py.Dataset(dsid)
- self.empty_obj = h5py.Empty(np.dtype("S10"))
- def test_ndim(self):
- """ Verify number of dimensions """
- self.assertEqual(self.dset.ndim, 0)
- def test_shape(self):
- """ Verify shape """
- self.assertEqual(self.dset.shape, None)
- def test_size(self):
- """ Verify shape """
- self.assertEqual(self.dset.size, None)
- def test_nbytes(self):
- """ Verify nbytes """
- self.assertEqual(self.dset.nbytes, 0)
- def test_ellipsis(self):
- self.assertEqual(self.dset[...], self.empty_obj)
- def test_tuple(self):
- self.assertEqual(self.dset[()], self.empty_obj)
- def test_slice(self):
- """ slice -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[0:4]
- def test_multi_block_slice(self):
- """ MultiBlockSlice -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[h5py.MultiBlockSlice()]
- def test_index(self):
- """ index -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[0]
- def test_indexlist(self):
- """ index list -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[[1,2,5]]
- def test_mask(self):
- """ mask -> ValueError """
- mask = np.array(True, dtype='bool')
- with self.assertRaises(ValueError):
- self.dset[mask]
- def test_fieldnames(self):
- """ field name -> ValueError """
- with self.assertRaises(ValueError):
- self.dset['field']
- class TestScalarFloat(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- self.data = np.array(42.5, dtype=np.double)
- self.dset = self.f.create_dataset('x', data=self.data)
- def test_ndim(self):
- """ Verify number of dimensions """
- self.assertEqual(self.dset.ndim, 0)
- def test_size(self):
- """ Verify size """
- self.assertEqual(self.dset.size, 1)
- def test_nbytes(self):
- """ Verify nbytes """
- self.assertEqual(self.dset.nbytes, self.data.dtype.itemsize) # not sure if 'f' is always alias for 'f4'
- def test_shape(self):
- """ Verify shape """
- self.assertEqual(self.dset.shape, tuple())
- def test_ellipsis(self):
- """ Ellipsis -> scalar ndarray """
- out = self.dset[...]
- self.assertArrayEqual(out, self.data)
- def test_tuple(self):
- """ () -> bare item """
- out = self.dset[()]
- self.assertArrayEqual(out, self.data.item())
- def test_slice(self):
- """ slice -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[0:4]
- def test_multi_block_slice(self):
- """ MultiBlockSlice -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[h5py.MultiBlockSlice()]
- def test_index(self):
- """ index -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[0]
- # FIXME: NumPy has IndexError instead
- def test_indexlist(self):
- """ index list -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[[1,2,5]]
- # FIXME: NumPy permits this
- def test_mask(self):
- """ mask -> ValueError """
- mask = np.array(True, dtype='bool')
- with self.assertRaises(ValueError):
- self.dset[mask]
- def test_fieldnames(self):
- """ field name -> ValueError (no fields) """
- with self.assertRaises(ValueError):
- self.dset['field']
- class TestScalarCompound(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- self.data = np.array((42.5, -118, "Hello"), dtype=[('a', 'f'), ('b', 'i'), ('c', '|S10')])
- self.dset = self.f.create_dataset('x', data=self.data)
- def test_ndim(self):
- """ Verify number of dimensions """
- self.assertEqual(self.dset.ndim, 0)
- def test_shape(self):
- """ Verify shape """
- self.assertEqual(self.dset.shape, tuple())
- def test_size(self):
- """ Verify size """
- self.assertEqual(self.dset.size, 1)
- def test_nbytes(self):
- """ Verify nbytes """
- self.assertEqual(self.dset.nbytes, self.data.dtype.itemsize)
- def test_ellipsis(self):
- """ Ellipsis -> scalar ndarray """
- out = self.dset[...]
- # assertArrayEqual doesn't work with compounds; do manually
- self.assertIsInstance(out, np.ndarray)
- self.assertEqual(out.shape, self.data.shape)
- self.assertEqual(out.dtype, self.data.dtype)
- def test_tuple(self):
- """ () -> np.void instance """
- out = self.dset[()]
- self.assertIsInstance(out, np.void)
- self.assertEqual(out.dtype, self.data.dtype)
- def test_slice(self):
- """ slice -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[0:4]
- def test_multi_block_slice(self):
- """ MultiBlockSlice -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[h5py.MultiBlockSlice()]
- def test_index(self):
- """ index -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[0]
- # FIXME: NumPy has IndexError instead
- def test_indexlist(self):
- """ index list -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[[1,2,5]]
- # FIXME: NumPy permits this
- def test_mask(self):
- """ mask -> ValueError """
- mask = np.array(True, dtype='bool')
- with self.assertRaises(ValueError):
- self.dset[mask]
- # FIXME: NumPy returns a scalar ndarray
- def test_fieldnames(self):
- """ field name -> bare value """
- out = self.dset['a']
- self.assertIsInstance(out, np.float32)
- self.assertEqual(out, self.dset['a'])
- class TestScalarArray(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- self.dt = np.dtype('(3,2)f')
- self.data = np.array([(3.2, -119), (42, 99.8), (3.14, 0)], dtype='f')
- self.dset = self.f.create_dataset('x', (), dtype=self.dt)
- self.dset[...] = self.data
- def test_ndim(self):
- """ Verify number of dimensions """
- self.assertEqual(self.data.ndim, 2)
- self.assertEqual(self.dset.ndim, 0)
- def test_size(self):
- """ Verify size """
- self.assertEqual(self.dset.size, 1)
- def test_nbytes(self):
- """ Verify nbytes """
- self.assertEqual(self.dset.nbytes, self.dset.dtype.itemsize) # not sure if 'f' is always alias for 'f4'
- def test_shape(self):
- """ Verify shape """
- self.assertEqual(self.data.shape, (3, 2))
- self.assertEqual(self.dset.shape, tuple())
- def test_ellipsis(self):
- """ Ellipsis -> ndarray promoted to underlying shape """
- out = self.dset[...]
- self.assertArrayEqual(out, self.data)
- def test_tuple(self):
- """ () -> same as ellipsis """
- out = self.dset[...]
- self.assertArrayEqual(out, self.data)
- def test_slice(self):
- """ slice -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[0:4]
- def test_multi_block_slice(self):
- """ MultiBlockSlice -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[h5py.MultiBlockSlice()]
- def test_index(self):
- """ index -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[0]
- def test_indexlist(self):
- """ index list -> ValueError """
- with self.assertRaises(ValueError):
- self.dset[[]]
- def test_mask(self):
- """ mask -> ValueError """
- mask = np.array(True, dtype='bool')
- with self.assertRaises(ValueError):
- self.dset[mask]
- def test_fieldnames(self):
- """ field name -> ValueError (no fields) """
- with self.assertRaises(ValueError):
- self.dset['field']
- class Test1DZeroFloat(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- self.data = np.ones((0,), dtype='f')
- self.dset = self.f.create_dataset('x', data=self.data)
- def test_ndim(self):
- """ Verify number of dimensions """
- self.assertEqual(self.dset.ndim, 1)
- def test_shape(self):
- """ Verify shape """
- self.assertEqual(self.dset.shape, (0,))
- def test_ellipsis(self):
- """ Ellipsis -> ndarray of matching shape """
- self.assertNumpyBehavior(self.dset, self.data, np.s_[...])
- def test_tuple(self):
- """ () -> same as ellipsis """
- self.assertNumpyBehavior(self.dset, self.data, np.s_[()])
- def test_slice(self):
- """ slice -> ndarray of shape (0,) """
- self.assertNumpyBehavior(self.dset, self.data, np.s_[0:4])
- def test_slice_stop_less_than_start(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[7:5])
- def test_index(self):
- """ index -> out of range """
- with self.assertRaises(IndexError):
- self.dset[0]
- def test_indexlist(self):
- """ index list """
- self.assertNumpyBehavior(self.dset, self.data, np.s_[[]])
- def test_mask(self):
- """ mask -> ndarray of matching shape """
- mask = np.ones((0,), dtype='bool')
- self.assertNumpyBehavior(
- self.dset,
- self.data,
- np.s_[mask],
- # Fast reader doesn't work with boolean masks
- skip_fast_reader=True,
- )
- def test_fieldnames(self):
- """ field name -> ValueError (no fields) """
- with self.assertRaises(ValueError):
- self.dset['field']
- class Test1DFloat(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- self.data = np.arange(50).astype('f')
- self.dset = self.f.create_dataset('x', data=self.data)
- def test_ndim(self):
- """ Verify number of dimensions """
- self.assertEqual(self.dset.ndim, 1)
- def test_shape(self):
- """ Verify shape """
- self.assertEqual(self.dset.shape, (50,))
- def test_ellipsis(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[...])
- def test_tuple(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[()])
- def test_slice_simple(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[0:4])
- def test_slice_zerosize(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[4:4])
- def test_slice_strides(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[1:7:3])
- def test_slice_negindexes(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[-8:-2:3])
- def test_slice_stop_less_than_start(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[7:5])
- def test_slice_outofrange(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[100:400:3])
- def test_slice_backwards(self):
- """ we disallow negative steps """
- with self.assertRaises(ValueError):
- self.dset[::-1]
- def test_slice_zerostride(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[::0])
- def test_index_simple(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[3])
- def test_index_neg(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[-4])
- # FIXME: NumPy permits this... it adds a new axis in front
- def test_index_none(self):
- with self.assertRaises(TypeError):
- self.dset[None]
- def test_index_illegal(self):
- """ Illegal slicing argument """
- with self.assertRaises(TypeError):
- self.dset[{}]
- def test_index_outofrange(self):
- with self.assertRaises(IndexError):
- self.dset[100]
- def test_indexlist_simple(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[[1,2,5]])
- def test_indexlist_numpyarray(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[np.array([1, 2, 5])])
- def test_indexlist_long(self):
- # selection logic changes with >16 indices
- self.assertNumpyBehavior(self.dset, self.data, np.s_[range(0, 50, 2)])
- def test_indexlist_single_index_ellipsis(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[[0], ...])
- def test_indexlist_numpyarray_single_index_ellipsis(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[np.array([0]), ...])
- def test_indexlist_long_ellipsis(self):
- # selection logic changes with >16 indices
- self.assertNumpyBehavior(self.dset, self.data, np.s_[range(0, 50, 2), ...])
- def test_indexlist_numpyarray_ellipsis(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[np.array([1, 2, 5]), ...])
- def test_indexlist_empty(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[[]])
- def test_indexlist_outofrange(self):
- with self.assertRaises(IndexError):
- self.dset[[100]]
- def test_indexlist_nonmonotonic(self):
- """ we require index list values to be strictly increasing """
- with self.assertRaises(TypeError):
- self.dset[[1,3,2]]
- def test_indexlist_monotonic_negative(self):
- # This should work: indices are logically increasing
- self.assertNumpyBehavior(self.dset, self.data, np.s_[[0, 2, -2]])
- with self.assertRaises(TypeError):
- self.dset[[-2, -3]]
- def test_indexlist_repeated(self):
- """ we forbid repeated index values """
- with self.assertRaises(TypeError):
- self.dset[[1,1,2]]
- def test_mask_true(self):
- self.assertNumpyBehavior(
- self.dset,
- self.data,
- np.s_[self.data > -100],
- # Fast reader doesn't work with boolean masks
- skip_fast_reader=True,
- )
- def test_mask_false(self):
- self.assertNumpyBehavior(
- self.dset,
- self.data,
- np.s_[self.data > 100],
- # Fast reader doesn't work with boolean masks
- skip_fast_reader=True,
- )
- def test_mask_partial(self):
- self.assertNumpyBehavior(
- self.dset,
- self.data,
- np.s_[self.data > 5],
- # Fast reader doesn't work with boolean masks
- skip_fast_reader=True,
- )
- def test_mask_wrongsize(self):
- """ we require the boolean mask shape to match exactly """
- with self.assertRaises(TypeError):
- self.dset[np.ones((2,), dtype='bool')]
- def test_fieldnames(self):
- """ field name -> ValueError (no fields) """
- with self.assertRaises(ValueError):
- self.dset['field']
- class Test2DZeroFloat(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- self.data = np.ones((0,3), dtype='f')
- self.dset = self.f.create_dataset('x', data=self.data)
- def test_ndim(self):
- """ Verify number of dimensions """
- self.assertEqual(self.dset.ndim, 2)
- def test_shape(self):
- """ Verify shape """
- self.assertEqual(self.dset.shape, (0, 3))
- def test_indexlist(self):
- """ see issue #473 """
- self.assertNumpyBehavior(self.dset, self.data, np.s_[:,[0,1,2]])
- class Test2DFloat(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- self.data = np.ones((5,3), dtype='f')
- self.dset = self.f.create_dataset('x', data=self.data)
- def test_ndim(self):
- """ Verify number of dimensions """
- self.assertEqual(self.dset.ndim, 2)
- def test_size(self):
- """ Verify size """
- self.assertEqual(self.dset.size, 15)
- def test_nbytes(self):
- """ Verify nbytes """
- self.assertEqual(self.dset.nbytes, 15*self.data.dtype.itemsize) # not sure if 'f' is always alias for 'f4'
- def test_shape(self):
- """ Verify shape """
- self.assertEqual(self.dset.shape, (5, 3))
- def test_indexlist(self):
- """ see issue #473 """
- self.assertNumpyBehavior(self.dset, self.data, np.s_[:,[0,1,2]])
- def test_index_emptylist(self):
- self.assertNumpyBehavior(self.dset, self.data, np.s_[:, []])
- self.assertNumpyBehavior(self.dset, self.data, np.s_[[]])
- class TestVeryLargeArray(TestCase):
- def setUp(self):
- TestCase.setUp(self)
- self.dset = self.f.create_dataset('x', shape=(2**15, 2**16), dtype='f4')
- @ut.skipIf(sys.maxsize < 2**31, 'Maximum integer size >= 2**31 required')
- def test_size(self):
- self.assertEqual(self.dset.size, 2**31)
- def test_read_no_fill_value(writable_file):
- # With FILL_TIME_NEVER, HDF5 doesn't write zeros in the output array for
- # unallocated chunks. If we read into uninitialized memory, it can appear
- # to read random values. https://github.com/h5py/h5py/issues/2069
- dcpl = h5py.h5p.create(h5py.h5p.DATASET_CREATE)
- dcpl.set_chunk((1,))
- dcpl.set_fill_time(h5py.h5d.FILL_TIME_NEVER)
- ds = h5py.Dataset(h5py.h5d.create(
- writable_file.id,
- make_name().encode("utf-8"),
- h5py.h5t.IEEE_F64LE,
- h5py.h5s.create_simple((5,)),
- dcpl,
- ))
- np.testing.assert_array_equal(ds[:3], np.zeros(3, np.float64))
- class TestBoolIndex(TestCase):
- """
- Tests for indexing with Boolean arrays
- """
- def setUp(self):
- super().setUp()
- self.arr = np.arange(9).reshape(3,-1)
- self.dset = self.f.create_dataset('x', data=self.arr)
- def test_select_first_axis(self):
- sel = np.s_[[False, True, False],:]
- self.assertNumpyBehavior(self.dset, self.arr, sel)
- def test_wrong_size(self):
- sel = np.s_[[False, True, False, False],:]
- with self.assertRaises(TypeError):
- self.dset[sel]
- def test_error_newaxis(writable_file):
- ds = writable_file.create_dataset(make_name(), data=np.arange(5))
- with pytest.raises(TypeError, match="newaxis"):
- ds[np.newaxis, :]
- @pytest.mark.parametrize(
- "arr", [
- np.arange(9),
- np.array([s.encode() for s in 'abcdefghi'], dtype=object),
- ]
- )
- def test_bool_selection_1d(writable_file, arr):
- """https://github.com/h5py/h5py/issues/2674"""
- arr = arr.reshape(3, 3)
- name = make_name()
- writable_file[name] = arr
- dset = writable_file[name]
- sel = np.array([True, False, True])
- np.testing.assert_array_equal(dset[sel], arr[sel])
- np.testing.assert_array_equal(dset[sel, :], arr[sel, :])
- class TestZeroSizeSelectionResizableDataset(TestCase):
- """
- Tests for indexing of zero Resizable Datasets
- see https://github.com/h5py/h5py/issues/2549
- """
- def setUp(self):
- super().setUp()
- self.dset0 = self.f.create_dataset('x', (0,), 'f4', maxshape=(None,))
- self.dset1 = self.f.create_dataset('y', (1,), 'f4', maxshape=(None,))
- def test_set_with_scalar(self):
- sel = np.s_[:]
- self.dset0[sel] = 1
- self.arr = np.array([], dtype=np.float32)
- self.assertNumpyBehavior(self.dset0, self.arr, sel)
- def test_set_with_array(self):
- sel = np.s_[:]
- with self.assertRaises(TypeError, msg="Can't broadcast (4,) -> (0,)"):
- self.dset0[sel] = np.arange(4)
- def test_set_zerosel_with_scalar(self):
- sel = np.s_[0:0]
- self.dset1[sel] = 1
- self.arr = np.array([], dtype=np.float32)
- self.assertNumpyBehavior(self.dset1, self.arr, sel)
- def test_set_zerosel_with_array(self):
- sel = np.s_[0:0]
- with self.assertRaises(TypeError, msg="Can't broadcast (4,) -> (0,)"):
- self.dset1[sel] = np.arange(4)
|