Jak poprawnie zapisywać i ładować dane numpy.array ()?

107

Zastanawiam się, jak numpy.arraypoprawnie zapisywać i ładować dane. Obecnie używam numpy.savetxt()metody. Na przykład, jeśli mam tablicę markers, która wygląda tak:

wprowadź opis obrazu tutaj

Staram się to uratować za pomocą:

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

W innym skrypcie próbuję otworzyć zapisany wcześniej plik:

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

I to właśnie dostaję ...

wprowadź opis obrazu tutaj

Zapisane dane wyglądają najpierw tak:

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

Ale kiedy zapisuję właśnie załadowane dane tą samą metodą tj. numpy.savetxt()To wygląda tak:

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

Co ja robię źle? PS nie ma innej operacji „za kulisami”, którą wykonuję. Tylko zapisuję i ładuję i to właśnie dostaję. Z góry dziękuję.

bluevoxel
źródło
Jaki jest wynik pliku tekstowego? Dlaczego po prostu nie pisać do pliku CSV?
5
Czy musisz zapisywać i wczytywać jako czytelne dla człowieka pliki tekstowe? Będzie szybciej (a pliki będą bardziej kompaktowe), jeśli zapiszesz / załadujesz pliki binarne za pomocą np.save()i np.load().
ali_m
Dziękuję za radę. Pomogło. Czy możesz jednak wyjaśnić, dlaczego tak jest i czy istnieje sposób, aby zezwolić na zapisywanie danych w formacie * .txt i ładowanie ich bez bólu głowy? Na przykład, gdy chcesz pracować z matlab, java lub innymi narzędziami / językami.
bluevoxel
3
Aby przekazać tablice do / z MATLABA, możesz użyć scipy.io.savemati scipy.io.loadmat.
ali_m
2
Domyślnie dane fromfilesą odczytywane jako binarne. loadtxtto prawidłowe parowanie z savetxt. Spójrz na dokumentację funkcji.
hpaulj

Odpowiedzi:

150

Najbardziej niezawodnym sposobem znalazłem to zrobić, aby skorzystać np.savetxtz np.loadtxta nie np.fromfilektóry jest lepiej dostosowany do plików binarnych z pisemnych tofile. np.fromfileI np.tofilemetody zapisu i odczytu plików binarnych, natomiast np.savetxtzapisuje plik tekstowy. Na przykład:

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)

Lub:

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)

Używam tej pierwszej metody, nawet jeśli jest wolniejsza i tworzy większe pliki (czasami): format binarny może być zależny od platformy (na przykład format pliku zależy od endianness twojego systemu).

Istnieje niezależny od platformy format tablic NumPy, który można zapisywać i odczytywać za pomocą np.savei 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
źródło
48
.npyPliki (np. generowane przez np.save()) niezależne od platformy i będą bardziej kompaktowe i szybsze w tworzeniu niż pliki tekstowe.
ali_m,
2
także np.savezjeśli chcesz skompresować dane wyjściowe.
rozpoczęło się
3
@tegan np.savezzapisuje kilka nieskompresowanych tablic - np.savez_compressedskompresuje je - np.save_compressedjeszcze nie ma . Zobacz docs.scipy.org/doc/numpy-1.15.1/reference/routines.io.html
Brian Burns
1
Dzięki xnx miałem ten sam problem (z dtype float) używając np.savetxt z np.loadtxt rozwiązałem go
Yogesh
Miałem problem z zapisywaniem danych w marynacie większych niż 2 GB. Dzięki xnx problem rozwiązano za pomocą a.tofile i np.fromfile.
Azr
49
np.save('data.npy', num_arr) # save
new_num_arr = np.load('data.npy') # load
Sherzod
źródło
1
czy jest problem z używaniem pickle?
Charlie Parker
na przykład, abyśmy mogli załadować dane za pomocą, a x = db["x"]następnie y = db["y"]?
Charlie Parker
Kiedy próbuję tego, moja tablica jest pusta. Urgh.
Keith
@Keith, czy po prostu próbowałeś drukować przed zapisaniem tablicy numpy?
Sherzod
@Sherzod Właśnie używałem array.shapei ()wracałem.
Keith
3

np.fromfile()ma sep=argument słowa kluczowego:

Separator między elementami, jeśli plik jest plikiem tekstowym. Separator pusty („”) oznacza, że ​​plik powinien być traktowany jako binarny. Spacje („”) w separatorze odpowiadają zeru lub większej liczbie białych znaków. Separator składający się wyłącznie ze spacji musi odpowiadać co najmniej jednej spacji.

Domyślna wartość sep=""oznacza, że np.fromfile()próbuje odczytać go jako plik binarny, a nie jako plik tekstowy z wartościami oddzielonymi spacjami, więc zwracane są bezsensowne wartości. Jeśli go użyjesz np.fromfile('markers.txt', sep=" "), otrzymasz wynik, którego szukasz.

Jednak, jak zauważyli inni, np.loadtxt()jest to preferowany sposób konwersji plików tekstowych na tablice numpy i jeśli plik nie musi być czytelny dla człowieka, zwykle lepiej jest użyć zamiast tego formatów binarnych (np. np.load()/ np.save()).

ali_m
źródło
czy jest problem z używaniem pickle?
Charlie Parker
1

Aby uzyskać krótką odpowiedź, użyj np.savei np.load. Zaletą tego jest to, że są one tworzone przez programistów biblioteki numpy i już działają (plus prawdopodobnie są już ładnie zoptymalizowane) np.

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]]

Rozszerzona odpowiedź:

Ostatecznie zależy to od twoich potrzeb ponieważ możesz również zapisać to w formacie czytelnym dla człowieka (zobacz ten Zrzuć tablicę NumPy do pliku csv ) lub nawet z innymi bibliotekamijeśli twoje pliki są bardzo duże (zobacz ten najlepszy sposób na zachowanie tablic numpy na dysku dla rozszerzonej dyskusji).

Jednak (robiąc rozwinięcie, ponieważ w swoim pytaniu użyłeś słowa „prawidłowo”) nadal uważam, że używanie funkcji numpy po wyjęciu z pudełka (i większości kodu!) Najprawdopodobniej zaspokoi większość potrzeb użytkowników. Najważniejszym powodem jest to, że już działa . Próba użycia czegoś innego z jakiegokolwiek innego powodu może doprowadzić cię do nieoczekiwanie DŁUGIEJ króliczej nory, aby dowiedzieć się, dlaczego to nie działa, i zmusić to.

Weźmy na przykład próbę uratowania go marynatą. Próbowałem tego tylko dla zabawy i zajęło mi co najmniej 30 minut, zanim zdałem sobie sprawę, że marynata nie zapisałaby moich rzeczy, chyba że otworzę i przeczytam plik w trybie bajtów za pomocą wb. Zajęło trochę czasu, żeby wygooglować, spróbować, zrozumieć komunikat o błędzie itp. Drobny szczegół, ale fakt, że już wymagało to ode mnie otwarcia pliku, skomplikował sprawę w nieoczekiwany sposób. Aby dodać, że wymagało to ponownego przeczytania (co przy okazji jest trochę zagmatwane) Różnica między trybami a, a +, w, w + i r + we wbudowanej funkcji open? .

Więc jeśli istnieje interfejs, który spełnia twoje potrzeby, użyj go, chyba że masz ( bardzo ) dobry powód (np. Zgodność z matlabem lub z jakiegoś powodu naprawdę chcesz odczytać plik, a drukowanie w Pythonie naprawdę nie spełnia twoich potrzeb), może budzić wątpliwości). Co więcej, najprawdopodobniej, jeśli chcesz go zoptymalizować, dowiesz się później (zamiast spędzać lata na debugowaniu bezużytecznych rzeczy, takich jak otwieranie prostego pliku numpy).

Więc użyj interfejsu / numpy dostarcza . To może nie być idealne, najprawdopodobniej w porządku, szczególnie w przypadku biblioteki, która istnieje tak długo, jak odrętwienie.

Spędziłem już sporo czasu na zapisywaniu i ładowaniu danych z numpy, więc baw się dobrze, mam nadzieję, że to pomoże!

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

Kilka komentarzy na temat tego, czego się nauczyłem:

  • np.savezgodnie z oczekiwaniami, to już dobrze kompresuje (patrz https://stackoverflow.com/a/55750128/1601580 ), działa po wyjęciu z pudełka bez otwierania pliku. Czysty. Łatwy. Wydajny. Użyj tego.
  • np.savezużywa nieskompresowanego formatu (patrz dokumentacja ) Save several arrays into a single file in uncompressed .npz format.Jeśli zdecydujesz się użyć tego (ostrzeżono Cię, aby odejść od standardowego rozwiązania, więc spodziewaj się błędów!), możesz odkryć, że musisz użyć nazw argumentów, aby go zapisać, chyba że chcesz użyj nazw domyślnych. Więc nie używaj tego, jeśli pierwsza już działa (lub jakieś prace używają tego!)
  • Pickle pozwala również na wykonanie dowolnego kodu. Niektórzy ludzie mogą nie chcieć tego używać ze względów bezpieczeństwa.
  • pliki czytelne dla ludzi są drogie w wykonaniu itp. Prawdopodobnie nie warto.
  • jest coś, co wymaga hdf5dużych plików. Fajne! https://stackoverflow.com/a/9619713/1601580

Zauważ, że to nie jest wyczerpująca odpowiedź. Ale w przypadku innych zasobów sprawdź to:

Charlie Parker
źródło