Вопрос
Учитывая большую серию DataFrames с небольшим разнообразием dtypes, какова оптимальная конструкция для сохранения/сериализации Pandas DataFrame, если мне важна степень сжатия в первую очередь, скорость распаковки во вторую и начальная скорость сжатия в третью?
Предыстория:
У меня есть примерно 200 тысяч кадров данных формы [2900,8], которые мне нужно хранить в логических блоках по ~ 50 кадров данных в файле. Фрейм данных содержит переменные типа np.int8, np.float64. Большинство фреймов данных являются хорошими кандидатами для разреженных типов, но разреженный не поддерживается в хранилищах формата «таблица» HDF (не то, чтобы это даже помогло — см. Размер разреженного рассола, сжатого с помощью gzip, ниже). Данные генерируются ежедневно и в настоящее время составляют более 20 ГБ. Хотя я не привязан к HDF, мне еще предстоит найти лучшее решение, позволяющее считывать отдельные кадры данных в постоянном хранилище в сочетании с высококачественным сжатием. Опять же, я готов немного пожертвовать скоростью ради лучшей степени сжатия, тем более, что мне нужно будет отправить это по всей сети.
Есть несколько других тем SO и других ссылок, которые могут быть актуальны для тех, кто находится в аналогичном положении. Однако большая часть того, что я нашел, не ориентирована на минимизацию размера хранилища в качестве приоритета:
Рабочие потоки «больших данных» с использованием pandas
HDF5 и SQLite. Параллелизм, сжатие и производительность ввода-вывода [закрыто]
Окружающая среда:
OSX 10.9.5
Pandas 14.1
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
PyTables version: 3.1.1
HDF5 version: 1.8.13
NumPy version: 1.8.1
Numexpr version: 2.4 (not using Intel's VML/MKL)
Zlib version: 1.2.5 (in Python interpreter)
LZO version: 2.06 (Aug 12 2011)
BZIP2 version: 1.0.6 (6-Sept-2010)
Blosc version: 1.3.5 (2014-03-22)
Blosc compressors: ['blosclz', 'lz4', 'lz4hc', 'snappy', 'zlib']
Cython version: 0.20.2
Python version: 2.7.8 (default, Jul 2 2014, 10:14:46)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
Platform: Darwin-13.4.0-x86_64-i386-64bit
Byte-ordering: little
Detected cores: 8
Default encoding: ascii
Default locale: (en_US, UTF-8)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Пример:
import pandas as pd
import numpy as np
import random
import cPickle as pickle
import gzip
def generate_data():
alldfs = {}
n = 2800
m = 8
loops = 50
idx = pd.date_range('1/1/1980',periods=n,freq='D')
for x in xrange(loops):
id = "id_%s" % x
df = pd.DataFrame(np.random.randn(n,m) * 100,index=idx)
# adjust data a bit..
df.ix[:,0] = 0
df.ix[:,1] = 0
for y in xrange(100):
i = random.randrange(n-1)
j = random.randrange(n-1)
df.ix[i,0] = 1
df.ix[j,1] = 1
df.ix[:,0] = df.ix[:,0].astype(np.int8) # adjust datatype
df.ix[:,1] = df.ix[:,1].astype(np.int8)
alldfs[id] = df
return alldfs
def store_all_hdf(x,format='table',complevel=9,complib='blosc'):
fn = "test_%s_%s-%s.hdf" % (format,complib,complevel)
hdfs = pd.HDFStore(fn,mode='w',format=format,complevel=complevel,complib=complib)
for key in x.keys():
df = x[key]
hdfs.put(key,df,format=format,append=False)
hdfs.close()
alldfs = generate_data()
for format in ['table','fixed']:
for complib in ['blosc','zlib','bzip2','lzo',None]:
store_all_hdf(alldfs,format=format,complib=complib,complevel=9)
# pickle, for comparison
with open('test_pickle.pkl','wb') as f:
pickle.dump(alldfs,f)
with gzip.open('test_pickle_gzip.pklz','wb') as f:
pickle.dump(alldfs,f)
with gzip.open('test_pickle_gzip_sparse.pklz','wb') as f:
sparsedfs = {}
for key in alldfs.keys():
sdf = alldfs[key].to_sparse(fill_value=0)
sparsedfs[key] = sdf
pickle.dump(sparsedfs,f)
Результаты
-rw-r--r-- 1 bazel staff 10292760 Oct 17 14:31 test_fixed_None-9.hdf
-rw-r--r-- 1 bazel staff 9531607 Oct 17 14:31 test_fixed_blosc-9.hdf
-rw-r--r-- 1 bazel staff 7867786 Oct 17 14:31 test_fixed_bzip2-9.hdf
-rw-r--r-- 1 bazel staff 9506483 Oct 17 14:31 test_fixed_lzo-9.hdf
-rw-r--r-- 1 bazel staff 8036845 Oct 17 14:31 test_fixed_zlib-9.hdf
-rw-r--r-- 1 bazel staff 26627915 Oct 17 14:31 test_pickle.pkl
-rw-r--r-- 1 bazel staff 8752370 Oct 17 14:32 test_pickle_gzip.pklz
-rw-r--r-- 1 bazel staff 8407704 Oct 17 14:32 test_pickle_gzip_sparse.pklz
-rw-r--r-- 1 bazel staff 14464924 Oct 17 14:31 test_table_None-9.hdf
-rw-r--r-- 1 bazel staff 8619016 Oct 17 14:31 test_table_blosc-9.hdf
-rw-r--r-- 1 bazel staff 8154716 Oct 17 14:31 test_table_bzip2-9.hdf
-rw-r--r-- 1 bazel staff 8481631 Oct 17 14:31 test_table_lzo-9.hdf
-rw-r--r-- 1 bazel staff 8047125 Oct 17 14:31 test_table_zlib-9.hdf
Учитывая приведенные выше результаты, кажется, что лучшим решением для «сжатия в первую очередь» является хранение данных в фиксированном формате HDF с помощью bzip2. Есть ли лучший способ организации данных, возможно, без HDF, который позволил бы мне сэкономить еще больше места?
Обновление 1
Согласно приведенному ниже комментарию Джеффа, я использовал ptrepack в HDF-файле хранилища таблиц без начального сжатия, а затем повторно сжал. Результаты ниже:
-rw-r--r-- 1 bazel staff 8627220 Oct 18 08:40 test_table_repack-blocsc-9.hdf
-rw-r--r-- 1 bazel staff 8627620 Oct 18 09:07 test_table_repack-blocsc-blosclz-9.hdf
-rw-r--r-- 1 bazel staff 8409221 Oct 18 08:41 test_table_repack-blocsc-lz4-9.hdf
-rw-r--r-- 1 bazel staff 8104142 Oct 18 08:42 test_table_repack-blocsc-lz4hc-9.hdf
-rw-r--r-- 1 bazel staff 14475444 Oct 18 09:05 test_table_repack-blocsc-snappy-9.hdf
-rw-r--r-- 1 bazel staff 8059586 Oct 18 08:43 test_table_repack-blocsc-zlib-9.hdf
-rw-r--r-- 1 bazel staff 8161985 Oct 18 09:08 test_table_repack-bzip2-9.hdf
Как ни странно, повторное сжатие с помощью ptrepack, по-видимому, увеличивает общий размер файла (по крайней мере, в этом случае с использованием табличного формата с аналогичными компрессорами).
ptrepack --chunkshape=auto --propindexes --complevel=9 --complib=blosc in.h5 out.h5
со сжатием (хотя не уверен, что это повлияет на фактическое соотношение, это функция самих ваших данных). случайные данные вообще не сильно сжимаются, тем не менее, более обычные данные - person Jeff   schedule 17.10.2014