在NumPy中交换数组数据

3

我有许多大型的多维 NP 数组(2D 和 3D),用于算法中。在此过程中有许多迭代,每次迭代都会通过计算并保存到相同大小的临时数组中来重新计算数组。在单个迭代结束时,将临时数组的内容复制到实际数据数组中。

例如:

global A, B # ndarrays
A_temp = numpy.zeros(A.shape)
B_temp = numpy.zeros(B.shape)
for i in xrange(num_iters):
    # Calculate new values from A and B storing in A_temp and B_temp...
    # Then copy values from temps to A and B
    A[:] = A_temp
    B[:] = B_temp

这个方法可以正常工作,但是复制所有的值来交换数组看起来有点浪费。实际上,只需要将 A 和 B 交换即可。以下是交换数组的代码:

A, A_temp = A_temp, A
B, B_temp = B_temp, B

然而,在其他作用域中可能存在对数组的其他引用,这不会改变。

似乎NumPy可以有一种内部方法来交换两个数组的内部数据指针,例如numpy.swap(A, A_temp)。然后所有指向A的变量都将指向更改后的数据。


你能举一个“其他作用域中对数组的其他引用不会改变这个”的例子吗? - dmytro
例如,对于计算步骤,我可以(并且将会)使用多个线程,在一个函数中调用这些线程,并将字段作为参数传递。为了减少实例化多个线程的开销,它们也会循环使用。 - coderforlife
如果我没记错的话,除非你使用特定的功能,比如sharedctypes数组等,否则在线程/进程之间传递数据时是无法避免拷贝的。但是我可能记错了... - dmytro
在线程之间共享数据并不困难,实际上不需要额外的工作。它只需要你对竞态条件格外小心(而且由于我只在循环中进行读取,只有一个线程在写入数组,所以这并不是真正的问题)。对于多进程,需要一些额外的工作,但Scipy网站上说:“可以在进程之间共享内存,包括numpy数组。”(http://www.scipy.org/ParallelProgramming)。 - coderforlife
确实,你需要使用 sharedctypes 数组来实现这个(我只用过 multiprocessing,对于 threading 我不太确定)。 - dmytro
3个回答

2
尽管您的方式应该是有效的(我怀疑问题出在其他地方),但您可以尝试明确地执行它:
import numpy as np
A, A_temp = np.frombuffer(A_temp), np.frombuffer(A)

验证你的方法是否有效并不难:

>>> import numpy as np
>>> arr = np.zeros(100)
>>> arr2 = np.ones(100)
>>> print arr.__array_interface__['data'][0], arr2.__array_interface__['data'][0]
152523144 152228040

>>> arr, arr2 = arr2, arr
>>> print arr.__array_interface__['data'][0], arr2.__array_interface__['data'][0]
152228040 152523144

指针成功切换


frombuffer这个想法很有趣,但是它需要一个"reshape(A.shape)"命令,因为它只返回1D数组。除此之外,这并不能解决替代引用的问题。我尝试了几种不同的方法,发现需要一个"setbuffer"函数... 这给了我一个思路去研究视图(对此我并不太了解)。 - coderforlife

1
也许您可以通过添加一个间接层解决这个问题。
您可以创建一个"数组持有者"类。它的作用只是保持对底层NumPy数组的引用。为一对这样的持有者对象实现一个廉价的交换操作将是微不足道的。
如果所有外部引用都指向这些持有者对象而不是直接指向数组,那么在交换过程中这些引用都不会失效。

这绝对是一个可行的替代方案。只是看起来有点愚蠢,因为ndarray已经有一个指向正确数据的指针,可以进行更改...如果我找不到使用视图的方法,那么这将是答案。 - coderforlife
@thaimin:在Python中,交换两个变量的最Pythonic方式是a, b = b, a。这样可以交换它们的引用。我想不出任何一个标准类实现了像你所寻找的交换接口。在Python中,这种操作几乎从不需要,因此并没有被实现。 - NPE
我明白。我尝试使用视图,如果允许重新分配"base"字段的话,它会起作用。我尝试分配"data"字段,这几乎起作用,但不能进行交换(尽管第一个赋值正好符合我的要求,a.data, b.data = b.data, a.data确实可以交换其中"一个")。 - coderforlife

1

我知道这是一个老问题,但是值得一提的是,您还可以通过执行异或交换来在两个ndarray缓冲区之间交换数据(无需进行临时复制):

A_bytes = A.view('ubyte')
A_temp_bytes = A.view('ubyte')
A_bytes ^= A_temp_bytes
A_temp_bytes ^= A_bytes
A_bytes ^= A_temp_bytes

由于这是在视图上完成的,如果您查看原始的AA_temp数组(无论它们最初的dtype是什么),它们的值应该被正确交换。这基本上相当于您正在寻找的numpy.swap(A,A_temp)。不幸的是,它需要3个循环 - 如果将其实现为ufunc(也许应该),它会快得多。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接