ডিস্কে নাম্পার অ্যারে সংরক্ষণের সেরা উপায়


124

আমি বড় আকারের নমাই অ্যারে সংরক্ষণের জন্য একটি দ্রুত উপায় সন্ধান করছি। আমি এগুলিকে একটি বাইনারি ফর্ম্যাটে ডিস্কে সংরক্ষণ করতে চাই, তারপরে এগুলি তুলনামূলক দ্রুত গতিতে মেমরিতে ফিরে পড়ি। দুর্ভাগ্যক্রমে, সিপিকেল যথেষ্ট দ্রুত নয়।

আমি numpy.savez এবং numpy.load খুঁজে পেয়েছি । তবে অদ্ভুত বিষয়টি হল, numpy.load একটি এনপিআই ফাইল "মেমরি-মানচিত্র" এ লোড করে। এর অর্থ অ্যারে নিয়মিত ম্যানিপুলেটিং করা ধীর গতিতে। উদাহরণস্বরূপ, এর মতো কিছু সত্যিই ধীর হবে:

#!/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

ন্যাপি অ্যারে সংরক্ষণের আর কোন ভাল উপায় আছে? আদর্শভাবে, আমি একটি ফাইলে একাধিক অ্যারে সঞ্চয় করতে সক্ষম হতে চাই।


3
ডিফল্টরূপে, ফাইলটি এমএম্যাপ করা np.loadউচিত নয়
ফ্রেড ফু

6
পিটবেবলস সম্পর্কে কী ?
ডিজাইন

উত্তরারস, জবাবের জন্য ধন্যবাদ। তবে আমার কোড উদাহরণে দেখার সময় (z ['a']) এত মন্থর কেন?
ভেন্ডেটা

1
আপনার প্রশ্নে আমরা যদি আরও কিছু তথ্য থাকতাম তবে আইফিল এবং এর আকারে যে ধরণের অ্যারে সংরক্ষণ করা হয় বা তারা বিভিন্ন ফাইলে বেশ কয়েকটি অ্যারে হয় বা আপনি কীভাবে সেভ করেন তা যদি আপনার প্রশ্নে আরও কিছু তথ্য থাকে It আপনার প্রশ্ন দ্বারা, আমি এই ধারণাটি পেয়েছি যে প্রথম লাইনটি কিছুই করে না এবং আসল লোডিং এর পরে ঘটে তবে সেগুলি কেবল অনুমান।
ডিজাইন করুন

19
@larmans - "এনপিজেড" ফাইলের জন্য এটি কী মূল্যবান (যেমন একাধিক অ্যারে দ্বারা সংরক্ষণ করা হয়েছে numpy.savez), ডিফল্টটি অ্যারেগুলিকে "অলসভাবে লোড" করতে হয়। এটি তাদের স্মরণীয় করে রাখে না, তবে NpzFileঅবজেক্টটি সূচক না হওয়া পর্যন্ত এগুলি লোড হয় না । জন্য (সুতরাং বিলম্ব ওপি উল্লেখ হয়।) ডকুমেন্টেশন loadছেড়ে যাওয়া এই, এবং সেইজন্য একটি স্পর্শ বিভ্রান্তিকর হচ্ছে ...
জো Kington

উত্তর:


63

আমি বড় আকারের নমাই অ্যারে সংরক্ষণের জন্য এইচডিএফ 5 এর একটি বড় অনুরাগী। পাইথনে এইচডিএফ 5 নিয়ে কাজ করার জন্য দুটি বিকল্প রয়েছে:

http://www.pytables.org/

http://www.h5py.org/

দু'জনকেই দক্ষতার সাথে নাম্পার অ্যারেগুলির সাথে কাজ করার জন্য ডিজাইন করা হয়েছে।


35
আপনি কি অ্যারে বাঁচাতে এই প্যাকেজগুলি ব্যবহার করে কিছু উদাহরণ কোড সরবরাহ করতে রাজি হবেন?
dbliss


1
আমার অভিজ্ঞতা থেকে, এইচডিএফ 5 পারফরম্যান্স খুব ধীর পঠন এবং লেখার অংশ স্টোরেজ এবং সংকোচনের সাথে সক্ষম। উদাহরণস্বরূপ, আমি আকারের (২0000 * 2000) আকারের (2,000-2000 অ্যারে) দুটি 2-ডি অ্যারে করেছি (10,000 * 2000)। আকৃতি (2000 * 2000) সহ একটি অ্যারের একক লেখার কাজটি সম্পূর্ণ হতে প্রায় 1 ~ 2s লাগবে take পারফরম্যান্স উন্নত করার বিষয়ে আপনার কি কোনও পরামর্শ আছে? ধন্যবাদ.
সাইমন লি

206

আমি নমপি অ্যারেগুলি সঞ্চয় করার বিভিন্ন উপায়ের জন্য পারফরম্যান্স (স্থান এবং সময়) তুলনা করেছি। তাদের মধ্যে কয়েকটি ফাইল প্রতি একাধিক অ্যারে সমর্থন করে তবে সম্ভবত এটি কার্যকরভাবেই কার্যকর।

নম্পি অ্যারে স্টোরেজের জন্য মানদণ্ড

এনপিপি এবং বাইনারি ফাইলগুলি ঘন ডেটার জন্য সত্যই দ্রুত এবং ছোট উভয়। যদি ডেটা অপ্রয়োজনীয় বা খুব কাঠামোগত হয় তবে আপনি সংক্ষেপে এনপিজেড ব্যবহার করতে চাইতে পারেন, যা প্রচুর জায়গা সাশ্রয় করবে তবে কিছুটা লোড সময় ব্যয় করতে পারে।

যদি বহনযোগ্যতা একটি সমস্যা হয় তবে বাইনারি এনপিপির চেয়ে ভাল। যদি মানব পাঠযোগ্যতা গুরুত্বপূর্ণ, তবে আপনাকে প্রচুর পারফরম্যান্স ত্যাগ করতে হবে, তবে এটি সিএসভি ব্যবহার করে মোটামুটি ভাল অর্জন করা যেতে পারে (এটি অবশ্যই খুব বহনযোগ্য))

আরও বিশদ এবং কোড গিথুব রেপোতে উপলব্ধ ।


2
আপনি ব্যাখ্যা করতে পারেন কেন বহনযোগ্যতার binaryচেয়ে ভাল npy? এটিও কি প্রযোজ্য npz?
daniel451

1
@ daniel451 কারন যে কোনও ভাষা বাইনারি ফাইলগুলি পড়তে পারে যদি তারা কেবল শেপ, ডাটা টাইপ এবং এটি সারি বা কলাম ভিত্তিক কিনা তা জানতে পারে। আপনি যদি কেবল পাইথন ব্যবহার করছেন তবে এনপিপি ভাল, বাইনারি থেকে সম্ভবত কিছুটা সহজ।
চিহ্নিত করুন

1
ধন্যবাদ! আরও একটি প্রশ্ন: আমি কি কিছু উপেক্ষা করব বা আপনি এইচডিএফ 5 ছেড়ে গেছেন? যেহেতু এটি বেশ সাধারণ, তাই আমি এটি অন্যান্য পদ্ধতির সাথে কীভাবে তুলনা করব আগ্রহী।
daniel451

1
আমি একই চিত্র সংরক্ষণ করতে png এবং npy ব্যবহার করার চেষ্টা করেছি। পিএনজি কেবল 2K স্পেস নেয় যখন এনপিটি 307K নেয়। এই ফলাফলটি আপনার কাজ থেকে সত্যিই আলাদা। আমি কি ভুল কিছু করছি? এই চিত্রটি গ্রেস্কেল চিত্র এবং কেবল 0 এবং 255 এর ভিতরে। আমি মনে করি এটি একটি বিরল তথ্য সঠিক? তারপরে আমি এনপিজেডও ব্যবহার করেছি তবে আকার সম্পূর্ণ একরকম।
ইয়র্ক ইয়াং

3
কেন h5py অনুপস্থিত? নাকি আমি কিছু মিস করছি?
daniel451

49

এখন একটি 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 )

সম্পাদনা করুন:

এটি করার মাধ্যমে সংকুচিত সংরক্ষণাগারে সরাসরি "আচার" দেওয়ার সম্ভাবনাও রয়েছে:

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' )

একটি সতর্কতা যা কিছু পিপিএল এর পক্ষে যত্ন নিতে পারে তা হ'ল আচারটি যথেচ্ছ কোড কার্যকর করতে পারে যা ডেটা সংরক্ষণের জন্য অন্যান্য প্রোটোকলের তুলনায় এটিকে কম সুরক্ষিত করে তোলে।
চার্লি পার্কার

এটা অসাধারণ! আপনি lzma বা bz2 ব্যবহার করে সরাসরি সংক্ষেপে ফাইলগুলি পড়ার জন্য কোড সরবরাহ করতে পারেন?
আর্নেস্ট এস কিরুবাকারন

14

savez () একটি জিপ ফাইলে ডেটা সংরক্ষণ করুন, ফাইলটি জিপ এবং আনজিপ করতে কিছু সময় নিতে পারে। আপনি সংরক্ষণ () এবং লোড () ফাংশনটি ব্যবহার করতে পারেন:

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()

এক ফাইলে একাধিক অ্যারে সংরক্ষণ করার জন্য আপনাকে প্রথমে প্রথমে ফাইলটি খুলতে হবে এবং তারপরে ধারাবাহিকভাবে অ্যারেগুলি সংরক্ষণ বা লোড করতে হবে।


7

দক্ষতার সাথে নাম্পার অ্যারেগুলি সঞ্চয় করার আর একটি সম্ভাবনা হ'ল ব্লকস্ক্যাক :

#!/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)

এবং আমার ল্যাপটপের জন্য আউটপুট (একটি কোর 2 প্রসেসরের সাথে তুলনামূলকভাবে পুরানো ম্যাকবুক এয়ার):

$ 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

দয়া করে মনে রাখবেন যে এটি অর্জনের জন্য ব্লোসক সংক্ষেপক ব্যবহারটি মৌলিক। একই স্ক্রিপ্ট তবে 'ক্ল্যাভেল' = 0 ব্যবহার করে (অর্থাত্ সংক্ষেপণ অক্ষম করা):

$ python bench/store-blpk.py
store time = 3.36 (68.04 MB/s)
loading time = 2.61 (87.80 MB/s)

ডিস্ক কর্মক্ষমতা দ্বারা স্পষ্টভাবে বাধা আছে।


2
যার জন্য এটি উদ্বিগ্ন হতে পারে: যদিও ব্লককপ্যাক এবং পাইটবেবলগুলি বিভিন্ন প্রকল্প, তবে কেবলমাত্র ডিস্ক ডাম্পের উপর ফোকাস দেওয়া এবং অ্যারে স্লাইসিং সংরক্ষণ করা হয়নি, আমি উভয়ই পরীক্ষা করে নিলাম এবং খাঁটি "ফাইল ডাম্প প্রকল্পের জন্য" ব্লককপ্যাক পিটিবেবলগুলির চেয়ে প্রায় 6x গতিযুক্ত।
মার্সেলো সার্ডেলিচ

4

দেখার সময়টি ধীর কারণ আপনি যখন ব্যবহার করবেন mmap অনুরোধ করেন তখন অ্যারের সামগ্রীগুলি মেমরিতে লোড করে নাload পদ্ধতিটি । নির্দিষ্ট ডেটা প্রয়োজন হলে ডেটা অলস লোড হয়। এবং এটি আপনার ক্ষেত্রে দেখার ক্ষেত্রে ঘটে। তবে দ্বিতীয় চেহারা এত ধীর হবে না।

এটি দুর্দান্ত বৈশিষ্ট্য mmap যখন আপনার একটি বড় অ্যারে থাকে তখন আপনাকে পুরো ডেটা মেমরিতে লোড করতে হবে না ।

আপনার জবলিব ব্যবহার করতে পারবেন সমাধান করতে আপনি 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')

গ্রন্থাগারটি আর উপলব্ধ নেই।
আন্দ্রেয়া মোরো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.