Skip to content

Commit e20afc2

Browse files
authored
Encode/Decode complex fill values (#363)
* Test that Zarr Arrays can contain complex data Implement a simple test to try constructing Zarr Arrays with complex data to see what issues crop up so as to fix them. * Test storing complex fill values in Zarr Arrays Provides a simple test to see if Zarr Arrays can handle storing a variety of complex typed fill values. * Handle encoding/decoding of complex fill values Simply places the real and imaginary values into a list with each one being encoded/decoded using the floating type used to represent them independently. This keeps the encoded complex fill values human readable, follows the existing floating value encoding/decoding rules, and represents the data in a way that JSON can easily represent it. * Note support of complex data in Zarr Arrays
1 parent a780691 commit e20afc2

File tree

4 files changed

+75
-0
lines changed

4 files changed

+75
-0
lines changed

docs/release.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ Bug fixes
5858
* Ensure ``DictStore`` contains only ``bytes`` to facilitate comparisons and protect against writes.
5959
By :user:`John Kirkham <jakirkham>`, :issue:`350`
6060

61+
* Test and fix an issue (w.r.t. fill values) when storing complex data to ``Array``.
62+
By :user:`John Kirkham <jakirkham>`, :issue:`363`
63+
6164
* Always use a ``tuple`` when indexing a NumPy ``ndarray``.
6265
By :user:`John Kirkham <jakirkham>`, :issue:`376`
6366

zarr/meta.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ def decode_fill_value(v, dtype):
163163
return np.NINF
164164
else:
165165
return np.array(v, dtype=dtype)[()]
166+
elif dtype.kind in 'c':
167+
v = (decode_fill_value(v[0], dtype.type().real.dtype),
168+
decode_fill_value(v[1], dtype.type().imag.dtype))
169+
v = v[0] + 1j * v[1]
170+
return np.array(v, dtype=dtype)[()]
166171
elif dtype.kind == 'S':
167172
# noinspection PyBroadException
168173
try:
@@ -201,6 +206,10 @@ def encode_fill_value(v, dtype):
201206
return int(v)
202207
elif dtype.kind == 'b':
203208
return bool(v)
209+
elif dtype.kind in 'c':
210+
v = (encode_fill_value(v.real, dtype.type().real.dtype),
211+
encode_fill_value(v.imag, dtype.type().imag.dtype))
212+
return v
204213
elif dtype.kind in 'SV':
205214
v = base64.standard_b64encode(v)
206215
if not PY2: # pragma: py2 no cover

zarr/tests/test_core.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,15 @@ def test_dtypes(self):
993993
z[:] = a
994994
assert_array_almost_equal(a, z[:])
995995

996+
# complex
997+
for dtype in 'c8', 'c16':
998+
z = self.create_array(shape=10, chunks=3, dtype=dtype)
999+
assert z.dtype == np.dtype(dtype)
1000+
a = np.linspace(0, 1, z.shape[0], dtype=dtype)
1001+
a -= 1j * a
1002+
z[:] = a
1003+
assert_array_almost_equal(a, z[:])
1004+
9961005
# datetime, timedelta
9971006
for base_type in 'Mm':
9981007
for resolution in 'D', 'us', 'ns':

zarr/tests/test_meta.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,60 @@ def test_encode_decode_array_2():
116116
assert [df.get_config()] == meta_dec['filters']
117117

118118

119+
def test_encode_decode_array_complex():
120+
121+
# some variations
122+
for k in ['c8', 'c16']:
123+
compressor = Blosc(cname='lz4', clevel=3, shuffle=2)
124+
dtype = np.dtype(k)
125+
fill_value = dtype.type(np.nan-1j)
126+
meta = dict(
127+
shape=(100, 100),
128+
chunks=(10, 10),
129+
dtype=dtype,
130+
compressor=compressor.get_config(),
131+
fill_value=fill_value,
132+
order=dtype.char,
133+
filters=[]
134+
)
135+
136+
meta_json = '''{
137+
"chunks": [10, 10],
138+
"compressor": {
139+
"id": "blosc",
140+
"clevel": 3,
141+
"cname": "lz4",
142+
"shuffle": 2,
143+
"blocksize": 0
144+
},
145+
"dtype": "%s",
146+
"fill_value": ["NaN", -1.0],
147+
"filters": [],
148+
"order": "%s",
149+
"shape": [100, 100],
150+
"zarr_format": %s
151+
}''' % (dtype.str, dtype.char, ZARR_FORMAT)
152+
153+
# test encoding
154+
meta_enc = encode_array_metadata(meta)
155+
assert_json_equal(meta_json, meta_enc)
156+
157+
# test decoding
158+
meta_dec = decode_array_metadata(meta_enc)
159+
assert ZARR_FORMAT == meta_dec['zarr_format']
160+
assert meta['shape'] == meta_dec['shape']
161+
assert meta['chunks'] == meta_dec['chunks']
162+
assert meta['dtype'] == meta_dec['dtype']
163+
assert meta['compressor'] == meta_dec['compressor']
164+
assert meta['order'] == meta_dec['order']
165+
# Based off of this SO answer: https://stackoverflow.com/a/49972198
166+
assert np.all(
167+
fill_value.view((np.uint8, fill_value.itemsize)) ==
168+
meta_dec['fill_value'].view((np.uint8, meta_dec['fill_value'].itemsize))
169+
)
170+
assert [] == meta_dec['filters']
171+
172+
119173
def test_encode_decode_array_datetime_timedelta():
120174

121175
# some variations

0 commit comments

Comments
 (0)