Saving & Loading (.npy / .npz)
Store arrays in NumPy's own binary formats with np.save, np.savez and np.savez_compressed, and read them back with np.load — exact dtype and shape preserved.
You spent minutes simulating 100,000 IPL seasons or cleaning a big dataset. You don't want to recompute it every time you open the notebook — you want to save the array to disk and load it back instantly. NumPy has its own file formats designed exactly for this.
Definition
.npyis NumPy's binary file format for one array. It stores the raw bytes plus a small header with the dtype, shape and memory order, so the array comes back exactly as it was.
.npzis a zip archive containing several.npyfiles, one per named array.np.savezstores them uncompressed;np.savez_compressedzips them smaller.
np.loadreads a.npyfile (returns the array) or an.npzfile (returns a dictionary-likeNpzFile).
- Binary — stores the numbers' actual bytes, not text. Fast, compact, exact (no rounding of floats).
- Header — the reason you never have to tell
np.loadthe dtype or shape. - Not human-readable — you can't open a
.npyin Notepad or Excel; use text formats (next topic) for that.
Syntax
python
np.save(file, arr, allow_pickle=True)
np.savez(file, *args, **kwds)
np.savez_compressed(file, *args, **kwds)
np.load(file, mmap_mode=None, allow_pickle=False)| Function | Parameter | Meaning | Default |
|---|---|---|---|
np.save | file | File name (.npy added if missing) or an open file object | — required |
np.save | arr | The array to save | — required |
np.savez / _compressed | **kwds | Arrays as name=array; each name becomes a key | — |
np.savez / _compressed | *args | Unnamed arrays, stored as arr_0, arr_1, … | — |
np.load | file | File name or file object | — required |
np.load | allow_pickle | Allow loading Python-object arrays (security risk with untrusted files) | False |
np.load | mmap_mode | "r" = memory-map a huge file instead of reading it all | None |
On your computer you normally pass a file name:
python
np.save("scores.npy", scores) # writes scores.npy
scores = np.load("scores.npy")
np.savez("season.npz", runs=runs, wickets=wkts)
data = np.load("season.npz") # data["runs"], data["wickets"]The browser playground has no disk, so the examples below use io.BytesIO — an in-memory "file" that behaves exactly like a real binary file.
Example — save and load one array
python
import numpy as np
import io
scores = np.array([[72, 45, 88], [56, 0, 74]])
buf = io.BytesIO() # an in-memory "file"
np.save(buf, scores)
buf.seek(0) # rewind before reading
loaded = np.load(buf)
print(loaded)
print(loaded.dtype, loaded.shape, np.array_equal(scores, loaded))text
[[72 45 88]
[56 0 74]]
int64 (2, 3) TrueHow it works: np.save wrote the header + bytes into buf. After writing, the "cursor" is at the end of the file, so buf.seek(0) rewinds it — with a real file name you don't need this. The loaded array has the same dtype and shape and identical values.
Example — several arrays in one .npz
python
runs = np.array([72, 45, 88])
wkts = np.array([0, 2, 1])
buf = io.BytesIO()
np.savez(buf, runs=runs, wickets=wkts)
buf.seek(0)
data = np.load(buf)
print(data.files)
print(data["runs"], data["wickets"])text
['runs', 'wickets']
[72 45 88] [0 2 1]data.files lists the stored names; index the result like a dictionary. With a real file, use with np.load("season.npz") as data: so the file is closed afterwards.
Example — compressed vs uncompressed size
python
big = np.zeros((1000, 100))
b1 = io.BytesIO(); np.savez(b1, big=big)
b2 = io.BytesIO(); np.savez_compressed(b2, big=big)
print("savez :", len(b1.getvalue()), "bytes")
print("savez_compressed:", len(b2.getvalue()), "bytes")
b3 = io.BytesIO(); np.save(b3, big)
print("save (.npy) :", len(b3.getvalue()), "bytes")text
savez : 800260 bytes
savez_compressed: 1009 bytes
save (.npy) : 800128 bytes100,000 float64 values = 800,000 bytes; the .npy adds only a 128-byte header. An all-zero array compresses extremely well (to ~1 KB). Real data compresses less, but savez_compressed is worth it for storage and sharing; plain save/savez are faster to read and write.
Comparison — which format?
| Format | Function | Arrays per file | Size | Human-readable | Keeps dtype/shape |
|---|---|---|---|---|---|
.npy | np.save | 1 | raw | no | ✅ |
.npz | np.savez | many (named) | raw | no | ✅ |
.npz (zipped) | np.savez_compressed | many (named) | smaller | no | ✅ |
.txt / .csv | np.savetxt | 1 (1D/2D only) | large | yes | ❌ (text) |
Common mistakes
- ❌ Forgetting
buf.seek(0)with an in-memory buffer →EOFError: No data left in file. ✅ Rewind beforenp.load(not needed with file names). - ❌
np.loadon an object array →ValueError: Object arrays cannot be loaded when allow_pickle=False. ✅ Only passallow_pickle=Truefor files you created — pickles can execute code. - ❌ Saving with
np.savez(file, runs, wkts)and then looking fordata["runs"]. ✅ Unnamed arrays arearr_0,arr_1; use keyword names. - ❌ Using
.npyto share data with Excel users. ✅ Usenp.savetxtto CSV for humans and other tools.
Interview tip
Q: Why use .npy instead of CSV for intermediate results?
A: .npy is binary: it is much faster to read/write, smaller, keeps the exact dtype and shape, and loses no float precision. CSV is only better when humans or non-Python tools must read the file.
Key points to remember
- .npy stores one array in binary with a header recording dtype and shape.
- np.save(file, arr) writes it; np.load(file) reads it back exactly.
- .npz is a zip of several named arrays: np.savez(file, runs=r, wickets=w).
- np.load on an .npz returns a dictionary-like object: data.files, data['runs'].
- np.savez_compressed makes smaller files at the cost of a little speed.
- np.load refuses object (pickled) arrays unless allow_pickle=True — only use it for trusted files.
- In the playground, io.BytesIO acts as an in-memory binary file; remember buf.seek(0).
Try it yourself
Round-trip an array through .npy
A whole season in one .npz
Practice: save, load, verify
Revision Flashcards
Tap a card to flip it and see the answer.
