лучший способ сохранить массивы numpy на диске
Я ищу быстрый способ сохранить большие массивы numpy. Я хочу сохранить их на диск в двоичном формате, а затем прочитать их обратно в память относительно быстро. cPickle не достаточно быстро, к сожалению.
нашел numpy.savez и numpy.загрузить. Но самое странное, тупица.load загружает файл npy в "memory-map". Это означает, что регулярное манипулирование массивами очень медленно. Например, что-то вроде этого будет очень медленно:
#!/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;
точнее, первая строка будет очень быстрой, но остальные строки, которые присваивают массивы obj
до смешного медленно:
loading time = 0.000220775604248
assining time = 2.72940087318
есть ли лучший способ сохранения массивов numpy? В идеале, я хочу иметь возможность хранить несколько массивов в одном файле.
6 ответов:
Я большой поклонник hdf5 для хранения больших массивов numpy. Есть два варианта работы с hdf5 в python:
оба предназначены для эффективной работы с массивами numpy.
Я сравнил производительность (пространство и время) для нескольких способов хранения массивов numpy. Немногие из них поддерживают несколько массивов в файл, но, возможно, это полезно в любом случае.
Npy и двоичные файлы являются очень быстрыми и маленькими для плотных данных. Если данные разрежены или очень структурированы, вы можете использовать npz со сжатием, что сэкономит много места, но будет стоить некоторого времени загрузки.
Если переносимость является проблемой, двоичный лучше, чем npy. Если человеческая читаемость важна, то вам придется пожертвовать большой производительностью, но это может быть достигнуто довольно хорошо с помощью csv (который также очень портативен, конечно).
более подробная информация и код доступны по адресу РЕПО github.
теперь есть клон на основе HDF5
pickle
под названием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 )
EDIT:
также есть возможность "засолить" непосредственно в сжатый архив, выполнив:
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' ) )
приложение
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' )
savez () сохранить данные в zip-файл, это может занять некоторое время, чтобы zip & распаковать файл. Вы можете использовать функцию save() & 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()
чтобы сохранить несколько массивов в одном файле, вам просто нужно сначала открыть файл, а затем сохранить или загрузить массивы в последовательности.
еще одна возможность эффективно хранить массивы numpy-это 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)
и выход для моего ноутбука (относительно старый MacBook Air с процессором Core2):
$ python store-blpk.py store time = 0.19 (1216.45 MB/s) loading time = 0.25 (898.08 MB/s)
это означает, что он может хранить очень быстро, т. е. узким местом обычно является диск. Однако, поскольку коэффициенты сжатия здесь довольно хороши, эффективная скорость умножается на коэффициенты сжатия. Здесь представлены размеры для этих 76 МБ массивы:
$ 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
обратите внимание, что использование Blosc компрессор является основополагающим для достижения этой цели. Тот же скрипт, но с использованием 'clevel' = 0 (т. е. отключение сжатия):
$ python bench/store-blpk.py store time = 3.36 (68.04 MB/s) loading time = 2.61 (87.80 MB/s)
явно узким местом является производительность диска.
время поиска медленно, потому что при использовании
mmap
to не загружает содержимое массива в память при вызовеload
метод. Данные лениво загружаются, когда необходимы определенные данные. И это происходит в поиске в вашем случае. Но второй поиск не будет таким медленным.это хорошая особенность
mmap
когда у вас есть большой массив, вам не обязательно загружать все данные в память.чтобы решить ваш может использовать joblib вы можете сбросить любой объект, который вы хотите использовать
joblib.dump
даже два или большеnumpy arrays
см. Пример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')