Skip to content

Commit e59822a

Browse files
committed
support datetime64 and timedelta64
1 parent 31b67e8 commit e59822a

File tree

3 files changed

+42
-28
lines changed

3 files changed

+42
-28
lines changed

zarr/core.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,7 +1590,10 @@ def _chunk_getitem(self, chunk_coords, chunk_selection, out, out_selection,
15901590
if self._compressor:
15911591
self._compressor.decode(cdata, dest)
15921592
else:
1593-
chunk = np.frombuffer(cdata, dtype=self._dtype)
1593+
if isinstance(cdata, np.ndarray):
1594+
chunk = cdata.view(self._dtype)
1595+
else:
1596+
chunk = np.frombuffer(cdata, dtype=self._dtype)
15941597
chunk = chunk.reshape(self._chunks, order=self._order)
15951598
np.copyto(dest, chunk)
15961599
return
@@ -1736,7 +1739,7 @@ def _decode_chunk(self, cdata):
17361739
elif isinstance(chunk, np.ndarray):
17371740
chunk = chunk.view(self._dtype)
17381741
else:
1739-
chunk = np.frombuffer(chunk, self._dtype)
1742+
chunk = np.frombuffer(chunk, dtype=self._dtype)
17401743

17411744
# reshape
17421745
chunk = chunk.reshape(self._chunks, order=self._order)
@@ -2087,15 +2090,15 @@ def view(self, shape=None, chunks=None, dtype=None,
20872090
>>> import zarr
20882091
>>> import numpy as np
20892092
>>> np.random.seed(42)
2090-
>>> labels = [b'female', b'male']
2093+
>>> labels = ['female', 'male']
20912094
>>> data = np.random.choice(labels, size=10000)
20922095
>>> filters = [zarr.Categorize(labels=labels,
2093-
... dtype=data.dtype,
2094-
... astype='u1')]
2096+
... dtype=data.dtype,
2097+
... astype='u1')]
20952098
>>> a = zarr.array(data, chunks=1000, filters=filters)
20962099
>>> a[:]
2097-
array([b'female', b'male', b'female', ..., b'male', b'male', b'female'],
2098-
dtype='|S6')
2100+
array(['female', 'male', 'female', ..., 'male', 'male', 'female'],
2101+
dtype='<U6')
20992102
>>> v = a.view(dtype='u1', filters=[])
21002103
>>> v.is_view
21012104
True
@@ -2110,10 +2113,10 @@ def view(self, shape=None, chunks=None, dtype=None,
21102113
>>> v[:]
21112114
array([1, 1, 1, ..., 2, 2, 2], dtype=uint8)
21122115
>>> a[:]
2113-
array([b'female', b'female', b'female', ..., b'male', b'male', b'male'],
2114-
dtype='|S6')
2116+
array(['female', 'female', 'female', ..., 'male', 'male', 'male'],
2117+
dtype='<U6')
21152118
2116-
View as a different dtype with the same itemsize:
2119+
View as a different dtype with the same item size:
21172120
21182121
>>> data = np.random.randint(0, 2, size=10000, dtype='u1')
21192122
>>> a = zarr.array(data, chunks=1000)
@@ -2125,7 +2128,7 @@ def view(self, shape=None, chunks=None, dtype=None,
21252128
>>> np.all(a[:].view(dtype=bool) == v[:])
21262129
True
21272130
2128-
An array can be viewed with a dtype with a different itemsize, however
2131+
An array can be viewed with a dtype with a different item size, however
21292132
some care is needed to adjust the shape and chunk shape so that chunk
21302133
data is interpreted correctly:
21312134

zarr/tests/test_core.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -855,27 +855,37 @@ def test_structured_array(self):
855855
def test_dtypes(self):
856856

857857
# integers
858-
for t in 'u1', 'u2', 'u4', 'u8', 'i1', 'i2', 'i4', 'i8':
859-
z = self.create_array(shape=10, chunks=3, dtype=t)
860-
assert z.dtype == np.dtype(t)
861-
a = np.arange(z.shape[0], dtype=t)
858+
for dtype in 'u1', 'u2', 'u4', 'u8', 'i1', 'i2', 'i4', 'i8':
859+
z = self.create_array(shape=10, chunks=3, dtype=dtype)
860+
assert z.dtype == np.dtype(dtype)
861+
a = np.arange(z.shape[0], dtype=dtype)
862862
z[:] = a
863863
assert_array_equal(a, z[:])
864864

865865
# floats
866-
for t in 'f2', 'f4', 'f8':
867-
z = self.create_array(shape=10, chunks=3, dtype=t)
868-
assert z.dtype == np.dtype(t)
869-
a = np.linspace(0, 1, z.shape[0], dtype=t)
866+
for dtype in 'f2', 'f4', 'f8':
867+
z = self.create_array(shape=10, chunks=3, dtype=dtype)
868+
assert z.dtype == np.dtype(dtype)
869+
a = np.linspace(0, 1, z.shape[0], dtype=dtype)
870870
z[:] = a
871871
assert_array_almost_equal(a, z[:])
872872

873-
# datetime, timedelta are not supported for the time being
874-
for resolution in 'D', 'us', 'ns':
875-
with assert_raises(ValueError):
876-
self.create_array(shape=10, dtype='datetime64[{}]'.format(resolution))
877-
with assert_raises(ValueError):
878-
self.create_array(shape=10, dtype='timedelta64[{}]'.format(resolution))
873+
# datetime, timedelta
874+
for base_type in 'Mm':
875+
for resolution in 'D', 'us', 'ns':
876+
dtype = '{}8[{}]'.format(base_type, resolution)
877+
z = self.create_array(shape=100, dtype=dtype)
878+
assert z.dtype == np.dtype(dtype)
879+
a = np.random.randint(0, np.iinfo('u8').max, size=z.shape[0],
880+
dtype='u8').view(dtype)
881+
z[:] = a
882+
assert_array_equal(a, z[:])
883+
884+
# check that datetime generic units are not allowed
885+
with assert_raises(ValueError):
886+
self.create_array(shape=100, dtype='M8')
887+
with assert_raises(ValueError):
888+
self.create_array(shape=100, dtype='m8')
879889

880890
def test_object_arrays(self):
881891

zarr/util.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,10 @@ def normalize_dtype(dtype, object_codec):
153153

154154
dtype = np.dtype(dtype)
155155

156-
if dtype.kind in 'mM':
157-
raise ValueError('datetime64 and timedelta64 dtypes are not currently '
158-
'supported; please store the data using int64 instead')
156+
# don't allow generic datetime64 or timedelta64, require units to be specified
157+
if dtype == np.dtype('M8') or dtype == np.dtype('m8'):
158+
raise ValueError('datetime64 and timedelta64 dtypes with generic units '
159+
'are not supported, please specify units (e.g., "M8[ns]")')
159160

160161
return dtype, object_codec
161162

0 commit comments

Comments
 (0)