Як правильно зберігати та завантажувати дані numpy.array ()?


104

Цікаво, як правильно зберігати та завантажувати 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 немає жодної іншої операції "за лаштунками", яку я виконую. Просто зберігаю і завантажую, і ось що я отримую. Спасибі заздалегідь.


Що виводить текстовий файл? Чому б просто не записати у файл CSV?

4
Чи потрібно зберігати та завантажувати як текстові файли, що читаються людиною? Це буде швидше (і файли будуть компактнішими), якщо ви зберігаєте / завантажуєте двійкові файли за допомогою 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

Відповіді:


146

Найбільш надійний спосіб, який я знайшов для цього, - це використовувати np.savetxtз, np.loadtxtа не np.fromfileякий найкраще підходить для двійкових файлів, записаних з tofile. Методи np.fromfileand і 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)

47
.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.
Азр


3

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

Розділювач між елементами, якщо файл є текстовим файлом. Порожній (“”) роздільник означає, що файл слід розглядати як двійковий. Пробіли (””) у роздільнику відповідають нулю або більше пробілів. Розділювач, що складається лише з пробілів, повинен відповідати принаймні одному пробілу.

Значення за замовчуванням sep=""означає, що воно np.fromfile()намагається прочитати його як двійковий файл, а не як розділений пробілом текстовий файл, тому ви отримуєте значення безглуздості назад. Якщо ви використовуєте, np.fromfile('markers.txt', sep=" ")ви отримаєте результат, який шукаєте.

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


чи є проблема з використанням pickle?
Чарлі Паркер,

0

Для короткої відповіді слід використовувати 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 хвилин, щоб зрозуміти, що соління не збереже мої речі, якщо я не відкрию та не прочитаю файл у байтовому режимі 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

Зауважте, що це не вичерпна відповідь. Але щодо інших ресурсів перевірте це:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.