重复的numpy子数组
这是我问题的简化版本。我有一个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
的一个视图。