根据键转换numpy数组中的每个元素

时间:2013-06-07 20:49:13

标签: python 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方式?

修改

我计时了,DSM提出的np.vectorize方法对于更大的数组要快得多:

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)

6 个答案:

答案 0 :(得分:53)

我不知道有效率,但您可以在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]])

答案 1 :(得分:9)

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

答案 2 :(得分:4)

我认为迭代字典会更好,并且“立刻”设置所有行和列中的值:

>>> 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's (really good) answer可能不如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

答案 3 :(得分:4)

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

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

实施的方法类似于John Vinyard提到的方法,但更为一般。例如,数组的项不需要是int,但可以是任何类型,甚至是nd-subarrays本身。

如果您设置了可选的'缺失' kwarg'筹集' (默认为'忽略'),性能稍好一些,如果不是' a'的所有元素,您将获得KeyError。钥匙中有。

答案 4 :(得分:2)

如果您不是必须使用字典作为替换表,那么简单的解决方案就是(对于您的示例):

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带有整数的整数时,这当然有效。

答案 5 :(得分:2)

假设你的dict键是正整数,没有巨大的间隙(类似于从0到N的范围),你最好将翻译字典转换为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 sunique_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来加速这个功能。