Convierta un entero a una matriz binaria con un relleno adecuado

Tengo enteros en el rango de 0..2**m - 1 y me gustaría convertirlos a matrices de números m binarios de longitud m . Por ejemplo, digamos m = 4 . Ahora 15 = 1111 en binario, por lo que la salida debería ser (1,1,1,1) . 2 = 10 en binario y, por lo tanto, la salida debería ser (0,0,1,0 ). Si m fuera 3 entonces 2 debería convertir a (0,1,0) .

Intenté np.unpackbits(np.uint8(num)) pero eso no da una matriz de la longitud correcta. Por ejemplo,

 np.unpackbits(np.uint8(15)) Out[5]: array([0, 0, 0, 0, 1, 1, 1, 1], dtype=uint8) 

Me gustaría un método que funcionara para lo que tengo en el código.

Deberías poder vectorizar esto, algo así como

 >>> d = np.array([1,2,3,4,5]) >>> m = 8 >>> (((d[:,None] & (1 << np.arange(m)))) > 0).astype(int) array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [1, 0, 1, 0, 0, 0, 0, 0]]) 

que solo obtiene los pesos de bits apropiados y luego toma los bits y:

 >>> (1 << np.arange(m)) array([ 1, 2, 4, 8, 16, 32, 64, 128]) >>> d[:,None] & (1 << np.arange(m)) array([[1, 0, 0, 0, 0, 0, 0, 0], [0, 2, 0, 0, 0, 0, 0, 0], [1, 2, 0, 0, 0, 0, 0, 0], [0, 0, 4, 0, 0, 0, 0, 0], [1, 0, 4, 0, 0, 0, 0, 0]]) 

Hay muchas formas de convertir esto a 1s donde no sea cero (> 0)*1 , .astype(bool).astype(int) , etc. Elegí una básicamente al azar.

Versión de una línea, aprovechando la ruta rápida en numpy.binary_repr :

 def bin_array(num, m): """Convert a positive integer num into an m-bit bit vector""" return np.array(list(np.binary_repr(num).zfill(m))).astype(np.int8) 

Ejemplo:

 In [1]: bin_array(15, 6) Out[1]: array([0, 0, 1, 1, 1, 1], dtype=int8) 

Versión vectorizada para expandir una matriz numpy completa de entradas a la vez:

 def vec_bin_array(arr, m): """ Arguments: arr: Numpy array of positive integers m: Number of bits of each integer to retain Returns a copy of arr with every element replaced with a bit vector. Bits encoded as int8's. """ to_str_func = np.vectorize(lambda x: np.binary_repr(x).zfill(m)) strs = to_str_func(arr) ret = np.zeros(list(arr.shape) + [m], dtype=np.int8) for bit_ix in range(0, m): fetch_bit_func = np.vectorize(lambda x: x[bit_ix] == '1') ret[...,bit_ix] = fetch_bit_func(strs).astype("int8") return ret 

Ejemplo:

 In [1]: vec_bin_array(np.array([[100, 42], [2, 5]]), 8) Out[1]: array([[[0, 1, 1, 0, 0, 1, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0]], [[0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 1]]], dtype=int8) 

Parece que puedes modificar la matriz resultante. No conozco exactamente la función, pero la mayoría de las implementaciones como np.unpackbits no sabrían de forma inherente el tamaño del número; después de todo, las np.unpackbits python pueden ser arbitrariamente grandes y no tienen un tamaño nativo.

Sin embargo, si conoce m , puede “arreglar” fácilmente la matriz. Básicamente, una función de desempaquetar le dará un número de bits (que es un múltiplo de 8) para el byte con el 1 más alto en el número. Solo necesitas eliminar los 0s adicionales, o los 0s prefabricados, para obtener la distancia correcta:

 m = 4 mval = np.unpackbits(np.uint8(15)) if len(mval) > m: mval = mval[m-len(mval):] elif m > len(mval): # Create an extra array, and extend it mval = numpy.concatenate([numpy.array([0]*(m-len(mval)), dtype=uint8), mval]) 

Aquí hay una solución un tanto ‘hacky’.

 def bin_array(num, m): """Returns an array representing the binary representation of num in m bits.""" bytes = int(math.ceil(m / 8.0)) num_arr = np.arange(num, num+1, dtype='>i%d' %(bytes)) return np.unpackbits(num_arr.view(np.uint8))[-1*m:]