90

我正在尝试numpy.array根据给定的键翻译 a 的每个元素:

例如:

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 方式?

编辑:

我对它进行了计时,np.vectorizeDSM 提出的方法对于较大的数组要快得多:

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

6 回答 6

119

我不知道效率,但你可以使用字典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]])
于 2013-06-07T20:53:57.990 回答
22

这是另一种方法,使用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]])

np.vectorize当数组中唯一元素的数量很少时, 这种方法比方法快得多。说明: Python 很慢,在这种方法中,in-python 循环用于转换唯一元素,之后我们依靠极其优化的 numpy 索引操作(在 C 中完成)来进行映射。因此,如果唯一元素的数量与数组的整体大小相当,那么将不会有加速。另一方面,如果只有几个独特的元素,那么您可以观察到高达 x100 的加速。

于 2013-06-07T21:38:40.477 回答
10

我认为迭代字典会更好,并“一次”在所有行和列中设置值:

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

编辑:

虽然它可能不像DSM 的(非常好的)答案using那样性感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
于 2013-06-07T21:00:53.423 回答
8

numpy_indexed包(免责声明:我是它的作者)为此类问题提供了一个优雅而高效的矢量化解决方案

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

实现的方法类似于 John Vinyard 提到的方法,但更通用。例如,数组的项目不需要是整数,而是可以是任何类型,甚至是 nd-subarrays 本身。

如果您将可选的 'missing' kwarg 设置为 'raise' (默认为 'ignore'),性能会稍好一些,如果键中并非所有 'a' 元素都存在,您将收到 KeyError。

于 2016-07-26T18:27:49.467 回答
4

假设您的 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_translate8 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 来加速该功能。
于 2018-01-15T13:00:52.783 回答
2

If you don't really have to use dictionary as substitution table, simple solution would be (for your example):

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)

This will work of course only if indexes of d cover all possible values of your a, in other words, only for a with usigned integers.

于 2015-03-15T00:34:13.913 回答