2013-06-07 6 views
28

Ich versuche, jedes Element eines numpy.array nach einem bestimmten Schlüssel zu übersetzen:Übersetzen jedes Element in numpy Array nach Schlüssel

Zum Beispiel:

a = np.array([[1,2,3], 
       [3,2,4]]) 

my_dict = {1:23, 2:34, 3:36, 4:45} 

Ich möchte bekommen:

array([[ 23., 34., 36.], 
     [ 36., 34., 45.]]) 

kann ich sehen, wie es mit einer Schleife zu tun:

def loop_translate(a, my_dict): 
    new_a = np.empty(a.shape) 
    for i,row in enumerate(a): 
     new_a[i,:] = map(my_dict.get, row) 
    return new_a 

Gibt es einen effizienteren und/oder rein numpligen Weg?

Edit:

ich es zeitlich und np.vectorize Verfahren von DSM vorgeschlagen für größere Arrays ist wesentlich schneller:

In [13]: def loop_translate(a, my_dict): 
    ....:  new_a = np.empty(a.shape) 
    ....:  for i,row in enumerate(a): 
    ....:   new_a[i,:] = map(my_dict.get, row) 
    ....:  return new_a 
    ....: 

In [14]: def vec_translate(a, my_dict):  
    ....:  return np.vectorize(my_dict.__getitem__)(a) 
    ....: 

In [15]: a = np.random.randint(1,5, (4,5)) 

In [16]: a 
Out[16]: 
array([[2, 4, 3, 1, 1], 
     [2, 4, 3, 2, 4], 
     [4, 2, 1, 3, 1], 
     [2, 4, 3, 4, 1]]) 

In [17]: %timeit loop_translate(a, my_dict) 
10000 loops, best of 3: 77.9 us per loop 

In [18]: %timeit vec_translate(a, my_dict) 
10000 loops, best of 3: 70.5 us per loop 

In [19]: a = np.random.randint(1, 5, (500,500)) 

In [20]: %timeit loop_translate(a, my_dict) 
1 loops, best of 3: 298 ms per loop 

In [21]: %timeit vec_translate(a, my_dict) 
10 loops, best of 3: 37.6 ms per loop 

In [22]: %timeit loop_translate(a, my_dict) 
+2

Verwandte Frage: http://stackoverflow.com/questions/3403973/fast-replacement-of-values-in-a-numpy-array –

Antwort

33

Ich weiß nicht, über eine effiziente, aber Sie np.vectorize auf verwenden könnte Die .get Methode der Wörterbücher:

>>> a = np.array([[1,2,3], 
       [3,2,4]]) 
>>> my_dict = {1:23, 2:34, 3:36, 4:45} 
>>> np.vectorize(my_dict.get)(a) 
array([[23, 34, 36], 
     [36, 34, 45]]) 
+4

+1 wenn OP weiß, dass jeder Schlüssel in 'my_dict' enthalten sein wie in 'a', dann wäre' my_dict .__ getitem__' eine bessere Wahl – jamylak

+0

@Akavall: das ist seltsam. Ich habe 1.6.2 im Moment noch nicht um zu überprüfen. – DSM

+0

Wenn ich 'my_dict.get' verwende, bekomme ich einen ValueError, aber ich habe dieses Problem nicht, wenn ich' my_dict .__ getitem__' verwende. Ich benutze numpy 1.6.2 – Akavall

5

Ich denke, es wäre besser, ich Terate über das Wörterbuch, und Sollwerte in allen Zeilen und Spalten „auf einmal“:

>>> a = np.array([[1,2,3],[3,2,1]]) 
>>> a 
array([[1, 2, 3], 
     [3, 2, 1]]) 
>>> d = {1 : 11, 2 : 22, 3 : 33} 
>>> for k,v in d.iteritems(): 
...  a[a == k] = v 
... 
>>> a 
array([[11, 22, 33], 
     [33, 22, 11]]) 

Edit:

Es ist zwar nicht so sexy wie DSM's (really good) answernumpy.vectorize mit sein kann, meine Tests aller die vorgeschlagenen Verfahren zeigen, dass dieser Ansatz (@ jamylak Vorschlag verwenden) ist eigentlich ein bisschen schneller:

from __future__ import division 
import numpy as np 
a = np.random.randint(1, 5, (500,500)) 
d = {1 : 11, 2 : 22, 3 : 33, 4 : 44} 

def unique_translate(a,d): 
    u,inv = np.unique(a,return_inverse = True) 
    return np.array([d[x] for x in u])[inv].reshape(a.shape) 

def vec_translate(a, d):  
    return np.vectorize(d.__getitem__)(a) 

def loop_translate(a,d): 
    n = np.ndarray(a.shape) 
    for k in d: 
     n[a == k] = d[k] 
    return n 

def orig_translate(a, d): 
    new_a = np.empty(a.shape) 
    for i,row in enumerate(a): 
     new_a[i,:] = map(d.get, row) 
    return new_a 


if __name__ == '__main__': 
    import timeit 
    n_exec = 100 
    print 'orig' 
    print timeit.timeit("orig_translate(a,d)", 
         setup="from __main__ import np,a,d,orig_translate", 
         number = n_exec)/n_exec 
    print 'unique' 
    print timeit.timeit("unique_translate(a,d)", 
         setup="from __main__ import np,a,d,unique_translate", 
         number = n_exec)/n_exec 
    print 'vec' 
    print timeit.timeit("vec_translate(a,d)", 
         setup="from __main__ import np,a,d,vec_translate", 
         number = n_exec)/n_exec 
    print 'loop' 
    print timeit.timeit("loop_translate(a,d)", 
         setup="from __main__ import np,a,d,loop_translate", 
         number = n_exec)/n_exec 

Ausgänge:

orig 
0.222067718506 
unique 
0.0472617006302 
vec 
0.0357889199257 
loop 
0.0285375618935 
+0

Wenn man bedenkt, dass die Geschwindigkeit ein Problem sein könnte, würde das Iterieren wie 'for k in d' das so schnell wie möglich machen. – jamylak

+1

Ich finde, dass die Vektorisierung für meine Situation schneller ist, wo' a' Form hat (50, 50, 50) ' , 'd' hat 5000 Schlüssel und Daten sind' numpy.uint32'. Und es ist nicht super nah ... ~ 0,1 Sekunden vs ~ 1,4 Sekunden. Das Reduzieren des Arrays hilft nicht. :/ – grisaitis

5

Hier ist ein weiterer Ansatz, numpy.unique mit:

>>> a = np.array([[1,2,3],[3,2,1]]) 
>>> a 
array([[1, 2, 3], 
     [3, 2, 1]]) 
>>> d = {1 : 11, 2 : 22, 3 : 33} 
>>> u,inv = np.unique(a,return_inverse = True) 
>>> np.array([d[x] for x in u])[inv].reshape(a.shape) 
array([[11, 22, 33], 
     [33, 22, 11]]) 
+0

Wie vergleicht dies die Geschwindigkeit mit der Verwendung von 'vectorize (dict.get)'? – grisaitis

+0

Addendum - ich fand diese eine die schnellste (im Vergleich zu Vectoring dictionary.get und Iteration durch Schlüssel)! ymmv ... – grisaitis

+1

Ich würde eine kleine Änderung vornehmen, die 'd [x]' durch 'd.get (x, default_value)' ersetzt, wobei 'default_value' sein kann, was immer Sie wollen. Für meinen Anwendungsfall ersetzte ich nur einige Werte und andere, die ich alleine lassen wollte, also habe ich 'd.get (x, x)'. – grisaitis

0

Wenn Sie müssen Verwendung Wörterbuch als Substitutionstabelle nicht wirklich tun, würden einfache Lösung (für Beispiel) sein:

a = numpy.array([your array]) 
my_dict = numpy.array([0, 23, 34, 36, 45])  # your dictionary as array 

def Sub (myarr, table) : 
    return table[myarr] 

values = Sub(a, my_dict) 

Dies funktioniert natürlich nur, wenn die Indizes d alle möglichen Werte Ihrer a abdecken, also nur für a mit usign ed ganze Zahlen.

2

Das numpy_indexed Paket (Disclaimer: Ich bin sein Autor) bietet eine elegante und effiziente vektorisiert Lösung für diese Art von Problem:

import numpy_indexed as npi 
remapped_a = npi.remap(a, list(my_dict.keys()), list(my_dict.values())) 

Die implementierte Methode zum Ansatz ähnelt erwähnt von John Vinyard, aber auch allgemeiner. Zum Beispiel müssen die Elemente des Arrays nicht ints sein, sondern können von jedem Typ sein, sogar nd-Sub-Arrays selbst.

Wenn Sie das optionale 'fehlende' kwarg auf 'raise' setzen (Standard ist 'ignore'), wird die Leistung etwas besser, und Sie erhalten einen KeyError, wenn nicht alle Elemente von 'a' in den Schlüsseln vorhanden sind .

1

Angenommen, Ihre dict Schlüssel sind positive ganze Zahlen, ohne große Lücken (ähnlich einem Bereich von 0 bis N), würden Sie besser konvertieren Sie Ihre Übersetzung dict in ein Array, dass my_array[i] = my_dict[i], und mit numpy Indexing für die Übersetzung .

Ein Code mit diesem Ansatz ist:

def direct_translate(a, d): 
    src, values = d.keys(), d.values() 
    d_array = np.arange(a.max() + 1) 
    d_array[src] = values 
    return d_array[a] 

Testen mit zufälligen Arrays:

N = 10000 
shape = (5000, 5000) 
a = np.random.randint(N, size=shape) 
my_dict = dict(zip(np.arange(N), np.random.randint(N, size=N))) 

Für diese Größen ich um 140 ms für diesen Ansatz erhalten. Die np.get-Vektorisierung dauert ungefähr 5.8 s und die unique_translate um 8 s.

Mögliche Verallgemeinerungen:

  • Wenn Sie negative Werte haben zu übersetzen, können Sie die Werte in a und in den Schlüsseln des Wörterbuchs durch einen konstanten verschieben könnte sie zurück positive ganze Zahlen zur Karte:

def direct_translate(a, d): # handles negative source keys 
    min_a = a.min() 
    src, values = np.array(d.keys()) - min_a, d.values() 
    d_array = np.arange(a.max() - min_a + 1) 
    d_array[src] = values 
    return d_array[a - min_a] 
  • Wenn die Quellschlüssel große Lücken aufweisen, würde die anfängliche Array-Erstellung Speicher verschwenden. Ich würde auf Cython zurückgreifen, um diese Funktion zu beschleunigen.
Verwandte Themen