python - tutorial - La mejor manera de conservar matrices numpy en el disco
que es un array en python (6)
¡Ahora hay un clon de pickle
basado en HDF5 llamado hickle
!
https://github.com/telegraphic/hickle
import hickle as hkl
data = { ''name'' : ''test'', ''data_arr'' : [1, 2, 3, 4] }
# Dump data to file
hkl.dump( data, ''new_data_file.hkl'' )
# Load data from file
data2 = hkl.load( ''new_data_file.hkl'' )
print( data == data2 )
EDITAR:
También existe la posibilidad de "extraer" directamente en un archivo comprimido haciendo:
import pickle, gzip, lzma, bz2
pickle.dump( data, gzip.open( ''data.pkl.gz'', ''wb'' ) )
pickle.dump( data, lzma.open( ''data.pkl.lzma'', ''wb'' ) )
pickle.dump( data, bz2.open( ''data.pkl.bz2'', ''wb'' ) )
Apéndice
import numpy as np
import matplotlib.pyplot as plt
import pickle, os, time
import gzip, lzma, bz2, h5py
compressions = [ ''pickle'', ''h5py'', ''gzip'', ''lzma'', ''bz2'' ]
labels = [ ''pickle'', ''h5py'', ''pickle+gzip'', ''pickle+lzma'', ''pickle+bz2'' ]
size = 1000
data = {}
# Random data
data[''random''] = np.random.random((size, size))
# Not that random data
data[''semi-random''] = np.zeros((size, size))
for i in range(size):
for j in range(size):
data[''semi-random''][i,j] = np.sum(data[''random''][i,:]) + np.sum(data[''random''][:,j])
# Not random data
data[''not-random''] = np.arange( size*size, dtype=np.float64 ).reshape( (size, size) )
sizes = {}
for key in data:
sizes[key] = {}
for compression in compressions:
if compression == ''pickle'':
time_start = time.time()
pickle.dump( data[key], open( ''data.pkl'', ''wb'' ) )
time_tot = time.time() - time_start
sizes[key][''pickle''] = ( os.path.getsize( ''data.pkl'' ) * 10**(-6), time_tot )
os.remove( ''data.pkl'' )
elif compression == ''h5py'':
time_start = time.time()
with h5py.File( ''data.pkl.{}''.format(compression), ''w'' ) as h5f:
h5f.create_dataset(''data'', data=data[key])
time_tot = time.time() - time_start
sizes[key][compression] = ( os.path.getsize( ''data.pkl.{}''.format(compression) ) * 10**(-6), time_tot)
os.remove( ''data.pkl.{}''.format(compression) )
else:
time_start = time.time()
pickle.dump( data[key], eval(compression).open( ''data.pkl.{}''.format(compression), ''wb'' ) )
time_tot = time.time() - time_start
sizes[key][ labels[ compressions.index(compression) ] ] = ( os.path.getsize( ''data.pkl.{}''.format(compression) ) * 10**(-6), time_tot )
os.remove( ''data.pkl.{}''.format(compression) )
f, ax_size = plt.subplots()
ax_time = ax_size.twinx()
x_ticks = labels
x = np.arange( len(x_ticks) )
y_size = {}
y_time = {}
for key in data:
y_size[key] = [ sizes[key][ x_ticks[i] ][0] for i in x ]
y_time[key] = [ sizes[key][ x_ticks[i] ][1] for i in x ]
width = .2
viridis = plt.cm.viridis
p1 = ax_size.bar( x-width, y_size[''random''] , width, color = viridis(0) )
p2 = ax_size.bar( x , y_size[''semi-random''] , width, color = viridis(.45))
p3 = ax_size.bar( x+width, y_size[''not-random''] , width, color = viridis(.9) )
p4 = ax_time.bar( x-width, y_time[''random''] , .02, color = ''red'')
ax_time.bar( x , y_time[''semi-random''] , .02, color = ''red'')
ax_time.bar( x+width, y_time[''not-random''] , .02, color = ''red'')
ax_size.legend( (p1, p2, p3, p4), (''random'', ''semi-random'', ''not-random'', ''saving time''), loc=''upper center'',bbox_to_anchor=(.5, -.1), ncol=4 )
ax_size.set_xticks( x )
ax_size.set_xticklabels( x_ticks )
f.suptitle( ''Pickle Compression Comparison'' )
ax_size.set_ylabel( ''Size [MB]'' )
ax_time.set_ylabel( ''Time [s]'' )
f.savefig( ''sizes.pdf'', bbox_inches=''tight'' )
Estoy buscando una manera rápida de preservar grandes matrices numpy. Quiero guardarlos en el disco en formato binario, y luego volver a leerlos en la memoria relativamente rápido. cPickle no es lo suficientemente rápido, desafortunadamente.
Encontré numpy.savez y numpy.load . Pero lo extraño es que numpy.load carga un archivo npy en "memory-map". Eso significa que la manipulación regular de matrices realmente es lenta. Por ejemplo, algo como esto sería muy lento:
#!/usr/bin/python
import numpy as np;
import time;
from tempfile import TemporaryFile
n = 10000000;
a = np.arange(n)
b = np.arange(n) * 10
c = np.arange(n) * -0.5
file = TemporaryFile()
np.savez(file,a = a, b = b, c = c);
file.seek(0)
t = time.time()
z = np.load(file)
print "loading time = ", time.time() - t
t = time.time()
aa = z[''a'']
bb = z[''b'']
cc = z[''c'']
print "assigning time = ", time.time() - t;
más precisamente, la primera línea será realmente rápida, pero las líneas restantes que asignan las matrices a obj
son ridículamente lentas:
loading time = 0.000220775604248
assining time = 2.72940087318
¿Hay alguna forma mejor de preservar las matrices numpy? Idealmente, quiero poder almacenar múltiples matrices en un archivo.
El tiempo de búsqueda es lento porque cuando usa mmap
no carga el contenido de la matriz en la memoria cuando invoca load
método de load
. Los datos se cargan de manera diferida cuando se necesitan datos particulares. Y esto sucede en la búsqueda en tu caso. Pero la segunda búsqueda no será tan lenta.
Esta es una buena característica de mmap
cuando tienes una gran matriz, no tienes que cargar datos completos en la memoria.
Para resolverlo puedes usar joblib puedes volcar cualquier objeto que quieras utilizando joblib.dump
incluso dos o más numpy arrays
, mira el ejemplo
firstArray = np.arange(100)
secondArray = np.arange(50)
# I will put two arrays in dictionary and save to one file
my_dict = {''first'' : firstArray, ''second'' : secondArray}
joblib.dump(my_dict, ''file_name.dat'')
He comparado el rendimiento (espacio y tiempo) con varias formas de almacenar matrices numpy. Pocos de ellos admiten matrices múltiples por archivo, pero tal vez sea útil de todos modos.
Los archivos Npy y binarios son realmente rápidos y pequeños para datos densos. Si los datos son escasos o muy estructurados, es posible que desee utilizar npz con compresión, lo que ahorrará mucho espacio pero le costará algo de tiempo de carga.
Si la portabilidad es un problema, el binario es mejor que npy. Si la legibilidad humana es importante, entonces tendrá que sacrificar mucho rendimiento, pero puede lograrse bastante bien usando csv (que también es muy portátil, por supuesto).
Más detalles y el código están disponibles en el repositorio github .
Otra posibilidad de almacenar matrices numpy de manera eficiente es Bloscpack :
#!/usr/bin/python
import numpy as np
import bloscpack as bp
import time
n = 10000000
a = np.arange(n)
b = np.arange(n) * 10
c = np.arange(n) * -0.5
tsizeMB = sum(i.size*i.itemsize for i in (a,b,c)) / 2**20.
blosc_args = bp.DEFAULT_BLOSC_ARGS
blosc_args[''clevel''] = 6
t = time.time()
bp.pack_ndarray_file(a, ''a.blp'', blosc_args=blosc_args)
bp.pack_ndarray_file(b, ''b.blp'', blosc_args=blosc_args)
bp.pack_ndarray_file(c, ''c.blp'', blosc_args=blosc_args)
t1 = time.time() - t
print "store time = %.2f (%.2f MB/s)" % (t1, tsizeMB / t1)
t = time.time()
a1 = bp.unpack_ndarray_file(''a.blp'')
b1 = bp.unpack_ndarray_file(''b.blp'')
c1 = bp.unpack_ndarray_file(''c.blp'')
t1 = time.time() - t
print "loading time = %.2f (%.2f MB/s)" % (t1, tsizeMB / t1)
y la salida para mi computadora portátil (una MacBook Air relativamente vieja con un procesador Core2):
$ python store-blpk.py
store time = 0.19 (1216.45 MB/s)
loading time = 0.25 (898.08 MB/s)
eso significa que puede almacenar muy rápido, es decir, el cuello de botella suele ser el disco. Sin embargo, como las relaciones de compresión son bastante buenas aquí, la velocidad efectiva se multiplica por las relaciones de compresión. Estos son los tamaños para estas matrices de 76 MB:
$ ll -h *.blp
-rw-r--r-- 1 faltet staff 921K Mar 6 13:50 a.blp
-rw-r--r-- 1 faltet staff 2.2M Mar 6 13:50 b.blp
-rw-r--r-- 1 faltet staff 1.4M Mar 6 13:50 c.blp
Tenga en cuenta que el uso del compresor Blosc es fundamental para lograr esto. El mismo script pero usando ''clevel'' = 0 (es decir, deshabilitando la compresión):
$ python bench/store-blpk.py
store time = 3.36 (68.04 MB/s)
loading time = 2.61 (87.80 MB/s)
está claramente embotellado por el rendimiento del disco.
Soy un gran admirador de hdf5 para almacenar grandes matrices numpy. Hay dos opciones para tratar con hdf5 en python:
Ambos están diseñados para trabajar con matrices numpy de manera eficiente.
savez () guarda los datos en un archivo zip, puede tomar algún tiempo para comprimir y descomprimir el archivo. Puede usar la función save () y load ():
f = file("tmp.bin","wb")
np.save(f,a)
np.save(f,b)
np.save(f,c)
f.close()
f = file("tmp.bin","rb")
aa = np.load(f)
bb = np.load(f)
cc = np.load(f)
f.close()
Para guardar varias matrices en un solo archivo, solo necesita abrir el archivo primero, y luego guardar o cargar las matrices en secuencia.