内存映射数组操作的正确方式

7 投票
1 回答
2868 浏览
提问于 2025-04-18 19:51

我对这个操作有点困惑,具体情况是这样的。我之前在普通的Numpy数组上做过这个,但在一个内存映射(memmap)上,我想了解它是怎么运作的。

arr2 = np.argsort(np.argsort(arr1,axis=0),axis=0) / float(len(arr1)) * 100
#This is basically to calculate Percentile rank of each value wrt the entire column

这是我在普通Numpy数组上使用的代码。

现在,假设arr1是一个20GB的内存映射数组,我有几个问题:

1:

arr2 = np.argsort(np.argsort(arr1,axis=0),axis=0) / float(len(arr1)) * 100 

arr2应该是一个普通的Numpy数组,对吧?那么执行这个操作会在内存上造成很大的压力,对吧?

考虑到我现在已经创建了一个大小合适的内存映射数组arr2(里面全是零)。

2:

arr2 = np.argsort(np.argsort(arr1,axis=0),axis=0) / float(len(arr1)) * 100

arr2[:] = np.argsort(np.argsort(arr1,axis=0),axis=0) / float(len(arr1)) * 100

有什么区别呢?

3.

如果我单独计算np.argsort作为一个临时的内存映射数组,然后再计算np.argsort(np.argsort)作为另一个临时的内存映射数组,这样做会不会更节省内存?因为对于一个20GB的数组,argsort数组本身也会非常大!

我觉得这些问题能帮助我更好地理解Python中内存映射数组的内部工作原理!

谢谢...

1 个回答

2

我先来回答第2部分,然后再说第1和第3部分。

首先,arr = <something> 是简单的变量赋值,而 arr[:] = <something> 是在给数组的内容赋值。在下面的代码中,执行 arr[:] = x 后,arr 仍然是一个内存映射数组,而执行 arr = x 后,arr 就变成了一个ndarray(即普通数组)。

>>> arr = np.memmap('mm', dtype='float32', mode='w+', shape=(1,10000000))
>>> type(arr)
<class 'numpy.core.memmap.memmap'>
>>> x = np.ones((1,10000000))
>>> type(x)
<class 'numpy.ndarray'>
>>> arr[:] = x
>>> type(arr)
<class 'numpy.core.memmap.memmap'>
>>> arr = x
>>> type(arr)
<class 'numpy.ndarray'>

对于 np.argsort,它返回一个和输入参数类型相同的数组。所以在这个特定的情况下,我认为 arr = np.argsort(x)arr[:] = np.argsort(x) 之间应该没有区别。在你的代码中,arr2 将会是一个内存映射数组。但实际上是有区别的。

>>> arr = np.memmap('mm', dtype='float32', mode='w+', shape=(1,10000000))
>>> x = np.ones((1,10000000))
>>> arr[:] = x
>>> type(np.argsort(x))
<class 'numpy.ndarray'>
>>> type(np.argsort(arr))
<class 'numpy.core.memmap.memmap'>

好,现在来说说有什么不同。使用 arr[:] = np.argsort(arr),如果我们查看内存映射文件的变化,会发现每次对 arr 的修改都会导致文件的md5校验和发生变化。

>>> import os
>>> import numpy as np
>>> arr = np.memmap('mm', dtype='float32', mode='w+', shape=(1,10000000))
>>> arr[:] = np.zeros((1,10000000))
>>> os.system("md5sum mm")
48e9a108a3ec623652e7988af2f88867  mm
0
>>> arr += 1.1
>>> os.system("md5sum mm")
b8efebf72a02f9c0b93c0bbcafaf8cb1  mm
0
>>> arr[:] = np.argsort(arr)
>>> os.system("md5sum mm")
c3607e7de30240f3e0385b59491ac2ce  mm
0
>>> arr += 1.3
>>> os.system("md5sum mm")
1e6af2af114c70790224abe0e0e5f3f0  mm
0

我们看到 arr 仍然保留着它的 _mmap 属性。

>>> arr._mmap
<mmap.mmap object at 0x7f8e0f086198>

而使用 arr = np.argsort(x) 时,我们会发现md5校验和不再变化。尽管 arr 的类型是内存映射数组,但它实际上是一个新对象,似乎内存映射的特性消失了。

>>> import os
>>> import numpy as np
>>> arr = np.memmap('mm', dtype='float32', mode='w+', shape=(1,10000000))
>>> arr[:] = np.zeros((1,10000000))
>>> os.system("md5sum mm")
48e9a108a3ec623652e7988af2f88867  mm
0
>>> arr += 1.1
>>> os.system("md5sum mm")
b8efebf72a02f9c0b93c0bbcafaf8cb1  mm
0
>>> arr = np.argsort(arr)
>>> os.system("md5sum mm")
b8efebf72a02f9c0b93c0bbcafaf8cb1  mm
0
>>> arr += 1.3
>>> os.system("md5sum mm")
b8efebf72a02f9c0b93c0bbcafaf8cb1  mm
0
>>> type(arr)
<class 'numpy.core.memmap.memmap'>

现在 _mmap 属性变成了 None。

>>> arr._mmap
>>> type(arr._mmap)
<class 'NoneType'>

接下来是第3部分。进行复杂操作时,似乎很容易失去对内存映射对象的引用。我目前的理解是,你需要将操作拆分开,并使用 arr[:] = <> 来处理中间结果。

使用的是numpy 1.8.1 和 Python 3.4.1

撰写回答