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
- Saving and loading .npy binary files
- Why .npy preserves dtype and shape exactly
- Bundling many arrays with np.savez (.npz)
- Writing CSV/text with np.savetxt
- Reading text back with np.loadtxt and delimiter=
- Choosing binary vs text format
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
# TrueExpected 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]]
# float64Expected 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.
| Goal | Use |
|---|---|
| One array, exact round-trip | np.save / np.load |
| Several arrays in one file | np.savez / np.load |
| Human-readable CSV / Excel | np.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 -> TrueExpected 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.
| Task | Code |
|---|---|
| Save one array (binary) | np.save('a.npy', arr) |
| Load binary | np.load('a.npy') |
| Save many arrays | np.savez('b.npz', x=a, y=b) |
| Save / read CSV | np.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
- Previous: Structured & Record Arrays
- Next: Performance & Vectorization vs Loops — Replace slow Python loops with fast vectorized NumPy code
- Quick reference: NumPy cheat sheet
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.