Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Numpy:使用字典作为映射高效地替换二维数组中的值_Python_Arrays_Numpy_Dictionary - Fatal编程技术网

Python Numpy:使用字典作为映射高效地替换二维数组中的值

Python Numpy:使用字典作为映射高效地替换二维数组中的值,python,arrays,numpy,dictionary,Python,Arrays,Numpy,Dictionary,我有一个二维整数数组,如下所示: a = np.array([[ 3, 0, 2, -1], [ 1, 255, 1, 2], [ 0, 3, 2, 2]]) 我有一个包含整数键和值的字典,我想用新值替换a的值。格言可能是这样的: d = {0: 1, 1: 2, 2: 3, 3: 4, -1: 0, 255: 0} 我想用d中对应的值替换a中与d中的键匹配的值。换句话说,d定义了a中旧(当前)值和新

我有一个二维整数数组,如下所示:

a = np.array([[  3,   0,   2,  -1],
              [  1, 255,   1,   2],
              [  0,   3,   2,   2]])
我有一个包含整数键和值的字典,我想用新值替换
a
的值。格言可能是这样的:

d = {0: 1, 1: 2, 2: 3, 3: 4, -1: 0, 255: 0}
我想用
d
中对应的值替换
a
中与
d
中的键匹配的值。换句话说,
d
定义了
a
中旧(当前)值和新(期望)值之间的映射。上述玩具示例的结果如下:

a_new = np.array([[  4,   1,   3,   0],
                  [  2,   0,   2,   3],
                  [  1,   4,   3,   3]])
实现这一点的有效方法是什么

这是一个很有趣的例子,但实际上数组会很大,它的形状会是例如
(10242048)
,字典会有几十个元素(在我的例子中是34个),虽然键是整数,但它们不一定都是连续的,它们可以是负数(如上面的例子)

我需要在数十万个这样的阵列上执行此替换,所以它需要很快。然而,字典是预先知道的,并且保持不变,因此,渐进地,任何用于修改字典或将其转换为更合适的数据结构的时间都无关紧要

我目前正在两个嵌套的
for
循环中循环数组条目(在
a
的行和列上),但肯定有更好的方法


如果映射不包含负值(如示例中的-1),我只需从字典中创建一个列表或数组,其中键是数组索引,然后将其用于高效的Numpy索引例程。但是,由于也存在负值,这将不起作用。

复制数组,然后迭代字典项,然后使用布尔索引将新值分配给副本

import numpy as np
b = np.copy(a)
for old, new in d.items():
    b[a == old] = new

这里有一种方法,如果您有一个小的dictionary/min和max值,这可能会更有效,您可以通过添加数组min来解决负索引问题:

In [11]: indexer = np.array([d.get(i, -1) for i in range(a.min(), a.max() + 1)])

In [12]: indexer[(a - a.min())]
Out[12]:
array([[4, 1, 3, 0],
       [2, 0, 2, 3],
       [1, 4, 3, 3]])
注意:这会将for循环移动到查找表中,但如果它明显小于实际数组,则速度可能会快得多。

Numpy可以创建用于在数组上执行映射操作的对象。我不确定这里的哪个方法会有最好的性能,所以我用timeit来计时我的方法。如果您想找出性能最好的方法,我建议您尝试其他几种方法

# Function to be vectorized
def map_func(val, dictionary):
    return dictionary[val] if val in dictionary else val 

# Vectorize map_func
vfunc  = np.vectorize(map_func)

# Run
print(vfunc(a, d))
您可以通过执行以下操作来计时:

from timeit import Timer
t = Timer('vfunc(a, d)', 'from __main__ import a, d, vfunc')
print(t.timeit(number=1000))
这种方法的结果大约是0.014秒


编辑:为了好玩,我在
(10242048)
大小为-10到10的随机数的numpy数组上试用了这个方法,使用的是同一个字典。单个阵列大约需要四分之一秒的时间。除非您正在运行很多这样的阵列,否则如果这是一个可接受的性能级别,那么就不值得进行优化

这篇文章解决了数组键和字典键之间的一对一映射问题。这个想法类似于中提出的,但我们将创建一个更大的数组,其中包含了,从而使我们能够高效地简单地索引,而不需要对传入的输入数组进行任何补偿,这应该是这里值得注意的改进

要获取索引器(由于字典保持不变,这将是一次性使用),请使用-

def getval_array(d):
    v = np.array(list(d.values()))
    k = np.array(list(d.keys()))
    maxv = k.max()
    minv = k.min()
    n = maxv - minv + 1
    val = np.empty(n,dtype=v.dtype)
    val[k] = v
    return val

val_arr = getval_array(d)
要获得最终替换,只需索引即可。因此,对于输入数组
a
,请执行以下操作-

out = val_arr[a]
样本运行-

In [8]: a = np.array([[  3,   0,   2,  -1],
   ...:               [  1, 255,   1, -16],
   ...:               [  0,   3,   2,   2]])
   ...: 
   ...: d = {0: 1, 1: 2, 2: 3, 3: 4, -1: 0, 255: 0, -16:5}
   ...: 

In [9]: val_arr = getval_array(d) # one-time operation

In [10]: val_arr[a]
Out[10]: 
array([[4, 1, 3, 0],
       [2, 0, 2, 5],
       [1, 4, 3, 3]])
平铺样本数据的运行时测试-

In [141]: a = np.array([[  3,   0,   2,  -1],
     ...:               [  1, 255,   1, -16],
     ...:               [  0,   3,   2,   2]])
     ...: 
     ...: d = {0: 1, 1: 2, 2: 3, 3: 4, -1: 10, 255: 89, -16:5}
     ...: 

In [142]: a = np.random.choice(a.ravel(), 1024*2048).reshape(1024,2048)

# @Andy Hayden's soln
In [143]: indexer = np.array([d.get(i, -1) for i in range(a.min(), a.max() + 1)])

In [144]: %timeit indexer[(a - a.min())]
100 loops, best of 3: 8.34 ms per loop

# Proposed in this post
In [145]: val_arr = getval_array(d)

In [146]: %timeit val_arr[a]
100 loops, best of 3: 2.69 ms per loop

这基本上允许您从问题中删除“如果地图不包含负值”的条件。是的,这很有效!我想在复杂性方面,这类似于在所有字典项上循环并执行
a[a==key]=value
?有人在这里的另一个答案中提出了这一点,但奇怪地删除了它。你的解决方案的好处是,我只需要创建这个索引器一次,所以它的复杂性其实并不重要,即使字典是大的(其实不是)。@Alex我认为另一个的复杂性是“相似的”(因为它最适合小字典),我怀疑这两种方法的性能都可以,但是我怀疑这对于较大的数组会稍微好一些,因为它只需要3次传递。这是我的情况下最好的方法:因为字典保持不变,所有可能的数组值都是预先知道的,索引器只需要创建一次,然后就可以用于处理大量数组。如果索引器只需要创建一次,那么此方法的速度大约是@wwii提出的方法的6倍。如果需要为每个要处理的数组新创建索引器,那么我想它不会更快。我非常喜欢这个问题。两个想法:(1)用一个巧妙的麻木数组替换DICT,如安迪建议的那样(有一些其他方法可以构造索引器和/或通过函数和索引器来运行原始数据值)或者(2)考虑使用大熊猫系列/DATAFRAME,它有一些很好的替换方法,这些方法可能足够快。我将研究熊猫数据结构!可能重复的。。。(在我回答后发现的)。@wwii I对那里的数字并不十分信服,我想如果它是一个小的数字,那么肯定是的,但是如果它的元素只有原来的几倍,它的速度会慢得多。无论如何,我认为我们的两个答案是两个需要尝试的解决方案(根据您的dict/数据,其中一个会更快/最好):@Alex请查看我的更新解决方案,以利用这里的一对一映射案例-应该非常有效。
vectorize
的文档中说,“提供矢量化功能主要是为了方便,而不是为了性能。这个实现本质上是一个for循环“,但我会尝试一下!是的,经过测试,Andy使用索引器的方法表现得更好。我使用他的方法得到了0.014秒,而使用矢量化的方法得到了0.27秒。唯一的调整是