import math
import numpy as np
from .abc import Codec
from .compat import ensure_ndarray, ndarray_copy
[docs]
class Quantize(Codec):
"""Lossy filter to reduce the precision of floating point data.
Parameters
----------
digits : int
Desired precision (number of decimal digits).
dtype : dtype
Data type to use for decoded data.
astype : dtype, optional
Data type to use for encoded data.
Examples
--------
>>> import numcodecs
>>> import numpy as np
>>> x = np.linspace(0, 1, 10, dtype='f8')
>>> x
array([0. , 0.11111111, 0.22222222, 0.33333333, 0.44444444,
0.55555556, 0.66666667, 0.77777778, 0.88888889, 1. ])
>>> codec = numcodecs.Quantize(digits=1, dtype='f8')
>>> codec.encode(x)
array([0. , 0.125 , 0.25 , 0.3125, 0.4375, 0.5625, 0.6875,
0.75 , 0.875 , 1. ])
>>> codec = numcodecs.Quantize(digits=2, dtype='f8')
>>> codec.encode(x)
array([0. , 0.109375 , 0.21875 , 0.3359375, 0.4453125,
0.5546875, 0.6640625, 0.78125 , 0.890625 , 1. ])
>>> codec = numcodecs.Quantize(digits=3, dtype='f8')
>>> codec.encode(x)
array([0. , 0.11132812, 0.22265625, 0.33300781, 0.44433594,
0.55566406, 0.66699219, 0.77734375, 0.88867188, 1. ])
See Also
--------
numcodecs.fixedscaleoffset.FixedScaleOffset
"""
codec_id = 'quantize'
def __init__(self, digits, dtype, astype=None):
self.digits = digits
self.dtype = np.dtype(dtype)
if astype is None:
self.astype = self.dtype
else:
self.astype = np.dtype(astype)
if self.dtype.kind != 'f' or self.astype.kind != 'f':
raise ValueError('only floating point data types are supported')
[docs]
def encode(self, buf):
# normalise input
arr = ensure_ndarray(buf).view(self.dtype)
# apply scaling
precision = 10.0**-self.digits
exp = math.log10(precision)
if exp < 0:
exp = int(math.floor(exp))
else:
exp = int(math.ceil(exp))
bits = math.ceil(math.log2(10.0**-exp))
scale = 2.0**bits
enc = np.around(scale * arr) / scale
# cast dtype
enc = enc.astype(self.astype, copy=False)
return enc
[docs]
def decode(self, buf, out=None):
# filter is lossy, decoding is no-op
dec = ensure_ndarray(buf).view(self.astype)
dec = dec.astype(self.dtype, copy=False)
return ndarray_copy(dec, out)
[docs]
def get_config(self):
# override to handle encoding dtypes
return dict(
id=self.codec_id,
digits=self.digits,
dtype=self.dtype.str,
astype=self.astype.str,
)
def __repr__(self):
r = f'{type(self).__name__}(digits={self.digits}, dtype={self.dtype.str!r}'
if self.astype != self.dtype:
r += f', astype={self.astype.str!r}'
r += ')'
return r