【发布时间】:2009-10-19 07:28:08
【问题描述】:
我对一些 numpy 的东西有疑问。我需要一个 numpy 数组以不寻常的方式运行,方法是将切片作为我切片的数据的视图返回,而不是副本。所以这是我想做的一个例子:
假设我们有一个像这样的简单数组:
a = array([1, 0, 0, 0])
我想用数组中的前一个条目更新数组中的连续条目(从左到右移动),使用如下语法:
a[1:] = a[0:3]
这将得到以下结果:
a = array([1, 1, 1, 1])
或者是这样的:
a[1:] = 2*a[:3]
# a = [1,2,4,8]
为了进一步说明,我想要以下行为:
for i in range(len(a)):
if i == 0 or i+1 == len(a): continue
a[i+1] = a[i]
除了我想要numpy的速度。
numpy 的默认行为是获取切片的副本,所以我实际得到的是这样的:
a = array([1, 1, 0, 0])
我已经将此数组作为 ndarray 的子类,因此如果需要,我可以对其进行进一步更改,我只需要不断更新右侧的切片,因为它会更新左侧的切片边。
我是在做梦还是这魔法可能?
更新:这都是因为我或多或少地尝试使用 Gauss-Seidel 迭代来解决线性代数问题。这是一个涉及调和函数的特殊情况,我试图避免进入这个,因为它真的没有必要并且可能会进一步混淆事情,但是这里是。
算法是这样的:
while not converged:
for i in range(len(u[:,0])):
for j in range(len(u[0,:])):
# skip over boundary entries, i,j == 0 or len(u)
u[i,j] = 0.25*(u[i-1,j] + u[i+1,j] + u[i, j-1] + u[i,j+1])
对吗?但是您可以通过两种方式做到这一点,Jacobi 涉及使用其邻居更新每个元素,而不考虑在 while 循环循环之前您已经进行的更新,要在循环中执行此操作,您将复制数组,然后从复制的数组中更新一个数组。然而,Gauss-Seidel 使用您已经为每个 i-1 和 j-1 条目更新的信息,因此不需要副本,循环应该基本上“知道”,因为在每个单个元素更新后重新评估了数组.也就是说,每次调用 u[i-1,j] 或 u[i,j-1] 这样的条目时,前面循环计算的信息都会出现。
我想用一个使用 numpy 切片的简洁代码行来替换这种缓慢而丑陋的嵌套循环情况:
u[1:-1,1:-1] = 0.25(u[:-2,1:-1] + u[2:,1:-1] + u[1:-1,:-2] + u[1:-1,2:])
但结果是 Jacobi 迭代,因为当您获取切片时: u[:,-2,1:-1] 您复制数据,因此切片不知道所做的任何更新。现在 numpy 仍然循环对吗?它不是并行的,它只是一种更快的循环方式,看起来像 python 中的并行操作。我想通过破解 numpy 来利用这种行为,以便在我获取切片时返回指针而不是副本。对?然后每次 numpy 循环时,该切片都会“更新”或实际上只是复制更新中发生的任何事情。为此,我需要将数组两侧的切片作为指针。
无论如何,如果那里有一个非常聪明的人那么棒,但我几乎已经让自己相信唯一的答案是在 C 中循环。
【问题讨论】:
-
很抱歉,我不太明白你的问题...无论如何,你用 a.copy() 试过了吗?
-
您的要求在很大程度上是毫无意义的。为什么你可以说
a[0:3]并且只有a[0]的意思?那是无法理解的。 -
只是对您正在尝试做的事情进行类比,因为大多数人似乎并不理解它:这有点像将摄像机指向显示其自身输出的电视屏幕。所以你想要的是某种递归赋值——但我不相信有任何保证这会稳定为一个常数值。确定在您的情况下确实如此,但通常不是-例如:
a[:] = 2*a[:]将永远循环。所以不,如果没有显式循环和比较直到值的传播完成,你想要的在 numpy 中是不可能的。 -
@daver:你能用循环发布一个合适的例子吗?现在循环与 a[1:]=a[1] 相同。
-
@daver,我不认为您了解 numpy 的工作原理。当您说 a[1:] = 2*a[:3] 时,有 两个 循环。第一个是 2*a[:3] ,它是一个临时数组。然后,第二个循环执行分配。 a[1:] 不是从自身分配的,而是从临时分配的。问题不在于 a[:3] 不是 a 的视图,因为它是,而是 2*a[:3] 是一个完全不同的数组。 Numpy 为您提供 C 的速度,但代价是更多的临时性。一旦你掌握了这一点,你就会明白为什么你不能在 NumPy 中轻松地做你想做的事而不深入研究底层的东西。