Codificación del termómetro Numpy

Estoy tratando de usar numerosas funciones integradas optimizadas para generar la encoding del termómetro. La encoding del termómetro básicamente genera n cantidad si 1 es en una longitud dada. Por ejemplo, en 8 de longitud, 3 se codificarán como:

1 1 1 0 0 0 0 0 

Usar numpy para generar ese vector basado en una entrada de entero es básicamente cortar y configurar 1.

 stream[:num_ones] = 1 

Entonces a mi pregunta se le da un vector como entrada, cuál será la mejor manera de generar una salida de matriz, por ejemplo:

 [2 3 4 1] 

como entrada debe producir:

 [[1 1 0 0 0 0 0 0], [1 1 1 0 0 0 0 0], [1 1 1 1 0 0 0 0], [1 0 0 0 0 0 0 0]] 

Mi solución actual es iterar sobre una matriz cero del tamaño requerido y establecer el número requerido de elementos en 1 usando el método de corte que escribí anteriormente. ¿Hay una manera más rápida para hacer esto?

Nunca había escuchado sobre la “encoding del termómetro” antes, pero cuando te das cuenta de que es tan similar a la encoding de un solo calor, queda claro que puedes llegar usando las operaciones de cambio de bits:

 >>> a = np.array([2, 3, 4, 1], dtype=np.uint8) >>> print(np.fliplr(np.unpackbits((1 << a) - 1).reshape(-1,8))) [[1 1 0 0 0 0 0 0] [1 1 1 0 0 0 0 0] [1 1 1 1 0 0 0 0] [1 0 0 0 0 0 0 0]] 

Edición: puede generalizar la idea a enteros de tamaño arbitrario trabajando en 8 fragmentos de columna:

 a = np.array([2, 13, 4, 0, 1, 17], dtype=np.uint8) out = np.empty((len(a), 0), dtype=np.uint8) while a.any(): block = np.fliplr(np.unpackbits((1 << a) - 1).reshape(-1,8)) out = np.concatenate([out, block], axis=1) a = np.where(a<8, 0, a-8) print(out) [[1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0] [1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0]] 
 In [22]: x = [2, 3, 4, 1, 0, 8] In [23]: length = 8 In [24]: (np.arange(length) < np.array(x).reshape(-1, 1)).astype(int) Out[24]: array([[1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1]]) 

O bien, cree una matriz de las distintas longitudes de "barras":

 In [46]: k = np.arange(length + 1) In [47]: bars = (k[:-1] < k.reshape(-1, 1)).astype(int) In [48]: bars Out[48]: array([[0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1]]) 

y usarlo como una tabla de búsqueda:

 In [49]: bars[x] Out[49]: array([[1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1]]) 

En el código anterior, las bars matriz preasignadas tienen forma (length+1, length) . Una representación de bars más eficiente en memoria se puede crear usando:

 In [61]: from numpy.lib.stride_tricks import as_strided In [62]: u = np.zeros(2*length, dtype=int) In [63]: u[length:] = 1 In [64]: bars = as_strided(u[length-1:], shape=(length+1, length), strides=(u.strides[0], -u.strides[0])) In [65]: bars Out[65]: array([[0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1]]) 

Luego las bars es una vista de la matriz unidimensional u , y solo usa enteros de 2*length .

La respuesta de Wim es increíble. Tampoco he oído hablar de la encoding del termómetro, pero si tuviera que hacerlo, iría con el mapa. Es simplemente más corto sin la solución de bucle. El rendimiento es bastante similar.

 >>> def setValue(val): return np.append(np.ones(val), np.zeros(8-val)) >>> np.array(list(map(setValue, [2,3,4,5]))) array([[ 1., 1., 0., 0., 0., 0., 0., 0.], [ 1., 1., 1., 0., 0., 0., 0., 0.], [ 1., 1., 1., 1., 0., 0., 0., 0.], [ 1., 1., 1., 1., 1., 0., 0., 0.]]) 

o una sola línea con la función lambda

 >>> np.array(list(map(lambda v: np.append(np.ones(v), np.zeros(8-v)), [1,6,3,8]))) array([[ 1., 0., 0., 0., 0., 0., 0., 0.], [ 1., 1., 1., 1., 1., 1., 0., 0.], [ 1., 1., 1., 0., 0., 0., 0., 0.], [ 1., 1., 1., 1., 1., 1., 1., 1.]]) 

no muy diferente, listcomp dentro de una función de creación de matriz

 temps = [1,2,4,1] tlen = 8 np.stack([np.pad(np.ones(t), (0, tlen-t), 'constant') for t in temps]) Out[66]: array([[ 1., 0., 0., 0., 0., 0., 0., 0.], [ 1., 1., 0., 0., 0., 0., 0., 0.], [ 1., 1., 1., 1., 0., 0., 0., 0.], [ 1., 0., 0., 0., 0., 0., 0., 0.]])