2013-06-07 9 views
28

próbuję tłumaczyć każdy element jest numpy.array według określonego klucza:Przekłada każdy element numpy tablicy według klucza

Na przykład:

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

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

chcę uzyskać:

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

mogę zobaczyć, jak to zrobić z pętlą:

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 

Czy istnieje skuteczniejszy i/lub czysty sposób numpy?

Edit:

I timed to i np.vectorize metoda zaproponowana przez DSM jest znacznie szybszy w przypadku większych tablic:

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

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

Odpowiedz

33

nie wiem o wydajne, ale można użyć na np.vectorize metoda słownikowa .get:

>>> 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 jeśli PO wie każdy klawisz zostanie zawarte w 'my_dict' jak w 'a', wtedy' my_dict .__ getitem__' będzie lepszym wyborem – jamylak

+0

@Akavall: to dziwne. Nie mam jednak w tej chwili wersji 1.6.2, żeby to sprawdzić. – DSM

+0

Kiedy używam 'my_dict.get' otrzymuję ValueError, ale nie mam tego problemu, gdy używam' my_dict .__ getitem__'. Używam numpy 1.6.2 – Akavall

5

Myślę, że byłoby lepiej terate nad słownika, a zbiór wartości we wszystkich wierszy i kolumn „na raz”:

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

Chociaż nie może być tak sexy jak DSM's (really good) answer użyciu numpy.vectorize, moje testy wszystkich proponowane metody pokazują, że takie podejście (stosując sugestię @ jamylak za) jest rzeczywiście nieco szybciej:

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 

Wyjścia:

orig 
0.222067718506 
unique 
0.0472617006302 
vec 
0.0357889199257 
loop 
0.0285375618935 
+0

Zważywszy, że prędkość może być problemem, iterowanie jak 'dla k in d' uczyniłoby to tak szybko jak to możliwe ' – jamylak

+1

Uważam, że wektoryzacja jest szybsza w mojej sytuacji, gdzie' a' ma kształt '(50, 50, 50)' 'd' ma 5000 kluczy, a dane to' numpy.uint32'. I nie jest super blisko ... ~ 0,1 sekundy vs ~ 1,4 sekundy. spłaszczenie tablicy nie pomaga. :/ – grisaitis

5

Oto inne podejście, używając numpy.unique:

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

W jaki sposób porównuje to szybkość z użyciem 'wektoryzacji (dict.get)'? – grisaitis

+0

Dodatek - uważam, że ten jest najszybszy (w porównaniu do wektorowania dictionary.get i iteracji za pomocą klawiszy)! ymmv ... – grisaitis

+1

Wprowadziłbym jedną drobną modyfikację, która ma zastąpić 'd [x]' przez 'd.get (x, default_value)', gdzie 'default_value' może być czymkolwiek chcesz. Dla mojego przypadku użycia, zastępowałem tylko niektóre wartości, a inne, które chciałem zostawić w spokoju, więc zrobiłem 'd.get (x, x)'. – grisaitis

0

Jeśli naprawdę nie trzeba słowniku wykorzystanie jako stołu substytucyjnego, prostym rozwiązaniem byłoby (dla przykładu):

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) 

To zadziała oczywiście tylko wtedy, gdy indeksy d pokrywają wszystkie możliwe wartości twojego a, innymi słowy, tylko dla a z usign ed liczby całkowite.

2

Pakiet numpy_indexed (disclaimer: Jestem jego autorem) i oferuje eleganckie i wydajne vectorized rozwiązanie tego typu problemu:

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

Sposób realizowany jest podobne do podejścia wspomnianej przez Jana Vinyard, ale nawet bardziej ogólne. Na przykład elementy tablicy nie muszą być typu int, ale mogą być dowolnym typem, nawet same podwarstwy nd.

Jeśli ustawisz opcjonalny "brakujący" kwarg na "przebicie" (domyślnie jest to "ignoruj"), wydajność będzie nieco lepsza, a otrzymasz KeyError, jeśli nie wszystkie elementy "a" są obecne w kluczach .

1

Zakładając, że twoje klucze dyktujące są dodatnimi liczbami całkowitymi, bez dużych przerw (podobnie jak w zakresie od 0 do N), lepiej byłoby przekonwertować swój słownikowy dyktat na tablicę taką, że my_array[i] = my_dict[i], i używając numpy indeksowania, aby wykonać tłumaczenie .

Kod użyciu tego podejścia jest:

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] 

Testowanie z przypadkowymi tablic:

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

Dla tych rozmiarów uzyskać wokół 140 ms dla tego podejścia. Wektoryzacja np.get zajmuje około 5.8 s i unique_translate około 8 s.

Możliwe uogólnienia:

  • Jeśli masz wartości ujemne tłumaczyć, można przesunąć wartości a i klucze słownika przez stałą mapować je z powrotem do dodatnich liczb całkowitych:

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] 
  • Jeśli klucze źródłowe mają duże odstępy, początkowe tworzenie macierzy spowoduje marnowanie pamięci. Uciekałbym do cytonu, aby przyspieszyć tę funkcję.
Powiązane problemy