python - Cambiar el tamaño de matrices numpy.memmap
arrays resize (2)
Estoy trabajando con un montón de arreglos de gran tamaño y, como estos comenzaron a consumir demasiada memoria últimamente, quería reemplazarlos con numpy.memmap
instancias de numpy.memmap
. El problema es que, de vez en cuando, tengo que cambiar el tamaño de los arreglos, y preferiría hacerlo en el lugar. Esto funcionó bastante bien con arreglos ordinarios, pero al intentarlo en los memmaps se queja, los datos pueden compartirse, e incluso deshabilitar la verificación de verificación no ayuda.
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
Cambiar el tamaño del búfer de mmap subyacente funciona perfectamente bien. El problema es cómo reflejar estos cambios en el objeto de matriz. He visto esta workaround , pero desafortunadamente no cambia el tamaño de la matriz en su lugar. También hay cierta documentación sobre el tamaño de mmaps, pero claramente no está funcionando, al menos con la versión 1.8.0. ¿Alguna otra idea, cómo anular los controles de tamaño incorporados?
El problema es que el indicador OWNDATA es False cuando crea su matriz. Puedes cambiar eso requiriendo que la bandera sea Verdadera cuando crees la matriz:
>>> 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,)
La única advertencia es que puede crear la matriz y hacer una copia para asegurarse de que se cumplan los requisitos.
Editar para guardar la dirección:
Si desea guardar la matriz redimensionada en el disco, puede guardar el memmap como un archivo con formato .npy y abrirlo como numpy.memmap
cuando necesite volver a abrirlo y usarlo como 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])
Editar para ofrecer otro método:
Puede acercarse a lo que está buscando al cambiar el tamaño del mmap base (a.base o a._mmap, almacenado en formato uint8) y "recargar" el 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])
Si no me equivoco, esto logra esencialmente lo que hace la segunda solución de @ wwwslinger, pero sin tener que especificar manualmente el tamaño del nuevo memmap en bits:
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])
Esto funciona bien cuando la nueva matriz debe ser más grande que la anterior, pero no creo que este tipo de enfoque permita que el tamaño del archivo asignado en la memoria se trunque automáticamente si la nueva matriz es más pequeña.
Cambiar el tamaño de la base manualmente, como en la respuesta de @ wwwslinger, parece permitir que se trunque el archivo, pero no reduce el tamaño de la matriz.
Por ejemplo:
# 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])