2

I have a long 2D matrix of Numpy array object whose dimension is n x 12. Here is the first 10 rows of this matrix:

b = ([[0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0]], dtype=uint8)

What I want to do with this array is to convert it to unsigned integer. As far as I know the fastest way to do it is by using np.packbits function. However this function only packs 8 bits into integer while my array above has 12 bits in each row. What I expect when converting array above to unsigned integer are:

250, 248, 248, 250, 248, 248, 248, 248, 248, 248

Does any one know how get above result ? I also tried by np.packbits above by extending the bits to 16 (`.view('u2'), the result is still not as I expected. Any feedback would be appreciated. Thanks.

Javen
  • 67
  • 6
  • Are the first 4 columns always going to be full of zeros? – user2357112 Sep 12 '17 at 20:18
  • No, they aren't, example above is only the first 10 rows. So in some rows, they are filled with 1 or 0. The result I got above is by using Matlab command bi2de – Javen Sep 12 '17 at 20:20
  • So, you would need `uint16` to store the output values, right? – Divakar Sep 12 '17 at 20:34
  • Possible duplicate of [Binary numpy array to list of integers?](https://stackoverflow.com/questions/15505514/binary-numpy-array-to-list-of-integers) – Marco Sep 12 '17 at 20:44
  • @Divakar: It doesn't have to be in 'uint16', what I want to have is they are fit in 12 bits. – Javen Sep 12 '17 at 20:57
  • @Busy Beaver, probably not, the example you mentioned is 8 bits or less than 8 bits array converted to integers which seems no issue when using 'np.packbits' function. – Javen Sep 12 '17 at 20:59

2 Answers2

4

We could slice out the first 4 columns and last 8 columns and use np.packbits separately on those. Then, scale the first slice to account for them being the most-significant block among them and add with the second slice.

Hence, the implementation would be -

slice0 = np.packbits(b[:,:-8], axis=-1).astype(np.uint16) * 16
slice1 = np.packbits(b[:,-8:], axis=-1).astype(np.uint16)
out = slice0 + slice1

Alternatively, using sum-redcution with matrix-multiplication -

b.dot(2**np.arange(b.shape[1]-1,-1,-1))

Sample run -

In [1045]: b
Out[1045]: 
array([[0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0]], dtype=uint8)

In [1046]: slice0 = np.packbits(b[:,:-8], axis=-1).astype(np.uint16) * 16
      ...: slice1 = np.packbits(b[:,-8:], axis=-1).astype(np.uint16)
      ...: out = slice0 + slice1
      ...: 

In [1047]: out.ravel()
Out[1047]: array([1786,  248,  248,  250,  248,  248,  248,  248, 1272,  760])

In [1048]: b.dot(2**np.arange(b.shape[1]-1,-1,-1))
Out[1048]: array([1786,  248,  248,  250,  248,  248,  248,  248, 1272,  760])
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • Result is still not the same as I shown in my question above, I expect to get these unsigned integer number '250, 248, 248, 250, 248, 248, 248, 248, 248, 248'. – Javen Sep 12 '17 at 21:00
  • @Javen Well I added variety there by adding two 1s in row1 and one 1 in last row for `b`. So, my `b` is different and hence that different output. – Divakar Sep 12 '17 at 21:08
  • yes it works like a charm, but I need to reverse first each row to MSB-LSB. I prefer to use code In [1048], it works pretty fast. One more thing, I did some mistakes, the array above should be 11 bits not 12 bits as I put it in my question. Thanks again. – Javen Sep 13 '17 at 08:56
1

A generic solution to the problem would be

from numpy import *
b = ([[0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
      [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0]])

def bin2int(x):
    y = 0
    for i,j in enumerate(x):
        y += j<<i
    return y


result = [bin2int(x[::-1]) for x in b]

so you don't have to worry about how many bits anymore.

Marco
  • 2,007
  • 17
  • 28
  • thank you so much for your help. Yes, it works too but I need to reverse bit in each row so the order will be MSB-LSB. The bits I show in my question supposed to 11 bits, not 12 bits. With this 11 bits, it would produce the same result as I put in my question. – Javen Sep 13 '17 at 08:58