python – сохранение массива numpy в файл (возможно минимальный размер)

Прямо сейчас у меня есть программа python, создающая довольно большой 2D-массив numpy и сохраняющий его в виде текстового файла с разделителями табуляции с использованием numpy.savetxt. Массив numpy содержит только float. Затем я читаю файл в одной строке за раз в отдельной программе на C ++.

То, что я хотел бы сделать, это найти способ выполнить эту же задачу, изменив мой код как можно меньше, чтобы уменьшить размер файла, который я передаю между двумя программами.

Я обнаружил, что могу использовать numpy.savetxt для сохранения в сжатом файле .gz вместо текстового файла. Это уменьшает размер файла от ~ 2 МБ до ~ 100 кБ.

Есть лучший способ сделать это? Могу ли я, возможно, написать массив numpy в двоичном файле для сохранения пространства? Если да, то как мне это сделать, чтобы я все еще мог прочитать его в программе на C ++?

Спасибо вам за помощь. Я ценю любое руководство, которое я могу получить.

РЕДАКТИРОВАТЬ:

Есть много нулей (возможно, 70% значений в массиве numpy составляют 0,0000). Я не уверен, как я могу каким-то образом использовать это, и создаю крошечный файл, который моя программа на C ++ может читать в

Поскольку у вас много нулей, вы можете записывать ненулевые элементы в форме (индекс, номер).

Предположим, у вас есть массив с небольшим количеством ненулевых чисел:

In [5]: a = np.zeros((10, 10)) In [6]: a Out[6]: array([[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]) In [7]: a[3,1] = 2.0 In [8]: a[7,4] = 17.0 In [9]: a[9,0] = 1.5 

Во-первых, выделите интересные номера и их индексы:

 In [11]: x, y = a.nonzero() In [12]: zip(x,y) Out[12]: [(3, 1), (7, 4), (9, 0)] In [13]: nonzero = zip(x,y) 

Теперь осталось только небольшое количество элементов данных. Самое простое – записать их в текстовый файл:

 In [17]: with open('numbers.txt', 'w+') as outf: ....: for r, k in nonzero: ....: outf.write('{:d} {:d} {:g}\n'.format(r, k, a[r,k])) ....: In [18]: cat numbers.txt 3 1 2 7 4 17 9 0 1.5 

Это также дает вам возможность наблюдать данные. В вашей программе на C ++ вы можете прочитать эти данные с помощью fscanf .

Но вы можете уменьшить размер еще больше, написав двоичные данные, используя struct :

 In [17]: import struct In [19]: c = struct.Struct('=IId') In [20]: with open('numbers.bin', 'w+') as outf: ....: for r, k in nonzero: ....: outf.write(c.pack(r, k, a[r,k])) 

Аргумент конструктора Struct означает; используйте формат родной даты '='. Первый и второй элементы данных представляют собой целые числа без знака «I», третий – двойной «d».

В вашей программе на C ++ эти данные, вероятно, лучше всего читать как двоичные данные в упакованную struct .

EDIT : ответ обновлен для 2D-массива.

Если вы не уверены, что вам не нужно беспокоиться о контенте и т. Д., Лучше всего используйте numpy.savez , как описано в ответе @ unutbu и комментарии @ jorgeca здесь: tostring / fromstring numpy — что мне нужно указать для восстановления массив .

Если результирующий размер не достаточно мал, всегда есть zlib (со стороны python: import zlib , на стороне C ++, я уверен, что реализация существует).

Альтернативой было бы использовать формат hdf5 : хотя это не обязательно уменьшает размер файла на диске, он ускоряет экономию / загрузку (для этого был разработан формат, для больших массивов данных). На hdf5 есть и питоны, и C ++-читатели / писатели.

numpy.ndarray.tofile и numpy.fromfile полезны для прямого двоичного вывода / ввода из python. std::ostream::write std::istream::read полезны для двоичного вывода / ввода в c ++.

Вы должны быть осторожны с endianess, если данные передаются с одной машины на другую.

Используйте файл hdf5, они очень просты в использовании с помощью h5py, и вы можете использовать флаг сжатия. Обратите внимание, что hdf5 также имеет интерфейс c ++.

Если вы не возражаете устанавливать дополнительные пакеты (как для python и для c++ ), вы можете использовать [BSON][1] (двоичный JSON).