重复的numpy子数组

4 投票
1 回答
729 浏览
提问于 2025-04-17 06:39

这是我问题的简化版本。我有一个numpy数组:

x = np.array([0,1,2,3])

我还有一个函数:

def f(y): return y**2

我可以计算f(x)。

现在假设我真的想计算f(x)对于一个重复的x:

x = np.array([0,1,2,3,0,1,2,3,0,1,2,3])

有没有办法在不创建一个重复版本的x,并且对f来说是透明的情况下做到这一点?

在我的具体情况下,f是一个复杂的函数,其中一个参数是x。我希望能够在x重复的情况下计算f,而不实际重复它,因为这样会占用内存。

重写f以处理重复的x会很麻烦,我希望能找到一个聪明的方法,可能是通过子类化一个numpy数组来实现这一点。

任何建议都很感激。

1 个回答

8

你可以通过一些技巧来实现这个功能,主要是利用步幅(strides)。

不过,有一些重要的注意事项...

import numpy as np
x = np.arange(4)
numrepeats = 3

y = np.lib.stride_tricks.as_strided(x, (numrepeats,)+x.shape, (0,)+x.strides)

print y
x[0] = 9
print y

所以,y 现在是 x 的一个视图,每一行都是 x。这样做不会占用新的内存,我们可以让 y 变得非常大。

举个例子,我可以这样做:

import numpy as np
x = np.arange(4)
numrepeats = 1e15

y = np.lib.stride_tricks.as_strided(x, (numrepeats,)+x.shape, (0,)+x.strides)

...这样做不会比 x 所需的32字节多占用内存。(否则,y 将会使用大约8个PB的内存)

但是,如果我们把 y 重新调整成只有一个维度,那么就会得到一个完整的副本,这样会占用全部的内存。没有办法用步幅和形状来描述 x 的“水平”切片视图,所以任何少于2个维度的形状都会返回一个副本。

另外,如果我们对 y 进行某些操作,这些操作会返回一个副本(比如你例子中的 y**2),那么我们会得到一个完整的副本。

因此,直接在原地进行操作更合理。(例如 y **= 2,或者等价的 x **= 2。这两者的效果是一样的。)

即使是对于一个通用的函数,你也可以传入 x,然后把结果放回 x 中。

例如:

def f(x):
    return x**3

x[...] = f(x)
print y

y 也会被更新,因为它只是 x 的一个视图。

撰写回答