2010-10-31 8 views
12
  1. ¿Cuál es la manera más rápida (o más "Pythonic") para convertirPython/Numpy: Convertir la lista de Bools a unsigned int

    x = [False, False, True, True] 
    

    en 12? (Si existe tal forma)

  2. ¿Qué pasaría si x fuera un numpy.array de bools? ¿Hay un comando especial para eso?

I tienen una gran variedad m-por-n de booleanos, donde cada fila de n elementos representa una sola de hash de pocas dimensiones de un vector de alta dimensión característica. (En el ejemplo anterior, n = 4.) Me gustaría saber la respuesta para comprimir mis datos tanto como sea posible. Gracias.


Edición: Gracias por las respuestas! Usando el siguiente código de prueba,

t = 0 
for iter in range(500): 
    B = scipy.signbit(scipy.randn(1000,20)) 
    for b in B: 
     t0 = time.clock() 
     # test code here 
     t1 = time.clock() 
     t += (t1-t0) 
print t 

... aquí eran los tiempos de ejecución en mi portátil Thinkpad:

Por supuesto, la bienvenida a cualquier pruebas independientes que pueden confirmar o refutar mis datos!


Edición: En mi respuesta a continuación, cambiando simplemente int(j)j todavía funciona, pero funciona seis veces más lento! Entonces, tal vez las otras respuestas serían más rápidas si el bool se lanzara usando int. Pero soy demasiado vago para probar todo de nuevo.


Edición: Liori publicado resultados de pruebas independientes here.

+0

¿Cuál es la regla para convertir [False, False, True, True] en 12? –

+0

'x [0]' es el LSB, y 'x [-1]' es el MSB. –

+2

Por favor use 'timeit' para probar, es mucho menos propenso a errores. Mis tiempos: http://pastebin.com/x1FEP9gY – liori

Respuesta

10

Tomando varias ideas de varias otras respuestas, aquí hay otra manera de hacerlo:

sum(1<<i for i, b in enumerate(x) if b) 

Es bastante rápido en mis pruebas - a la derecha con el método Numpy para una gran cantidad de bits, aunque se desborde como loco. Usé el módulo de prueba de liori para probarlo. El método de Steve, con el cambio que sugerí, es apenas más rápido. Sin embargo, si hay que hacer muchos de estos tipos de conversiones a la vez (y sin demasiados bits), apuesto a que el numpy será más rápido.

+1

'suma (b << i para i, b en enumerar (x))' – kennytm

+0

@KennyTM. Inteligente, pero lo perfilé, el original es aproximadamente un 20% más rápido. Es el más rápido de lejos. – aaronasterling

1

¿Algo como esto?

>>> x = [False, False, True, True] 
>>> sum([int(y[1])*2**y[0] for y in enumerate(x)]) 
12 

Puede convertir una matriz numpy a una lista regular usando un molde list().

>>> a = numpy.array([1,2,3,4]) 
>>> a 
array([1, 2, 3, 4]) 
>>> list(a) 
[1, 2, 3, 4] 
+1

'0 ** 0' es 1, por lo que se obtiene un error de uno por uno si el primer elemento es False. – liori

+0

@liori, no creo que eso se aplique a mi código, ya que en realidad no hago eso en ninguna parte. Aún así, interesante. No lo sabía –

+0

'int (False) * 2 == 0'. El primer índice dado por 'enumerate' es' 0'. – liori

6

más Pythonic podría ser la siguiente:

sum(2**i*b for i, b in enumerate(x)) 

Es difícil decir si es también el más rápido.

En numpy usaría

numpy.sum(2**numpy.arange(len(x))*x) 

pero esto no será más rápido para las pequeñas matrices x, y no va a trabajar para grandes conjuntos de números enteros desde x tamaño de la máquina se utilizan en lugar de pitones enteros de precisión arbitraria .

+0

Gracias! Para algunos tamaños de matriz, la segunda solución funcionó bastante bien, pero para otros no. –

+0

@Steve - La otra ventaja de la solución numpy es que puede evitar iterar a través de cada fila. Usando la matriz "' B' "del código de prueba anterior:' numpy.sum (2 ** numpy.arange (B.shape [1]) * B, axis = 1) '. Esto debería dar una gran aceleración en comparación con iterar sobre cada fila en la matriz ... El ciclo completo de 500x se ejecuta en menos de un segundo en mi máquina ... –

+1

Dado que numpy no maneja enteros grandes al igual que Python, tiene tener cuidado con números realmente grandes. Si hay números más grandes, puede obtener un poco más de este método haciendo 'dtype = numpy.longlong' en arange(). Además, hay una muy, muy pequeña aceleración mediante el uso del método de suma de la matriz numpy resultante en lugar de utilizar numpy.sum. –

2

Una forma elegante, Pythonic, siempre trabajo es la siguiente:

def powers(x): 
    """yield powers of x, starting from x**0 forever""" 
    power = 1 
    while True: 
     yield power 
     power *= x 

def bools_to_int(bools): 
    # in Python 2, use itertools.izip! 
    return sum(int(place) * place_weight for place_weight, place in 
       zip(powers(2), bools)) 

Tenga en cuenta que puede deshacerse de powers (por enumerar y cuadrar en la comprensión, como otras respuestas hacen) - pero tal vez está más claro de esta manera.

+0

Su respuesta no da la misma respuesta que las demás. Sustituir 'bools' por' reverse (bools) 'lo arregla. –

+0

@Justin Peel: ¿Vienes de nuevo? Ya me di cuenta de que poco después de responder y agregó 'invertido' ... – delnan

+0

pruebe el código que tiene aquí con el ejemplo dado por el OP. Obtengo 3 como respuesta cuando debería ser 12. No necesitas poner el 'invertido'. –

3
reduce(lambda a,b:2*a+b, reversed(x)) 

Puede deshacerse de reverseed() si tiene un bit menos significativo al final de la matriz. Esto también funciona con numpy.array, y no necesita enumerar(). De mis pruebas parece ser más rápido también: no es necesario usar exponenciación.

+0

¡Gracias por la solución elegante! Me quedé impresionado cuando lo vi por primera vez. Desafortunadamente, parece ejecutar el más lento, con o sin 'invertido'. ¿Alguien podría saber por qué? –

+0

@Steve: en mi computadora es más rápido que suma + exponenciación. Algo gracioso ... ¿cuánto tiempo usan los vectores? ¿Probas el rendimiento usando 'timeit'? – liori

2

Mi primer intento, apenas para la referencia:

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

Espera, esto es interesante: el cambio de 'int (j)' a simplemente 'j' todavía funciona, ¡pero se ejecuta seis veces más lento! –

+3

Si simplemente cambia 'int (j)' a 1, el suyo es el más rápido. –

+0

Espera ... ¡duh! ¡Gracias! Soy estúpido. –

0

Si está dispuesto a agregar otra extensión a la mezcla, agregué pack() y unpack() a la rama de desarrollo de gmpy. Mis pruebas muestran que puede ser 2x o 3x más rápido.

>>> import gmpy2 
>>> gmpy2.pack([0,0,1,1],1) 
mpz(12) 
>>> gmpy2.unpack(12,1) 
[mpz(0), mpz(0), mpz(1), mpz(1)] 

exención de responsabilidad: La versión de desarrollo se llama gmpy2 y puede coexistir con la versión estable. Todavía está en fase alfa, pero con suerte se convertirá en beta en unas pocas semanas. Necesita tener instaladas las bibliotecas GMP y MPFR. La fuente está disponible en http://code.google.com/p/gmpy/source/checkout

1

Si tiene una matriz, es probable que desee hacerlo de esta manera:

#precompute powers of two 
vals = 2.**np.arange(20) 

B = .... 
compressed = np.dot(B, vals) # matrix multiplication. 

np.dot debe ser más rápido que cualquier bucle en Python. Mucho mas rápido.

1

yo estaba tratando ipython %timeit y parece que haciendo lo siguiente es más rápido:

y = 0 
for i,j in enumerate(x): 
    if j: y += 1<<i 

Además, si su vector booleano es un numpy.ndarray, convirtiéndola en serie pitón x.tolist() y ejecutar el mismo parece trabaje más rápido en este caso. Todo es marginal, pero constante, y a estas velocidades, los marginales se suman bien.

1

numpy tiene la función packbits para esto. También es compatible con las operaciones a lo largo de ejes:

In [3]: B = scipy.signbit(scipy.randn(1000,8)).astype("i1") 

In [3]: B[0] 
Out[3]: array([0, 1, 0, 0, 0, 1, 0, 0], dtype=int8) 

In [4]: np.packbits(B[0]) 
Out[4]: array([68], dtype=uint8) 

In [5]: %timeit np.packbits(B, axis=1) 
10000 loops, best of 3: 37 µs per loop 

funciona para tamaños int8 para tamaños más grandes que tienen que cambiar y o

In [8]: x # multiple of 8 
Out[8]: array([1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1], dtype=int8) 

In [9]: r = np.packbits(x).astype(np.int32); r 
Out[9]: array([171, 129], dtype=uint8) 

In [10]: r[0] << 8 | r[1] 
Out[10]: 33237 

In [11]: sum(1<<i for i, b in enumerate(x[::-1]) if b) 
Out[11]: 33237 

si x no es múltiplo de 8 que tiene que pad en ceros