Skip to content

Commit a919ea9

Browse files
committed
add packbits
1 parent e7e7ebb commit a919ea9

File tree

9 files changed

+145
-16
lines changed

9 files changed

+145
-16
lines changed

MANIFEST.in

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
recursive-include c-blosc *
2-
recursive-include numcodecs *.pxd
32
recursive-include numcodecs *.pyx
4-
recursive-include numcodecs *.h
53
recursive-include numcodecs *.c
64
include cpuinfo.py

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Contents
2929
lzma
3030
delta
3131
fixedscaleoffset
32+
packbits
3233
release
3334

3435
Acknowledgments

docs/packbits.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
PackBits
2+
========
3+
.. module:: numcodecs.packbits
4+
5+
.. autoclass:: PackBits

numcodecs/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@
2828

2929
from numcodecs.fixedscaleoffset import FixedScaleOffset
3030
register_codec(FixedScaleOffset)
31+
32+
from numcodecs.packbits import PackBits
33+
register_codec(PackBits)

numcodecs/abc.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ def get_config(self):
5757
codec. Must include an 'id' field with the codec ID. All values must be
5858
compatible with JSON encoding."""
5959
# override in sub-class if need special encoding of config values
60-
config = dict()
61-
config['id'] = self.codec_id
60+
config = dict(id=self.codec_id)
6261
# add in all non-private members
6362
for k in self.__dict__:
6463
if not k.startswith('_'):
@@ -75,10 +74,17 @@ def from_config(cls, config):
7574

7675
def __eq__(self, other):
7776
# override in sub-class if need special equality comparison
78-
attrs = ['codec_id']
79-
# compare all non-private members
80-
attrs += [k for k in self.__dict__ if not k.startswith('_')]
8177
try:
82-
return all([getattr(self, k) == getattr(other, k) for k in attrs])
78+
return self.get_config() == other.get_config()
8379
except AttributeError:
8480
return False
81+
82+
def __repr__(self):
83+
# override in sub-class if need special representation
84+
r = '%s(' % type(self).__name__
85+
# by default, include all non-private members
86+
params = ['%s=%r' % (k, getattr(self, k))
87+
for k in sorted(self.__dict__)
88+
if not k.startswith('_')]
89+
r += ', '.join(params) + ')'
90+
return r

numcodecs/bz2.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,3 @@ def decode(self, buf, out=None):
5050
# support direct decompression into buffer, so we have to copy into
5151
# out if given
5252
return buffer_copy(dec, out)
53-
54-
def __repr__(self):
55-
r = '%s(level=%s)' % (type(self).__name__, self.level)
56-
return r

numcodecs/packbits.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import, print_function, division
3+
4+
5+
import numpy as np
6+
7+
8+
from numcodecs.abc import Codec
9+
from numcodecs.compat import ndarray_from_buffer, buffer_copy
10+
11+
12+
class PackBits(Codec):
13+
"""Codec to pack elements of a boolean array into bits in a uint8 array.
14+
15+
Examples
16+
--------
17+
>>> import numcodecs as codecs
18+
>>> import numpy as np
19+
>>> codec = codecs.PackBits()
20+
>>> x = np.array([True, False, False, True], dtype=bool)
21+
>>> y = codec.encode(x)
22+
>>> y
23+
array([ 4, 144], dtype=uint8)
24+
>>> z = codec.decode(y)
25+
>>> z
26+
array([ True, False, False, True], dtype=bool)
27+
28+
Notes
29+
-----
30+
The first element of the encoded array stores the number of bits that
31+
were padded to complete the final byte.
32+
33+
"""
34+
35+
codec_id = 'packbits'
36+
37+
def __init__(self):
38+
pass
39+
40+
def encode(self, buf):
41+
42+
# view input as ndarray
43+
arr = ndarray_from_buffer(buf, bool)
44+
45+
# determine size of packed data
46+
n = arr.size
47+
n_bytes_packed = (n // 8)
48+
n_bits_leftover = n % 8
49+
if n_bits_leftover > 0:
50+
n_bytes_packed += 1
51+
52+
# setup output
53+
enc = np.empty(n_bytes_packed + 1, dtype='u1')
54+
55+
# store how many bits were padded
56+
if n_bits_leftover:
57+
n_bits_padded = 8 - n_bits_leftover
58+
else:
59+
n_bits_padded = 0
60+
enc[0] = n_bits_padded
61+
62+
# apply encoding
63+
enc[1:] = np.packbits(arr)
64+
65+
return enc
66+
67+
def decode(self, buf, out=None):
68+
69+
# view encoded data as ndarray
70+
enc = ndarray_from_buffer(buf, 'u1')
71+
72+
# find info how many bits were padded
73+
n_bits_padded = int(enc[0])
74+
75+
# apply decoding
76+
dec = np.unpackbits(enc[1:])
77+
78+
# remove padded bits
79+
if n_bits_padded:
80+
dec = dec[:-n_bits_padded]
81+
82+
# view as boolean array
83+
dec = dec.view(bool)
84+
85+
# handle destination
86+
return buffer_copy(dec, out)

numcodecs/tests/test_packbits.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import, print_function, division
3+
4+
5+
import numpy as np
6+
7+
8+
from numcodecs.packbits import PackBits
9+
from numcodecs.tests.common import check_encode_decode, check_config, \
10+
check_repr
11+
12+
13+
arrays = [
14+
np.random.randint(0, 2, size=1000, dtype=bool),
15+
np.random.randint(0, 2, size=(100, 10), dtype=bool),
16+
np.random.randint(0, 2, size=(10, 10, 10), dtype=bool),
17+
np.random.randint(0, 2, size=1000, dtype=bool).reshape(10, 10, 10,
18+
order='F'),
19+
]
20+
21+
22+
def test_encode_decode():
23+
codec = PackBits()
24+
for arr in arrays:
25+
check_encode_decode(arr, codec)
26+
# check different number of left-over bits
27+
arr = np.random.randint(0, 2, size=1000, dtype=bool)
28+
for size in list(range(1, 17)):
29+
check_encode_decode(arr[:size], codec)
30+
31+
32+
def test_config():
33+
codec = PackBits()
34+
check_config(codec)
35+
36+
37+
def test_repr():
38+
check_repr("PackBits()")

numcodecs/zlib.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,3 @@ def decode(self, buf, out=None):
4444
# support direct decompression into buffer, so we have to copy into
4545
# out if given
4646
return buffer_copy(dec, out)
47-
48-
def __repr__(self):
49-
r = '%s(level=%s)' % (type(self).__name__, self.level)
50-
return r

0 commit comments

Comments
 (0)