2010-10-31 16 views
12
  1. Qual è il (o la maggior parte "Pythonic") modo più veloce per convertirePython/Numpy: Convertire elenco di Caccio a unsigned int

    x = [False, False, True, True] 
    

    in 12? (Se c'è un modo.)

  2. E se di bool fosse invece ? C'è un comando speciale per questo?

Ho un grande array m-by-n di booleani, in cui ogni riga n-elemento rappresenta un singolo hash dimensionale ridotto di un vettore di caratteristiche altamente dimensionale. (Nell'esempio sopra, n = 4.) Vorrei conoscere la risposta per comprimere i miei dati il ​​più possibile. Grazie.


Edit: Grazie per le risposte! Utilizzando il seguente codice di prova,

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 

... ecco erano i tempi di esecuzione sul mio computer portatile Thinkpad:

Naturalmente, accolgo con favore eventuali test indipendenti che possono confermare o smentire i miei dati!


Edit: Nella mia risposta qui sotto, cambiando int(j) semplicemente j funziona ancora, ma corre sei volte più lento! Allora forse le altre risposte diventerebbero più veloci se la bool fosse lanciata usando int. Ma sono troppo pigro per testare di nuovo tutto.


Edit: Liori ha registrato risultati di test indipendenti here.

+0

Qual è la regola per convertire [False, False, True, True] in 12? –

+0

'x [0]' è l'LSB e 'x [-1]' è l'MSB. –

+2

Si prega di utilizzare 'timeit' per i test, è molto meno soggetto a errori. I miei tempi: http://pastebin.com/x1FEP9gY – liori

risposta

10

Prendendo varie idee da varie altre risposte, ecco un altro modo per farlo:

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

E 'abbastanza veloce nel mio test - a destra con il metodo numpy per un gran numero di bit anche se trabocca come un matto. Ho usato il modulo di test di Liori per il test. Il metodo di Steve, con il cambiamento che ho suggerito, è appena più veloce. Tuttavia, se un sacco di questi tipi di conversioni devono essere eseguiti alla volta (e con non troppi bit), scommetto che Numpy sarà più veloce.

+1

'sum (b << i per i, b in enumerate (x))' – kennytm

+0

@KennyTM. Intelligente ma l'ho profilata l'originale è circa il 20% più veloce. È il più veloce di gran lunga. – aaronasterling

1

Qualcosa di simile?

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

È possibile convertire un allineamento NumPy a un elenco regolare utilizzando un cast list().

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

'0 ** 0' è 1, quindi si ottiene un errore" fuori-per-uno "se il primo elemento è False. – liori

+0

@liori, non credo che si applichi al mio codice, dal momento che in realtà non lo faccio da nessuna parte? Comunque interessante, comunque. Non lo sapevo. –

+0

'int (False) * 2 == 0'. Il primo indice dato da 'enumerate' è' 0'. – liori

6

La maggior parte Pythonic potrebbe essere questo:

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

E 'difficile dire se è anche il più veloce.

In NumPy vorrei utilizzare

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

, ma questo non sarà più veloce per i piccoli array x, e non funzionerà per i grandi array x dal interi dimensioni della macchina sono utilizzati al posto di Pythons interi precisione arbitraria .

+0

Grazie! Per alcune dimensioni di array, la seconda soluzione ha funzionato abbastanza bene, ma per altri no. –

+0

@Steve - L'altro vantaggio della soluzione numpy è che puoi evitare di scorrere tra le righe. Usando l'array "' B' "dal tuo codice di test qui sopra:' numpy.sum (2 ** numpy.arange (B.shape [1]) * B, axis = 1) '. Questo dovrebbe dare una grande accelerazione rispetto all'iterazione su ogni riga dell'array ... Il ciclo completo di 500x viene eseguito in meno di un secondo sulla mia macchina ... –

+1

Poichè numpy non gestisce interi grandi come Python, hai stare attenti con numeri veramente grandi Se ci saranno numeri più grandi, puoi ottenere un po 'di più da questo metodo facendo 'dtype = numpy.longlong' in arange(). Inoltre, vi è una velocità molto, molto piccola usando il metodo sum dell'array numerico risultante piuttosto che usando numpy.sum. –

2

Un elegante, modo sempre lavorare pythonic è questo:

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)) 

Si noti che è possibile sbarazzarsi di powers (da enumerare e quadratura nella comprensione, come altre risposte fanno) - ma forse è più chiaro in questo modo.

+0

La tua risposta non dà la stessa risposta degli altri. Sostituendo 'bools' per' reverseed (bool) 'lo corregge. –

+0

@Justin Peel: vieni di nuovo? Ho già notato che poco dopo aver risposto e aggiunto 'invertito' ... – delnan

+0

prova il codice che hai qui con l'esempio dato dall'OP. Ottengo 3 come risposta quando dovrebbe essere 12. Non hai bisogno di mettere il 'invertito' in. –

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

Si potrebbe eliminare il contrario() se il bit meno significativo alla fine dell'array. Funziona anche con numpy.array e non ha bisogno enumerate(). Dai miei test sembra anche essere più veloce: non c'è bisogno di usare l'esponenziazione.

+0

Grazie per l'elegante soluzione! Sono stato spazzato via quando l'ho visto per la prima volta. Sfortunatamente, sembra correre il più lento, con o senza 'invertito'. Qualcuno potrebbe sapere perché? –

+0

@Steve: sul mio computer è più veloce della somma + esponenziazione. Cosa divertente ... quanti vettori usi? Verificate le prestazioni usando 'timeit'? – liori

2

Il mio primo tentativo, appena per riferimento:

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

Aspetta, questo è interessante: cambiare 'int (j)' in semplicemente 'j' funziona ancora, ma viene eseguito sei volte più lentamente! –

+3

Se si cambia 'int (j)' su 1, il tuo è il più veloce. –

+0

Aspetta ... duh! Grazie! Io sono stupido. –

0

Se si desidera aggiungere un'altra estensione al mix, ho aggiunto pack() e unpack() al ramo di sviluppo di gmpy. I miei test mostrano che potrebbe essere 2x o 3 volte più veloce.

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

Disclaimer: la versione di sviluppo si chiama gmpy2 e può coesistere con la versione stabile. È ancora in fase Alfa, ma si spera che diventi beta in poche settimane. È necessario disporre di librerie GMP e MPFR installate. La sorgente è disponibile presso http://code.google.com/p/gmpy/source/checkout

1

Se si dispone di una matrice, probabilmente avrete bisogno di fare in questo modo:

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

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

np.dot dovrebbe essere più veloce di qualsiasi ciclo in Python. Più veloce.

1

Stavo cercando ipython %timeit e sembra che facendo quanto segue è più veloce:

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

Inoltre, se il vettore booleano è un numpy.ndarray, convertendolo in pitone gamma x.tolist() e in esecuzione lo stesso sembra lavorare più velocemente in questo caso. È tutto marginale, ma costante e, a queste velocità, i margini si sommano bene.

1

numpy ha la funzione packbits per questo. Esso supporta anche le operazioni lungo gli assi:

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 

funziona per dimensioni int8 per taglie più grandi si deve spostare eo

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 

se x c'è multiplo di 8 dovete pad in zeri