python - functions - Traduzca cada elemento en numpy array de acuerdo a la clave
numpy functions (6)
Estoy tratando de traducir cada elemento de numpy.array
acuerdo con una clave dada:
Por ejemplo:
a = np.array([[1,2,3],
[3,2,4]])
my_dict = {1:23, 2:34, 3:36, 4:45}
Quiero tener:
array([[ 23., 34., 36.],
[ 36., 34., 45.]])
Puedo ver cómo hacerlo con un bucle:
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
¿Hay una forma numpy más eficiente y / o pura?
Editar:
Lo sincronicé y el método np.vectorize
propuesto por DSM es considerablemente más rápido para matrices más grandes:
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)
Aquí hay otro enfoque, usando 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]])
Asumiendo que las claves dict son enteros positivos, sin grandes huecos (similar a un rango de 0 a N), sería mejor que convirtieras tu traducción dict a una matriz tal que my_array[i] = my_dict[i]
, y usando indización numpy para hacer la traducción
Un código que utiliza este enfoque es:
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]
Prueba con matrices aleatorias:
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)))
Para estos tamaños obtengo alrededor de 140 ms
para este enfoque. La vectorización np.get toma alrededor de 5.8 s
la unique_translate
alrededor de 8 s
.
Posibles generalizaciones:
- Si tiene valores negativos para traducir, puede cambiar los valores en ay en las teclas del diccionario por una constante para volver a asignarlos a números enteros positivos:
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]
- Si las claves de origen tienen enormes lagunas, la creación inicial de la matriz perdería la memoria. Yo recurriría a cython para acelerar esa función.
Creo que sería mejor iterar sobre el diccionario y establecer valores en todas las filas y columnas "a la vez":
>>> 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]])
Editar:
Si bien puede no ser tan sexy como la respuesta de DSM (realmente buena) usando numpy.vectorize
, mis pruebas de todos los métodos propuestos muestran que este enfoque (usando la sugerencia de @ jamylak) es en realidad un poco más rápido:
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
Productos:
orig
0.222067718506
unique
0.0472617006302
vec
0.0357889199257
loop
0.0285375618935
El paquete numpy_indexed (descargo de responsabilidad: soy su autor) proporciona una solución vectorizada elegante y eficiente para este tipo de problema:
import numpy_indexed as npi
remapped_a = npi.remap(a, list(my_dict.keys()), list(my_dict.values()))
El método implementado es similar al enfoque mencionado por John Vinyard, pero aún más general. Por ejemplo, los elementos de la matriz no necesitan ser enteros, pero pueden ser de cualquier tipo, incluso los subdramas nd mismos.
Si configura el kwarg ''faltante'' opcional para ''subir'' (el valor predeterminado es ''ignorar''), el rendimiento será ligeramente mejor, y obtendrá un KeyError si no todos los elementos de ''a'' están presentes en las teclas.
No sé acerca de efficient, pero usted podría usar np.vectorize
en el .get
method of dictionaries:
>>> 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]])
Si realmente no tiene que usar el diccionario como tabla de sustitución, una solución simple sería (para su ejemplo):
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)
Esto funcionará, por supuesto, solo si los índices de d
cubren todos los valores posibles de su a
, en otras palabras, solo para los enteros con signo.