Как правильно сохранить и загрузить данные numpy.array ()?

107

Интересно, как правильно сохранять и загружать numpy.arrayданные. В настоящее время использую numpy.savetxt()метод. Например, если у меня есть массив markers, который выглядит так:

введите описание изображения здесь

Я пытаюсь спасти это с помощью:

numpy.savetxt('markers.txt', markers)

В другом скрипте я пытаюсь открыть ранее сохраненный файл:

markers = np.fromfile("markers.txt")

И вот что я получаю ...

введите описание изображения здесь

Сохраненные данные сначала выглядят так:

0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00

Но когда я сохраняю только что загруженные данные тем же методом, т.е. numpy.savetxt()это выглядит так:

1.398043286095131769e-76
1.398043286095288860e-76
1.396426376485745879e-76
1.398043286055061908e-76
1.398043286095288860e-76
1.182950697433698368e-76
1.398043275797188953e-76
1.398043286095288860e-76
1.210894289234927752e-99
1.398040649781712473e-76

Что я делаю не так? PS других "закулисных" операций, которые я выполняю, нет. Просто сохранение и загрузка, вот что я получаю. Заранее спасибо.

Bluevoxel
источник
Что выводит текстовый файл? Почему бы просто не записать в файл CSV?
5
Вам нужно сохранять и загружать в виде удобочитаемых текстовых файлов? Это будет быстрее (и файлы будут более компактными), если вы сохраните / загрузите двоичные файлы с помощью np.save()и np.load().
ali_m
Спасибо за ваш совет. Это помогло. Однако можете ли вы объяснить, почему это именно то, что есть, и есть ли способ разрешить сохранение данных в формате * .txt и загрузку их без головной боли? Например, когда кто-то хочет работать с Matlab, java или другими инструментами / языками.
bluevoxel
3
Чтобы передать массивы в / из MATLAB, вы можете использовать scipy.io.savematи scipy.io.loadmat.
ali_m
2
По умолчанию для fromfileчтения данные как двоичные. loadtxtправильное соединение с savetxt. Посмотрите документацию по функциям.
hpaulj

Ответы:

150

Самый надежный способ, который я нашел для этого, - использовать np.savetxtwith, np.loadtxtа не, np.fromfileкоторый лучше подходит для двоичных файлов, написанных с помощью tofile. np.fromfileИ np.tofileметоды записи и чтения двоичных файлов , тогда как np.savetxtпишет текстовый файл. Так, например:

In [1]: a = np.array([1, 2, 3, 4])
In [2]: np.savetxt('test1.txt', a, fmt='%d')
In [3]: b = np.loadtxt('test1.txt', dtype=int)
In [4]: a == b
Out[4]: array([ True,  True,  True,  True], dtype=bool)

Или:

In [5]: a.tofile('test2.dat')
In [6]: c = np.fromfile('test2.dat', dtype=int)
In [7]: c == a
Out[7]: array([ True,  True,  True,  True], dtype=bool)

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

Для массивов NumPy существует платформенно-независимый формат, который можно сохранять и читать с помощью np.saveи np.load:

In  [8]: np.save('test3.npy', a)    # .npy extension is added if not given
In  [9]: d = np.load('test3.npy')
In [10]: a == d
Out[10]: array([ True,  True,  True,  True], dtype=bool)
xnx
источник
48
.npyфайлы (например, созданные с помощью np.save()) не зависят от платформы, их можно компактнее и быстрее создавать, чем текстовые файлы.
ali_m
2
также, np.savezесли вы хотите, чтобы вывод был сжат.
Теган
3
@tegan np.savezсохраняет несколько массивов несжатыми - np.savez_compressedбуду их сжимать - np.save_compressedпока нет . См. Docs.scipy.org/doc/numpy-1.15.1/reference/routines.io.html
Брайан Бернс,
1
Спасибо, xnx, у меня была такая же проблема (с dtype float) с использованием np.savetxt, а np.loadtxt решил ее
Йогеш
У меня возникла проблема с сохранением данных рассола более 2 ГБ. Благодаря xnx проблема решена с помощью a.tofile и np.fromfile.
Azr
49
np.save('data.npy', num_arr) # save
new_num_arr = np.load('data.npy') # load
Шерзод
источник
1
есть проблема с использованием pickle?
Чарли Паркер,
например, чтобы мы могли загружать данные с x = db["x"]помощью y = db["y"]?
Чарли Паркер,
Когда я пробую это сделать, мой массив возвращается пустым. Уф.
Кейт
@Keith, вы просто пробовали распечатать перед сохранением массива numpy?
Шерзод
@Sherzod Я только что использовал array.shapeи ()возвращаюсь.
Кейт
3

np.fromfile()имеет sep=аргумент ключевого слова:

Разделитель между элементами, если файл является текстовым. Пустой («») разделитель означает, что файл следует рассматривать как двоичный. Пробелы (””) в разделителе соответствуют нулю или более пробелов. Разделитель, состоящий только из пробелов, должен соответствовать хотя бы одному пробелу.

Значение по умолчанию sep=""означает, что он np.fromfile()пытается прочитать его как двоичный файл, а не как текстовый файл, разделенный пробелами, поэтому вы получите обратно бессмысленные значения. Если вы используетеnp.fromfile('markers.txt', sep=" ") то получите желаемый результат.

Однако, как указывали другие, np.loadtxt()это предпочтительный способ преобразования текстовых файлов в массивы numpy, и, если файл не должен быть удобочитаемым, обычно лучше вместо этого использовать двоичные форматы (например, np.load()/ np.save()).

ali_m
источник
есть проблема с использованием pickle?
Чарли Паркер,
1

Для краткого ответа используйте np.saveи np.load. Их преимущества в том, что они созданы разработчиками библиотеки numpy и уже работают (плюс, вероятно, уже хорошо оптимизированы), например

import numpy as np
from pathlib import Path

path = Path('~/data/tmp/').expanduser()
path.mkdir(parents=True, exist_ok=True)

lb,ub = -1,1
num_samples = 5
x = np.random.uniform(low=lb,high=ub,size=(1,num_samples))
y = x**2 + x + 2

np.save(path/'x', x)
np.save(path/'y', y)

x_loaded = np.load(path/'x.npy')
y_load = np.load(path/'y.npy')

print(x is x_loaded) # False
print(x == x_loaded) # [[ True  True  True  True  True]]

Расширенный ответ:

В конце концов, это действительно зависит от ваших потребностей, потому что вы также можете сохранить его в удобочитаемом формате (см. Дамп массива NumPy в файл csv ) или даже с другими библиотеками, если ваши файлы очень большие (см. Этот лучший способ сохранить массивы numpy на диске для расширенного обсуждения).

Однако (делая расширение, поскольку вы используете слово «правильно» в своем вопросе) я все еще думаю, что использование функции numpy из коробки (и большей части кода!), Скорее всего, удовлетворит большинство потребностей пользователей. Самая главная причина в том, что это уже работает . Попытка использовать что-то еще по любой другой причине может привести вас к неожиданно ДЛИННОЙ кроличьей норе, чтобы выяснить, почему это не работает, и заставить его работать.

Возьмем, к примеру, попытку спасти рассолом. Я попробовал это просто для удовольствия, и мне потребовалось не менее 30 минут, чтобы понять, что pickle не спасет мои вещи, если я не открою и не прочитаю файл в байтовом режиме с помощью wb. Потребовалось время, чтобы погуглить, попробовать что-то, понять сообщение об ошибке и т. Д. Небольшая деталь, но тот факт, что мне уже требовалось открыть файл, усложнял ситуацию неожиданным образом. Чтобы добавить, что мне потребовалось перечитать это (что, кстати, немного сбивает с толку) Разница между режимами a, a +, w, w + и r + во встроенной функции открытия?.

Поэтому, если есть интерфейс, который соответствует вашим потребностям, используйте его, если у вас нет ( очень ) веской причины (например, совместимость с Matlab или по какой-то причине вы действительно хотите прочитать файл, а печать на python действительно не соответствует вашим потребностям, что может быть сомнительным). Кроме того, скорее всего, если вам нужно оптимизировать его, вы узнаете позже (вместо того, чтобы тратить годы на отладку бесполезных вещей, таких как открытие простого файла numpy).

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

Я уже потратил кучу времени на сохранение и загрузку данных с помощью numpy, так что получайте удовольствие, надеюсь, это поможет!

import numpy as np
import pickle
from pathlib import Path

path = Path('~/data/tmp/').expanduser()
path.mkdir(parents=True, exist_ok=True)

lb,ub = -1,1
num_samples = 5
x = np.random.uniform(low=lb,high=ub,size=(1,num_samples))
y = x**2 + x + 2

# using save (to npy), savez (to npz)
np.save(path/'x', x)
np.save(path/'y', y)
np.savez(path/'db', x=x, y=y)
with open(path/'db.pkl', 'wb') as db_file:
    pickle.dump(obj={'x':x, 'y':y}, file=db_file)

## using loading npy, npz files
x_loaded = np.load(path/'x.npy')
y_load = np.load(path/'y.npy')
db = np.load(path/'db.npz')
with open(path/'db.pkl', 'rb') as db_file:
    db_pkl = pickle.load(db_file)

print(x is x_loaded)
print(x == x_loaded)
print(x == db['x'])
print(x == db_pkl['x'])
print('done')

Некоторые комментарии к тому, что я узнал:

  • np.saveкак и ожидалось, это уже хорошо сжимает его (см. https://stackoverflow.com/a/55750128/1601580 ), работает из коробки без открытия файла. Чистый. Легко. Эффективно. Используй это.
  • np.savezиспользует несжатый формат (см. документацию ) Save several arrays into a single file in uncompressed .npz format.Если вы решите использовать это (вас предупредили, что нужно отказаться от стандартного решения, так что ожидайте ошибок!), вы можете обнаружить, что вам нужно использовать имена аргументов для его сохранения, если вы не хотите используйте имена по умолчанию. Так что не используйте это, если первый уже работает (или какие-либо работы используют это!)
  • Pickle также позволяет выполнять произвольный код. Некоторые люди могут не захотеть использовать это по соображениям безопасности.
  • создание файлов, удобочитаемых человеком, обходится дорого и т.д.
  • есть что-то, что называется hdf5для больших файлов. Круто! https://stackoverflow.com/a/9619713/1601580

Обратите внимание, что это не исчерпывающий ответ. Но для других ресурсов проверьте это:

Чарли Паркер
источник