2014-01-05 33 views
20

我正在处理一堆大的numpy数组,并且由于最近这些数组开始咀嚼太多内存,我想用numpy.memmap实例替换它们。问题是,现在,我不得不调整数组的大小,我最好做到这一点。这对普通数组来说效果很好,但是在memmaps上试着抱怨,可能会共享数据,甚至禁用refcheck也无济于事。调整numpy.memmap数组的大小

a = np.arange(10) 
a.resize(20) 
a 
>>> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 

a = np.memmap('bla.bin', dtype=int) 
a 
>>> memmap([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 

a.resize(20, refcheck=False) 
--------------------------------------------------------------------------- 
ValueError        Traceback (most recent call last) 
<ipython-input-41-f1546111a7a1> in <module>() 
----> 1 a.resize(20, refcheck=False) 

ValueError: cannot resize this array: it does not own its data 

调整底层mmap缓冲区的大小非常好。问题是如何将这些更改反映到数组对象。我已经看到这个workaround,但不幸的是它没有调整阵列的大小。还有一些关于调整mmap大小的numpy documentation,但它显然不起作用,至少在1.8.0版本中是这样。任何其他想法,如何覆盖内置的大小调整检查?

+0

我觉得我必须失去了一些东西......这个代码运行对我罚款。它会为你运行吗?这不是你想要做的吗? http://codepad.org/eEWmYBHZ –

+0

@three_pineapples他想改变数组的总大小 - 你的代码只是重塑它的形状 –

+0

@ali_m啊,我明白了。我没有从这个问题中得到答案,但正如我所说,我以为我错过了一些东西!感谢澄清 –

回答

10

问题是当您创建阵列时,标志OWNDATA为False。您可以更改通过要求标志为True,当你创建数组:

>>> a = np.require(np.memmap('bla.bin', dtype=int), requirements=['O']) 
>>> a.shape 
(10,) 
>>> a.flags 
    C_CONTIGUOUS : True 
    F_CONTIGUOUS : True 
    OWNDATA : True 
    WRITEABLE : True 
    ALIGNED : True 
    UPDATEIFCOPY : False 
>>> a.resize(20, refcheck=False) 
>>> a.shape 
(20,) 

唯一需要注意的是,它可以创建数组,然后复印一份,以确保满足要求。

编辑,以解决节能:

如果你想保存重新调整大小阵列磁盘,您可以在MEMMAP作为.npy格式的文件,并打开保存为numpy.memmap当你需要重新打开它,作为一个MEMMAP使用:

>>> a[9] = 1 
>>> np.save('bla.npy',a) 
>>> b = np.lib.format.open_memmap('bla.npy', dtype=int, mode='r+') 
>>> b 
memmap([0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 

编辑提供了另一种方法:

你可以亲近你被重新调整大小寻找什么底座MMAP(a.base或a._mmap,存储在UINT8格式)和“重装”的MEMMAP:

>>> a = np.memmap('bla.bin', dtype=int) 
>>> a 
memmap([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 
>>> a[3] = 7 
>>> a 
memmap([0, 0, 0, 7, 0, 0, 0, 0, 0, 0]) 
>>> a.flush() 
>>> a = np.memmap('bla.bin', dtype=int) 
>>> a 
memmap([0, 0, 0, 7, 0, 0, 0, 0, 0, 0]) 
>>> a.base.resize(20*8) 
>>> a.flush() 
>>> a = np.memmap('bla.bin', dtype=int) 
>>> a 
memmap([0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 
+2

有趣。不幸的是,对我来说,它总是在内存中创建一个副本。如果我尝试写入数组,刷新,删除和重新打开数组,它将像以前一样再次为空。所以我猜这些数据永远不会写入磁盘。 – Michael

+0

我添加了一个例子,说明如何将它保存并在稍后作为memmap重新打开 – wwwslinger

+0

@wwwslinger答案的问题是,如果'a'太大而无法放入核心内存中(为什么还要使用内存 - 映射数组?),然后在内核中创建它的另一个副本显然会导致一些问题。你最好从头开始创建一个正确大小的新内存映射数组,然后用'a'的内容填充它。 –

3

如果我没有记错,这基本上达到什么@ wwwslinger的第二个解决方案的做法,但不无需手动指定新MEMMAP的位大小:

In [1]: a = np.memmap('bla.bin', mode='w+', dtype=int, shape=(10,)) 

In [2]: a[3] = 7 

In [3]: a 
Out[3]: memmap([0, 0, 0, 7, 0, 0, 0, 0, 0, 0]) 

In [4]: a.flush() 

# this will append to the original file as much as is necessary to satisfy 
# the new shape requirement, given the specified dtype 
In [5]: new_a = np.memmap('bla.bin', mode='r+', dtype=int, shape=(20,)) 

In [6]: new_a 
Out[6]: memmap([0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 

In [7]: a[-1] = 10 

In [8]: a 
Out[8]: memmap([ 0, 0, 0, 7, 0, 0, 0, 0, 0, 10]) 

In [9]: a.flush() 

In [11]: new_a 
Out[11]: 
memmap([ 0, 0, 0, 7, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0]) 

这种运作良好,当新的阵列需要比旧的大,但我不认为这种类型的方法将允许如果新数组较小,内存映射文件的大小将自动截断。

像在@ wwwslinger的答案中一样,手动调整基地的大小似乎允许文件被截断,但它不会减小数组的大小。

例如:

# this creates a memory mapped file of 10 * 8 = 80 bytes 
In [1]: a = np.memmap('bla.bin', mode='w+', dtype=int, shape=(10,)) 

In [2]: a[:] = range(1, 11) 

In [3]: a.flush() 

In [4]: a 
Out[4]: memmap([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 

# now truncate the file to 40 bytes 
In [5]: a.base.resize(5*8) 

In [6]: a.flush() 

# the array still has the same shape, but the truncated part is all zeros 
In [7]: a 
Out[7]: memmap([1, 2, 3, 4, 5, 0, 0, 0, 0, 0]) 

In [8]: b = np.memmap('bla.bin', mode='r+', dtype=int, shape=(5,)) 

# you still need to create a new np.memmap to change the size of the array 
In [9]: b 
Out[9]: memmap([1, 2, 3, 4, 5]) 
+0

这是一种类似于我发布的解决方法中的方法。我更喜欢就地解决方案,因为它会让我无法进一步封装对象。无论如何,这可能是我最终必须忍受的。 – Michael

+0

@Michael如果你还没有,你应该向numpy的维护者报告这个问题。至少应该更新'np.memmap'类的文档字符串,以反映当前不可能调整内存映射数组的大小。 –

+0

我没有,但因为看起来没有简单的解决方案,我会的。 – Michael