File I/O: save, load, savetxt

Reviewed & published by Brayan K

NumPy file I/O is the set of functions that write arrays to disk and read them back — np.save and np.load for compact binary .npy files, np.savez for bundling several arrays into one .npz archive, and np.savetxt and np.loadtxt for human-readable CSV and text. In this lesson you'll round-trip arrays through binary and text formats, save multiple arrays at once, control the CSV delimiter and number format, and learn when each format is the right choice.

Part of the free NumPy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1 np.save and np.load (.npy binary)

np.save('name.npy', arr) writes an array to a compact binary file, and np.load('name.npy') reads it back. The .npy format stores the exact dtype and shape, so what you load is byte-for-byte identical to what you saved.

import numpy as np

# A 2x3 integer array
arr = np.array([[1, 2, 3],
                [4, 5, 6]])

# Save to a binary .npy file, then load it straight back
np.save('my_array.npy', arr)
loaded = np.load('my_array.npy')

print(loaded)
# [[1 2 3]
#  [4 5 6]]
print(loaded.shape, loaded.dtype)   # (2, 3) int64
print(np.array_equal(arr, loaded))  # True (the round-trip is exact)

# ✅ Expected output:
# [[1 2 3]
#  [4 5 6]]
# (2, 3) int64
# True

Expected output: the same 2×3 array, (2, 3) int64, and True — the data round-trips perfectly.

2 np.savez: Many Arrays in One File (.npz)

Need to store several arrays together? np.savez('bundle.npz', x=a, y=b) packs them into one .npz archive. Loading returns a dictionary-like object; read each array back by the keyword name you gave it.

import numpy as np

a = np.arange(5)             # [0 1 2 3 4]
b = np.array([1.5, 2.5])     # [1.5 2.5]

# Save both arrays under names 'x' and 'y'
np.savez('bundle.npz', x=a, y=b)

# Load returns a dict-like object; read arrays by name
data = np.load('bundle.npz')
print(data.files)   # ['x', 'y']
print(data['x'])    # [0 1 2 3 4]
print(data['y'])    # [1.5 2.5]

# ✅ Expected output:
# ['x', 'y']
# [0 1 2 3 4]
# [1.5 2.5]

Expected output: ['x', 'y'], then [0 1 2 3 4] and [1.5 2.5].

3 np.savetxt and np.loadtxt (CSV/text)

When a human or another program (Excel, R, a database importer) must read the file, write text. np.savetxt('data.csv', arr, delimiter=',', fmt='%.2f') controls the separator and number format; np.loadtxt('data.csv', delimiter=',') reads it back into an array.

Handy savetxt options: delimiter=',' for CSV, fmt='%.2f' for two decimals, fmt='%d' for integers, and header='a,b,c' for a comment header line.

import numpy as np

arr = np.array([[1.234, 2.0],
                [3.5,   4.75]])

# Write a CSV with 2 decimal places
np.savetxt('data.csv', arr, delimiter=',', fmt='%.2f')

# The file 'data.csv' now contains:
# 1.23,2.00
# 3.50,4.75

# Read it back as floats
loaded = np.loadtxt('data.csv', delimiter=',')

print(loaded)
# [[1.23 2.  ]
#  [3.5  4.75]]
print(loaded.dtype)   # float64

# ✅ Expected output:
# [[1.23 2.  ]
#  [3.5  4.75]]
# float64

Expected output: the array reloaded as floats, with values rounded to 2 decimals, dtype float64. (Note the tiny precision loss from text rounding.)

4 Binary vs Text: When to Use Which

The choice comes down to who reads the file next. If it is your own NumPy program, use binary — it is faster, smaller, and lossless. If a person or a non-NumPy tool needs it, use text.

GoalUse
One array, exact round-tripnp.save / np.load
Several arrays in one filenp.savez / np.load
Human-readable CSV / Excelnp.savetxt / np.loadtxt
import numpy as np

# Floats with lots of precision
arr = np.array([1/3, 2/3, 1/7])

# Binary preserves full precision; text with %.2f rounds it
np.save('exact.npy', arr)
print(np.load('exact.npy'))      # [0.33333333 0.66666667 0.14285714]

np.savetxt('rough.csv', arr, fmt='%.2f')
print(np.loadtxt('rough.csv'))   # [0.33 0.67 0.14]

# ✅ Expected output:
# [0.33333333 0.66666667 0.14285714]
# [0.33 0.67 0.14]

Expected output: the binary reload matches the original; the text reload is rounded to [0.33 0.67 0.14].

🎯 YOUR TURN: Fill in the Blanks

Replace each ___ so the program saves an array to a binary file and loads it back unchanged.

import numpy as np

scores = np.array([88, 92, 79, 100])

# Save to a binary .npy file
np.___('scores.npy', scores)        # fill in the save function

# Load it back
loaded = np.___('scores.npy')       # fill in the load function

print(loaded)                       # [ 88  92  79 100]
print(np.array_equal(scores, ___))  # fill in the variable -> True

Expected output: [ 88 92 79 100] then True. (Answers: save, load, loaded.)

⚠️ Common Errors & Quick Reference

❌ ValueError: could not convert string to float

np.loadtxt hit a header row or text it could not parse as a number.

✅ Fix: skip the header with skiprows=1, or use np.genfromtxt for messy data.

❌ Wrong delimiter splits the file incorrectly

By default savetxt uses spaces; reading a comma file without delimiter=',' fails.

✅ Fix: pass the same delimiter to both savetxt and loadtxt.

TaskCode
Save one array (binary)np.save('a.npy', arr)
Load binarynp.load('a.npy')
Save many arraysnp.savez('b.npz', x=a, y=b)
Save / read CSVnp.savetxt(...) / np.loadtxt(...)

🏆 Mini Challenge: Save a Table as CSV

Save a 2D integer table to a CSV with a header line, then load just the numbers back.

import numpy as np

table = np.array([[1, 2, 3],
                  [4, 5, 6]])

# Save as integer CSV with a comment header
np.savetxt('table.csv', table, delimiter=',', fmt='%d', header='a,b,c')

# loadtxt ignores lines starting with '#' by default
loaded = np.loadtxt('table.csv', delimiter=',')

print(loaded)
# [[1. 2. 3.]
#  [4. 5. 6.]]
print(loaded.shape)   # (2, 3)

# ✅ Expected output:
# [[1. 2. 3.]
#  [4. 5. 6.]]
# (2, 3)

❓ Frequently Asked Questions

Lesson complete — your arrays can live on disk!

You can now round-trip arrays through binary .npy and .npz files, write and read CSV text with custom delimiters and formats, and pick the right format for the job.

Practice quiz

What kind of file does np.save create?

  • A compact binary .npy file
  • A CSV text file
  • A JSON file
  • An Excel spreadsheet

Answer: A compact binary .npy file. np.save writes a binary .npy file that preserves dtype and shape exactly.

Which function reads a .npy file back into an array?

  • np.read
  • np.load
  • np.open
  • np.loadtxt

Answer: np.load. np.load reads .npy and .npz files created by np.save and np.savez.

How do you store several named arrays in one file?

  • np.save
  • np.savetxt
  • np.savez
  • np.dump

Answer: np.savez. np.savez('file.npz', a=arr1, b=arr2) bundles arrays into one .npz archive.

What does np.savetxt write?

  • A binary .npy file
  • A pickled object
  • A compressed archive
  • A human-readable text/CSV file

Answer: A human-readable text/CSV file. savetxt writes plain text, ideal when a human or another tool must read it.

After np.load('bundle.npz'), how do you read an array saved as x?

  • data['x']
  • data.x()
  • data.read('x')
  • load('x')

Answer: data['x']. An npz load returns a dict-like object; index it by the name you saved, data['x'].

What dtype does np.loadtxt return by default for a numeric CSV?

  • int64
  • object
  • float64
  • bool

Answer: float64. loadtxt parses values as float64 unless you specify a dtype.

Which savetxt argument sets the column separator for a CSV?

  • sep=
  • delimiter=
  • split=
  • comma=

Answer: delimiter=. delimiter=',' tells savetxt and loadtxt to use commas between values.

Why does np.save preserve data exactly while np.savetxt may not?

  • save compresses better
  • Binary .npy keeps full dtype and precision; text can round
  • savetxt drops the array
  • loadtxt always fails

Answer: Binary .npy keeps full dtype and precision; text can round. Binary stores exact bytes; text formatting like %.2f can lose precision.

How do you skip a header row when reading a CSV with np.loadtxt?

  • header=True
  • ignore=1
  • skiprows=1
  • skip=1

Answer: skiprows=1. skiprows=1 skips the first line; np.genfromtxt is also good for messy data.

When should you choose a text format over a binary .npy file?

  • Always, text is faster
  • When a person or a non-NumPy tool must read it
  • When you need exact precision
  • When saving many arrays

Answer: When a person or a non-NumPy tool must read it. Use text when humans or other programs read it; use binary for exact NumPy round-trips.

Continue this course

Frequently asked questions

What is the difference between np.save and np.savetxt?

np.save writes a compact .npy binary file that preserves the exact dtype and shape and reloads instantly with np.load. np.savetxt writes a human-readable text file (such as a CSV) but loses some precision and shape information. Use binary for round-tripping NumPy data and text when a human or another program must read it.

How do I save several arrays in one file?

Use np.savez('file.npz', a=arr1, b=arr2). It bundles multiple arrays into a single .npz archive. Load it back with data = np.load('file.npz'), then read each array by the name you gave it: data['a'], data['b'].

Why does np.load need allow_pickle for some files?

Plain numeric arrays load safely by default. Object arrays (those holding Python objects) are stored with pickle, which np.load refuses to read unless you pass allow_pickle=True. Only enable it for files you fully trust, because pickle can execute arbitrary code.

How do I read a CSV into a NumPy array?

Use np.loadtxt('data.csv', delimiter=','). Add skiprows=1 to skip a header line, and dtype=... if you need a specific type. For messy files with missing values, np.genfromtxt is a more forgiving alternative.