可以推迟对 numpy.memmap 的操作吗?
Can operations on a numpy.memmap be deferred?
考虑这个例子:
import numpy as np
a = np.array(1)
np.save("a.npy", a)
a = np.load("a.npy", mmap_mode='r')
print(type(a))
b = a + 2
print(type(b))
输出
<class 'numpy.core.memmap.memmap'>
<class 'numpy.int32'>
所以 b
似乎不再是 memmap
,我假设这迫使 numpy
阅读整个 a.npy
,破坏了 a.npy
的目的内存映射。因此我的问题是,memmaps
上的操作可以推迟到访问时间吗?
我相信子类化 ndarray
或 memmap
可以工作,但对我的 Python 技能没有足够的信心来尝试它。
这是一个显示我的问题的扩展示例:
import numpy as np
# create 8 GB file
# np.save("memmap.npy", np.empty([1000000000]))
# I want to print the first value using f and memmaps
def f(value):
print(value[1])
# this is fast: f receives a memmap
a = np.load("memmap.npy", mmap_mode='r')
print("a = ")
f(a)
# this is slow: b has to be read completely; converted into an array
b = np.load("memmap.npy", mmap_mode='r')
print("b + 1 = ")
f(b + 1)
这就是 python 的工作原理。默认情况下,numpy 操作 return 一个新数组,因此 b
永远不会作为内存映射存在 - 它是在 a
上调用 +
时创建的。
有几种方法可以解决这个问题。最简单的就是全部操作到位,
a += 1
这需要加载内存映射数组进行读写,
a = np.load("a.npy", mmap_mode='r+')
当然,如果你不想覆盖原来的数组,这就没有任何好处了。
在这种情况下,您需要指定 b
应该被映射。
b = np.memmap("b.npy", mmap+mode='w+', dtype=a.dtype, shape=a.shape)
可以使用 out
关键字进行分配 provided by numpy ufuncs.
np.add(a, 2, out=b)
这是一个 ndarray
子类的简单示例,它推迟对其的操作,直到通过索引请求特定元素。
我包括这个是为了表明它可以完成,但它几乎肯定会以新颖和意想不到的方式失败,并且需要大量工作才能使其可用。
对于非常具体的情况,它可能比重新设计代码以更好的方式解决问题更容易。
我建议阅读文档 these examples 以帮助理解它是如何工作的。
import numpy as np
class Defered(np.ndarray):
"""
An array class that deferrs calculations applied to it, only
calculating them when an index is requested
"""
def __new__(cls, arr):
arr = np.asanyarray(arr).view(cls)
arr.toApply = []
return arr
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
## Convert all arguments to ndarray, otherwise arguments
# of type Defered will cause infinite recursion
# also store self as None, to be replaced later on
newinputs = []
for i in inputs:
if i is self:
newinputs.append(None)
elif isinstance(i, np.ndarray):
newinputs.append(i.view(np.ndarray))
else:
newinputs.append(i)
## Store function to apply and necessary arguments
self.toApply.append((ufunc, method, newinputs, kwargs))
return self
def __getitem__(self, idx):
## Get index and convert to regular array
sub = self.view(np.ndarray).__getitem__(idx)
## Apply stored actions
for ufunc, method, inputs, kwargs in self.toApply:
inputs = [i if i is not None else sub for i in inputs]
sub = super().__array_ufunc__(ufunc, method, *inputs, **kwargs)
return sub
如果不使用 numpy 的通用函数对其进行修改,这将失败。例如 percentile
和 median
不是基于 ufunc,最终会加载整个数组。同样,如果您将它传递给迭代数组的函数,或将索引应用于大量的索引,整个数组将被加载。
考虑这个例子:
import numpy as np
a = np.array(1)
np.save("a.npy", a)
a = np.load("a.npy", mmap_mode='r')
print(type(a))
b = a + 2
print(type(b))
输出
<class 'numpy.core.memmap.memmap'>
<class 'numpy.int32'>
所以 b
似乎不再是 memmap
,我假设这迫使 numpy
阅读整个 a.npy
,破坏了 a.npy
的目的内存映射。因此我的问题是,memmaps
上的操作可以推迟到访问时间吗?
我相信子类化 ndarray
或 memmap
可以工作,但对我的 Python 技能没有足够的信心来尝试它。
这是一个显示我的问题的扩展示例:
import numpy as np
# create 8 GB file
# np.save("memmap.npy", np.empty([1000000000]))
# I want to print the first value using f and memmaps
def f(value):
print(value[1])
# this is fast: f receives a memmap
a = np.load("memmap.npy", mmap_mode='r')
print("a = ")
f(a)
# this is slow: b has to be read completely; converted into an array
b = np.load("memmap.npy", mmap_mode='r')
print("b + 1 = ")
f(b + 1)
这就是 python 的工作原理。默认情况下,numpy 操作 return 一个新数组,因此 b
永远不会作为内存映射存在 - 它是在 a
上调用 +
时创建的。
有几种方法可以解决这个问题。最简单的就是全部操作到位,
a += 1
这需要加载内存映射数组进行读写,
a = np.load("a.npy", mmap_mode='r+')
当然,如果你不想覆盖原来的数组,这就没有任何好处了。
在这种情况下,您需要指定 b
应该被映射。
b = np.memmap("b.npy", mmap+mode='w+', dtype=a.dtype, shape=a.shape)
可以使用 out
关键字进行分配 provided by numpy ufuncs.
np.add(a, 2, out=b)
这是一个 ndarray
子类的简单示例,它推迟对其的操作,直到通过索引请求特定元素。
我包括这个是为了表明它可以完成,但它几乎肯定会以新颖和意想不到的方式失败,并且需要大量工作才能使其可用。
对于非常具体的情况,它可能比重新设计代码以更好的方式解决问题更容易。
我建议阅读文档 these examples 以帮助理解它是如何工作的。
import numpy as np
class Defered(np.ndarray):
"""
An array class that deferrs calculations applied to it, only
calculating them when an index is requested
"""
def __new__(cls, arr):
arr = np.asanyarray(arr).view(cls)
arr.toApply = []
return arr
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
## Convert all arguments to ndarray, otherwise arguments
# of type Defered will cause infinite recursion
# also store self as None, to be replaced later on
newinputs = []
for i in inputs:
if i is self:
newinputs.append(None)
elif isinstance(i, np.ndarray):
newinputs.append(i.view(np.ndarray))
else:
newinputs.append(i)
## Store function to apply and necessary arguments
self.toApply.append((ufunc, method, newinputs, kwargs))
return self
def __getitem__(self, idx):
## Get index and convert to regular array
sub = self.view(np.ndarray).__getitem__(idx)
## Apply stored actions
for ufunc, method, inputs, kwargs in self.toApply:
inputs = [i if i is not None else sub for i in inputs]
sub = super().__array_ufunc__(ufunc, method, *inputs, **kwargs)
return sub
如果不使用 numpy 的通用函数对其进行修改,这将失败。例如 percentile
和 median
不是基于 ufunc,最终会加载整个数组。同样,如果您将它传递给迭代数组的函数,或将索引应用于大量的索引,整个数组将被加载。