Containers · .npz

NumPy .npz archive

A standard ZIP archive whose members are .npy files named "<key>.npy". np.savez stores them uncompressed (ZIP_STORED) and np.savez_compressed deflates them (ZIP_DEFLATED). Several named arrays, no metadata beyond names. Used by JAX/Flax tooling, scientific code, and older Keras/Chainer weight files.

.npz

What it is
many tensors
Byte order
per array declared
Dimension order
per array flag
Compression
optional
Metadata
none
Loading
Loading it into a library can execute code, depending on the loader and the file.

Versions

members

No archive-level format version. Each member is an npy 1.0, 2.0, or 3.0 file, including the NumPy 1.14 alignment change (64-byte data start; older members may be 16-byte aligned). ZIP64 is the APPNOTE mechanism numpy uses when a member or the archive exceeds the 4 GiB ZIP limits; this spec does not restate the ZIP64 extra field.

How to read it

np.savez writes a ZIP archive of STORED .npy members (np.savez_compressed uses deflate instead). Each member is a local file header, then the member name, then that member's npy file. After every member, the central directory lists those members again, and the end-of-central-directory record ends the file. Array bytes live only in the members. ZIP64 extra fields are not restated here; see the versions note and APPNOTE.

member = key + '.npy'. Positional arrays get keys arr_0, arr_1, ...; a positional array cannot be combined with a kwarg named arr_N. np.load strips a trailing '.npy' from the listed names and also keeps the raw member name. A member is parsed as .npy only when it starts with the npy magic; otherwise np.load returns that member's raw bytes and does not fail.

compression: none (savez) or deflate (savez_compressed); any ZIP method in principle. mmap: ZIP magic PK\x03\x04 or PK\x05\x06 makes np.load return a lazy NpzFile and does not pass mmap_mode through. A member is read, and a deflated member is inflated, only when that member is accessed. allow_pickle (default false) and max_header_size (default 10000) apply to each .npy member on its own; allow_pickle true lifts the header cap. A deflated member cannot be memory-mapped. data_offset below is the STORED case of that limit. A custom reader CAN mmap STORED members by computing the data offset (local header offset + 30 + name_len + extra_len + npy header length), but member data is not aligned (first member's array bytes were at offset 183 in a test file). sparse via-convention: scipy.sparse.save_npz writes data/indices/indptr/shape/format members.

local_file_header
bytes. APPNOTE local file header. Magic 50 4b 03 04. savez sets compression method 0 (stored).
member_name
utf8. Name length comes from the local header. np.savez uses '<key>.npy'.
npy_member
bytes. One formats/npy.yaml file, including its own preamble. This is the array payload.
central_directory
bytes. Central-directory headers, then the end-of-central-directory record. Not a second copy of the npy bytes.
member 0                         member 1                      tail
+--------+------+--------+       +--------+------+--------+    +----------+------+
| local  | name | npy    |  ...  | local  | name | npy    |    | central  | EOCD |
| header | .npy | file   |       | header | .npy | file   |    | directory|      |
+--------+------+--------+       +--------+------+--------+    +----------+------+

One tensor

name
member filename with the '.npy' suffix removed
dtype
member npy header descr
shape
member npy header shape
data_offset
STORED only: local_header_offset + 30 + n + m + npy_preamble_and_header (n/m = name/extra lengths from the LOCAL header, which can differ from the central directory)
nbytes
prod(shape)*itemsize
alignment
1. no alignment of members

Reader

The procedure that finds each tensor's bytes.

read

pseudo-python
def read_npz(path, allow_pickle=False, max_header_size=10000):
    z = ZipFile(path); out = []
    for info in z.infolist():
        key = info.filename[:-4] if info.filename.endswith(".npy") else info.filename
        with z.open(info) as m:                     # inflated here, only when accessed
            magic = m.read(6); m.seek(0)
            if magic == b"\x93NUMPY":
                t = read_npy(m, allow_pickle=allow_pickle, max_header_size=max_header_size)
                t.name = key; out.append(t)
            else:
                out.append(Blob(name=key, data=m.read()))   # not an array; do not fail
    return {}, out

Reject the file when

Listed in the spec's order. Only rules marked MUST that reject the file are shown. The other IDs are warnings or implementation limits.

  • NPZ-001. valid ZIP (central directory found)
  • NPZ-002. each member intended as an array is a valid .npy (see npy.yaml)

Example

Example file. A ZIP archive of npy files, one member per array, named <key>.npy.

small.npz2,314 bytes

NameDtypeShapeBytes
w_f32<f4float324 × 641,024
b_f16<f2float16816
e_bf16The header only says 2 opaque bytes. expected.json records this array as bfloat16.<V22 × 312
i_i32<i4int323 × 224
s_f32<f4float32[]4

Sources

Nick Reynolds · @goldnode · rev 2.8