假设我们有一个大矩阵 A ,并且两个矩阵元素的索引(c1,r1),(c2,r2)我们要交换:
import numpy as np
A = np.random.rand(1000,1000)
c1, r1 = 10, 10
c2, r2 = 20, 40
这样做的pythonic方法是:
A[c1, r1], A[c2, r2] = A[c2, r2], A[c1, r1]
但是,如果您想进行大量的交换,这种解决方案可能会很慢。
是否有更有效的方法在numpy数组中交换两个元素?
提前致谢。
答案 0 :(得分:2)
您可以通过使用索引数组(c1,r1,c2,r2)轻松地对交换操作进行矢量化,而不是迭代标量索引列表。
c1 = np.array(<all the "c1" values>, dtype=int)
r1 = np.array(<all the "r1" values>, dtype=int)
c2 = np.array(<all the "c2" values>, dtype=int)
r2 = np.array(<all the "r2" values>, dtype=int)
A[c1, r1], A[c2, r2] = A[c2, r2], A[c1, r1]
请注意,如果交换的顺序有所不同,则可以一次性执行所有交换,这可以与迭代不同。因此,这不是您问题的有效答案。
E.g。用p2交换p1,然后用p3交换p2,不同于交换p1和p2,以及p2和p3。在后一种情况下,p1和p3都得到p2的原始值,而p2得到p1和p3之间值的 last ,即p3(根据它们出现在索引数组中的顺序) )。
然而,由于它是你所追求的速度,所以必须采用矢量化操作(以某种方式)。
那么我们如何才能进行矢量化交换,同时获得我们需要的输出?我们可以采用混合方法,将索引列表分成块(尽可能少),其中每个块只包含唯一的点,从而保证顺序没有区别。交换每个块都是vercrorized-ly,我们只迭代块。
这里有一个如何运作的草图:
c1, r1 = np.array([ np.arange(10), np.arange(10) ])
c2, r2 = np.array([ [2,4,6,8,0,1,3,5,7,9], [9,1,6,8,2,2,2,2,7,0] ])
A = np.empty((15,15))
def get_chunk_boundry(c1, r1, c2, r2):
a1 = c1 + 1j * r1
a2 = c2 + 1j * r2
set1 = set()
set2 = set()
for i, (x1, x2) in enumerate(zip(a1, a2)):
if x1 in set2 or x2 in set1:
return i
set1.add(x1); set2.add(x2)
return len(c1)
while len(c1) > 0:
i = get_chunk_boundry(c1, r1, c2, r2)
c1b = c1[:i]; r1b = r1[:i]; c2b = c2[:i]; r2b = r2[:i]
print 'swapping %d elements' % i
A[c1b,r1b], A[c2b,r2b] = A[c2b,r2b], A[c1b,r1b]
c1 = c1[i:]; r1 = r1[i:]; c2 = c2[i:]; r2 = r2[i:]
如果将索引存储为2dim数组(N x 4),则此处的切片速度会更快。
答案 1 :(得分:1)
这是一个迭代解决方案,我为了参考目的而编写(作为处理可能重复项目的一种方法):
def iter2(A, rc1, rc2):
for r,c in zip(rc1.T, rc2.T):
j,k = tuple(r),tuple(c)
A[j],A[k] = A[k],A[j]
return A
例如,如果:
N = 4
Aref=np.arange(N)+np.arange(N)[:,None]*10
rc1=np.array([[0,0,0],[0,3,0]])
rc2=np.array([[3,3,2],[3,0,2]])
然后
print(iter2(A.copy(), rc1,rc2))
生成角落交换,然后交换(2,2):
[[22 1 2 30]
[10 11 12 13]
[20 21 33 23]
[ 3 31 32 0]]
shx2's
解决方案似乎做了同样的事情,但是对于我的测试用例,在分块函数中似乎存在错误。
对于shx2's
测试(15,15)
数组,我的迭代解决方案快4倍!它正在进行更多交换,但每次交换的工作量更少。对于较大的阵列,我希望分块更快,但我不知道我们必须走多远。它还取决于重复模式。
我的数组的哑向量化交换是:
A[tuple(rc1)],A[tuple(rc2)] = A[tuple(rc2)],A[tuple(rc1)]
在这个(15,15)示例中,它只比我的迭代解决方案快20%。显然,我们需要一个非常大的测试用例来产生一些严肃的时间。
在1d阵列上运行时,迭代解决方案更快,更简单。即使进行了散乱和重塑,这个功能也是我发现的最快的功能。我没有在Cython上获得太多的速度提升。 (但关于数组大小的注意事项仍然适用。)
def adapt_1d(A, rc1, rc2, fn=None):
# adapt a multidim case to use a 1d iterator
rc2f = np.ravel_multi_index(rc2, A.shape)
rc1f = np.ravel_multi_index(rc1, A.shape)
Af = A.flatten()
if fn is None:
for r,c in zip(rc1f,rc2f):
Af[r],Af[c] = Af[c],Af[r]
else:
fn(Af, rc1f, rc2f)
return Af.reshape(A.shape)