Skip to content

第 9 章 文件读写

学习目标

  • 掌握二进制格式:np.save / np.load(.npy)
  • 掌握文本格式:np.savetxt / np.loadtxt(CSV、带表头)
  • 会用 np.genfromtxt 处理缺失值
  • 能选择合适的格式读写数据

9.1 为什么需要文件读写

程序运行时,数据在内存里;程序退出,内存释放,数据消失。持久化是把数组保存到磁盘文件,下次运行再读回来。

NumPy 提供两类方式:

  1. 二进制格式(.npy):保存精确的 dtype 与形状,速度最快,但只能用 NumPy 读。
  2. 文本格式(CSV/TXT):人类可读、可用 Excel 打开,但体积大、速度慢,数字精度受限。

原则:自己程序之间交换数据用 .npy;给别人看、或与其他软件对接用文本。

9.2 二进制格式:save 与 load

np.save(文件名, 数组) 保存,np.load(文件名) 读回:

python
import numpy as np

a = np.arange(12).reshape(3, 4)
np.save("demo.npy", a)

b = np.load("demo.npy")
print(b)

输出:

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

要点:

  • 文件名建议以 .npy 结尾(不加时 save 会自动补)。
  • load 读回的数组,形状、dtype 与保存时完全一致。
  • 保存多个数组可用 np.savez(把数组打包进一个 .npz),入门先用 save

9.3 文本格式:savetxt 与 loadtxt

np.savetxt(文件名, 数组, delimiter=分隔符, fmt=格式) 把数组写成文本;np.loadtxt(文件名, delimiter=分隔符) 读回。

保存并读回一个成绩表:

python
import numpy as np

scores = np.arange(12).reshape(3, 4)    # 3 名学生 × 4 门课
np.savetxt("scores.txt", scores, fmt="%d", delimiter=",")

fmt="%d" 表示按整数格式写出。生成的文件内容:

0,1,2,3
4,5,6,7
8,9,10,11

读回:

python
import numpy as np

data = np.loadtxt("scores.txt", delimiter=",")
print(data)
print(data.dtype)    # 文本读回默认是浮点

输出:

[[ 0.  1.  2.  3.]
 [ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]]
float64

loadtxt 默认把数字读成浮点;要读回整数,加 dtype=int:

python
import numpy as np

data = np.loadtxt("scores.txt", delimiter=",", dtype=int)
print(data.dtype)

输出:

int64

9.4 带表头的文件

np.savetxtheader 参数写表头行,comments 设为空字符串避免表头前加 #;读取时用 skiprows=1 跳过表头:

python
import numpy as np

scores = np.arange(12).reshape(3, 4)
np.savetxt("scores2.txt", scores, fmt="%d", delimiter=",",
           header="math,eng,phy,chem", comments="")

data = np.loadtxt("scores2.txt", delimiter=",", skiprows=1)
print(data)

文件内容(前两行):

math,eng,phy,chem
0,1,2,3

读取输出:

[[ 0.  1.  2.  3.]
 [ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]]

9.5 缺失值:genfromtxt

loadtxt 遇到空单元格或缺失值会直接报错。np.genfromtxt 是更宽容的版本:缺失位置填入 nan(浮点数组)。

准备一个含缺失的成绩文件 missing.csv:

1,2,3
4,,6
7,8,

读回:

python
import numpy as np

data = np.genfromtxt("missing.csv", delimiter=",")
print(data)

输出:

[[ 1.  2.  3.]
 [ 4. nan  6.]
 [ 7.  8. nan]]

缺失值成为 nan,之后用第 8 章的掩码排除:

python
import numpy as np

data = np.genfromtxt("missing.csv", delimiter=",")
valid = data[~np.isnan(data)]
print("有效成绩平均:", valid.mean())

输出:

有效成绩平均: 4.428571428571429

9.6 完整实践:成绩单的保存与读取

把程序流程串起来:生成成绩 → 保存 → 读回 → 统计。

python
import numpy as np

# 1. 模拟 4 名学生的 3 门课成绩(0~100 随机整数)
rng = np.random.default_rng(42)
scores = rng.integers(60, 101, size=(4, 3))
np.savetxt("grades.csv", scores, fmt="%d", delimiter=",",
           header="math,eng,phy", comments="")

# 2. 读回
data = np.loadtxt("grades.csv", delimiter=",", skiprows=1, dtype=int)

# 3. 统计
print("每门课平均分:", data.mean(axis=0))
print("每个学生总分:", data.sum(axis=1))
print("全班最高分:", data.max())

输出:

每门课平均分: [78.   85.   88.25]
每个学生总分: [239 264 257 264]
全班最高分: 98

动手实践

  1. 创建一个 5×5 数组,用 np.save 保存为 my.npy,重启交互环境后 np.load 读回,验证形状与 dtype。
  2. 把同样的数组用 savetxt 写成 CSV,用文本编辑器打开查看内容,再用 loadtxt 读回。
  3. 给 CSV 加上表头后保存,读取时用 skiprows=1,验证读回正确。
  4. 手写一个含空格的 CSV,用 genfromtxt 读回,用掩码统计有效值平均。

常见错误

错误写法现象原因
忘记 delimiter 参数读回结果不对或报错保存与读取的 delimiter 必须一致
读取带表头的文件不用 skiprows表头被当作数据报错文本第 1 行不是数字,要跳过
savetxt 后表头带 #文件第一行是 #math,...comments 默认是 #,设为 "" 去掉
loadtxt 读含空格的 CSVValueError: could not convert string to float: ''空单元格要用 genfromtxt
用文本格式保存浮点读回精度下降文本只保留有效数字;高精度需求用 .npy
保存后马上读,路径不对FileNotFoundError注意保存与读取的目录一致(用相对路径时在当前工作目录)

章末练习

基础

  1. np.arange(20).reshape(4, 5) 保存为 arr.npy 并读回,验证相等。
  2. np.array([[1, 2], [3, 4], [5, 6]])savetxt 写成逗号分隔的 CSV,再用 loadtxt 读回并打印。
  3. 给上面的 CSV 加表头 a,b 保存,用 skiprows=1 读回。

提高

  1. 生成 10 名学生的 5 门课成绩(60~100 整数),保存为带表头的 CSV,读回后输出:每门课平均分、不及格(<60)人数。
  2. 手写一个 3 行 4 列的 CSV,其中包含 2 个空单元格,用 genfromtxt 读回,统计每行有效值的平均。

挑战

  1. savetxtfmt="%.2f" 保存一个浮点数组,读回后与原始数组比较,记录误差;再换成 .npy 保存读回,比较两者误差,解释差异。
  2. 写一个完整的「成绩登记程序」:从 grades.csv 读数据,计算并输出每个学生的总分与排名(降序),把排名写回一个新 CSV。

章末自测

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. np.savenp.load 使用的文件格式是?
    • A. CSV
    • B. .npy(二进制)
    • C. JSON
    • D. TXT
  2. np.savetxt("a.csv", a, delimiter=",")delimiter 的作用是?
    • A. 指定文件编码
    • B. 指定列分隔符
    • C. 指定小数位数
    • D. 指定表头
  3. loadtxt 默认把数字读成什么类型?
    • A. int64
    • B. float64
    • C. str
    • D. bool
  4. 读取带表头的 CSV,应使用参数?
    • A. header=1
    • B. skiprows=1
    • C. comments=1
    • D. names=1
  5. loadtxt 遇到空单元格会?
    • A. 自动填 0
    • B. 报错
    • C. 填入 nan
    • D. 跳过该行
  6. genfromtxt 遇到空单元格会?
    • A. 自动填 0
    • B. 报错
    • C. 填入 nan
    • D. 跳过该行
  7. 需要与 Excel 交换数据,应使用?
    • A. np.save
    • B. savetxt 写成 CSV
    • C. savez
    • D. np.load
  8. savetxtfmt="%d" 表示?
    • A. 按整数格式写出
    • B. 按浮点格式写出
    • C. 按字符串写出
    • D. 指定小数位数
  9. 读回整数数组的正确写法是?
    • A. np.loadtxt(f, dtype=float)
    • B. np.loadtxt(f, dtype=int)
    • C. np.loadtxt(f, fmt="%d")
    • D. np.loadtxt(f, delimiter=",")
  10. .npy 相比 CSV 的优势是?
    • A. 人类可读
    • B. 保存精确的 dtype 且速度快
    • C. 可以用 Excel 打开
    • D. 体积更小一定