Search code examples
pythonarraysnumpyclip

Converting positive and negative values to a bitstring using numpy.clip


I want to efficiently convert values from a list (or numpy array) into a numpy array of bits: A negative value should become a 0 in the new array, and a positive value a 1 in the new array.

E.g.,

>> import numpy as np
>> np.clip([1,2,3,-1,-2],  a_min=0, a_max=1)
array([1, 1, 1, 0, 0])

However, if I have floats in the list, this method keeps them as is:

>> np.clip([1,0.45,3,-1,-2],  a_min=0, a_max=1)
array([ 1.  ,  0.45,  1.  ,  0.  ,  0.  ])

Is there a good way of circumventing this behavior? One way would be to round the values. But I would want everything that is positive assigned a 1. If I would use np.around(), this would round 0.45 -> 0.


Solution

  • To map everything greater than 0 to 1 (and everything less to 0) you could use np. where:

    In [25]: np.where(np.array([1,0.45,3,-1,-2]) > 0, 1, 0)
    Out[25]: array([1, 1, 1, 0, 0])
    

    or

    In [29]: (np.array([1,0.45,3,-1,-2]) > 0).astype('i1')
    Out[29]: array([1, 1, 1, 0, 0], dtype=int8)
    

    Note that np.where is returning an array with dtype int32 (4-byte ints), while astype('i1') is returning an array with dtype int8 (1-byte ints).

    If you wish to pack these binary values into a uint8, you could use np.packbits:

    In [48]: x = np.array([1,0.45,3,-1,-2])
    
    In [49]: np.packbits((x > 0).astype('i1'))
    Out[49]: array([224], dtype=uint8)
    
    In [50]: bin(224)
    Out[50]: '0b11100000'
    

    Or, as a string:

    In [60]: np.packbits((x > 0).astype('i1')).tostring()
    Out[60]: '\xe0'
    
    In [62]: bin(0xe0)
    Out[62]: '0b11100000'