Переведите каждый элемент в массив numpy в соответствии с ключом
Я пытаюсь перевести каждый элемент numpy.array
в соответствии с заданным ключом:
Например:
a = np.array([[1,2,3],
[3,2,4]])
my_dict = {1:23, 2:34, 3:36, 4:45}
Я хочу получить:
array([[ 23., 34., 36.],
[ 36., 34., 45.]])
Я вижу, как это сделать с помощью цикла:
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
Есть ли более эффективный и/или чистый способ numpy?
Edit:
Я приурочил это время, а метод np.vectorize
, предложенный DSM, значительно быстрее для больших массивов:
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)
Ответы
Ответ 1
Я не знаю об эффективности, но вы можете использовать np.vectorize
в методе .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]])
Ответ 2
Здесь другой подход, используя 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]])
Ответ 3
Я думаю, что было бы лучше перебирать словарь и устанавливать значения во всех строках и столбцах "сразу":
>>> 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:
Хотя это может быть не так сексуально, как DSM (действительно хороший) ответ, используя numpy.vectorize
, мои тесты всех предложенных методов показывают, что этот подход (с использованием предложения @jamylak) на самом деле немного быстрее:
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
Выходы:
orig
0.222067718506
unique
0.0472617006302
vec
0.0357889199257
loop
0.0285375618935
Ответ 4
Пакет numpy_indexed (отказ от ответственности: я являюсь его автором) предоставляет элегантное и эффективное векторизованное решение этой проблемы:
import numpy_indexed as npi
remapped_a = npi.remap(a, list(my_dict.keys()), list(my_dict.values()))
Реализованный метод похож на подход, упомянутый Джоном Винярдом, но еще более общий. Например, элементы массива не обязательно должны быть int, но могут быть любыми типами, даже самими nd-subarrays.
Если вы установите необязательный "недостающий" kwarg в "raise" (по умолчанию "ignore" ), производительность будет немного лучше, и вы получите KeyError, если не все элементы "a" присутствуют в ключах.
Ответ 5
Если вы действительно не должны использовать словарь в качестве таблицы подстановок, простым решением будет (для вашего примера):
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)
Это будет работать, конечно, только если индексы d
охватывают все возможные значения вашего a
, другими словами, только для a
с введенными целыми числами.
Ответ 6
Предполагая, что ваши ключи dict являются положительными целыми числами, без огромных пробелов (аналогично диапазону от 0 до N), вам лучше было бы преобразовать ваш перевод dict в массив таким образом, чтобы my_array[i] = my_dict[i]
и с помощью индексации numpy, чтобы выполнить перевод.
Код с использованием этого подхода:
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]
Тестирование со случайными массивами:
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)))
Для этих размеров я обойду 140 ms
для этого подхода. Энциклопедия np.get занимает около 5.8 s
и unique_translate
вокруг 8 s
.
Возможные обобщения:
- Если у вас есть отрицательные значения для перевода, вы можете сдвинуть значения в
a
и в ключах словаря константой, чтобы отобразить их обратно в положительные целые числа:
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]
- Если исходные ключи имеют огромные разрывы, первоначальное создание массива будет направлено на удаление памяти. Я бы прибегнул к cython, чтобы ускорить эту функцию.