根据key翻译numpy数组中的每个元素

ctzwtxfj  于 2023-05-22  发布在  其他
关注(0)|答案(7)|浏览(122)

我试图根据给定的键翻译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)
sqxo8psd

sqxo8psd1#

我不知道效率如何,但你可以在字典的.get方法上使用np.vectorize

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

62lalag42#

下面是另一种方法,使用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中完成)来进行Map。因此,如果唯一元素的数量与数组的整体大小相当,则不会有加速。另一方面,如果只有几个独特的元素,那么您可以观察到高达x100的加速。

new9mtju

new9mtju3#

我认为最好迭代字典,并“一次”设置所有行和列的值:

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

编辑:

虽然它可能没有使用numpy.vectorizeDSM's (really good) answer那么性感,但我对所有建议方法的测试表明,这种方法(使用@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
z4bn682m

z4bn682m4#

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本身。
如果将可选的'missing' kwarg设置为'raise'(默认值为'ignore'),性能会稍微好一点,如果不是'a'的所有元素都存在于键中,则会得到KeyError。

wvyml7n5

wvyml7n55#

假设你的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。get矢量化大约需要5.8 sunique_translate大约需要8 s

可能的概括:

  • 如果你有负值要转换,你可以将a和字典键中的值移动一个常量,将它们Map回正整数:
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来加速这个功能。
9wbgstp7

9wbgstp76#

如果你真的不需要**使用字典作为替换表,简单的解决方案是(对于你的例子):

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具有带符号的整数。

4dc9hkyq

4dc9hkyq7#

充分利用 @DSM@John Vinyard 解决方案:

  • 仅对唯一值向量化dict.__getitem__
  • 使用numpy优化索引Map。

验证码:

>>> 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.vectorize(d.get)(u)[inv].reshape(a.shape)
array([[11, 22, 33],
       [33, 22, 11]])

这与 @DSM answer具有相同的优点,同时也避免了数组中唯一元素的python循环。

相关问题