NumPy: dtype 数据类型

dtype 数据类型

1. 你将学到


2. 故事

Alice 训练模型时准确率从 95% 暴跌到 12%。排查发现:float64 转 int8 时超过 127 的值全部溢出变负数。"dtype 不是小问题——选错类型,数据就毁了。"


3. 知识点

(1) dtype 体系

NumPy 的 dtype(data type)是数组中每个元素的类型描述。每个 ndarray 都有一个 .dtype 属性。

TEXT 📖 仅展示
np.int32    # 32-bit signed integer
np.float64  # 64-bit floating point
np.bool_    # boolean
np.str_     # unicode string
np.object_  # Python object

dtype 包含两个字段:

字段 含义 示例
kind 类型类别 'i'=int, 'f'=float, 'u'=uint, 'b'=bool
itemsize 单个元素字节数 4 for int32
PYTHON
import numpy as np

a = np.array([1, 2, 3])
print(a.dtype)       # int64 (on 64-bit platforms)
print(a.dtype.kind)  # 'i'
print(a.dtype.itemsize)  # 8
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。


(2) 整数类型对比

类型 范围 字节 别名
int8 -128 ~ 127 1
int16 -32768 ~ 32767 2
int32 -2147483648 ~ 2147483647 4 intp (32-bit)
int64 -9223372036854775808 ~ 9223372036854775807 8 intp (64-bit)
uint8 0 ~ 255 1
uint16 0 ~ 65535 2
uint32 0 ~ 4294967295 4
uint64 0 ~ 18446744073709551615 8

(3) 浮点类型对比

类型 精度(有效位) 范围 字节
float16 ~3 位十进制 ±65504 2
float32 ~7 位十进制 ±3.4e38 4
float64 ~15 位十进制 ±1.8e308 8
longdouble ~18-19 位十进制 平台相关 8/12/16

(4) 指定 dtype 创建数组

PYTHON
import numpy as np

a = np.array([1, 2, 3], dtype=np.int8)
b = np.array([1.0, 2.5], dtype=np.float32)
c = np.arange(10, dtype=np.uint8)
d = np.zeros(5, dtype=np.complex128)
e = np.array([1, 2, 3], dtype='f4')  # string shorthand for float32
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。

常用字符串简写:

简写 等价 简写 等价
'i1' int8 'u1' uint8
'i2' int16 'u2' uint16
'i4' int32 'u4' uint32
'f4' float32 'f8' float64

(5) 类型转换与溢出风险

.astype() 用于类型转换,不会修改原数组,而是返回新数组。

PYTHON
import numpy as np

a = np.array([1.7, 2.3, 3.9])
b = a.astype(np.int32)  # truncates toward zero
print(b)  # [1 2 3]
print(a)  # [1.7 2.3 3.9]  -- original unchanged
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。

类型转换风险表:

转换方向 风险 说明
float → int 精度丢失 小数部分被截断
int64 → int8 溢出 超出范围则回绕
float64 → float32 精度丢失 有效位从 15 降至 7
int → float 精度丢失 大整数可能不精确
uint → int 符号错误 高位被解释为符号位

(6) 溢出行为

NumPy 整数溢出不会报错,而是回绕(wrap around):

PYTHON
import numpy as np

a = np.array([127], dtype=np.int8)
a[0] = a[0] + 1
print(a[0])  # -128  (wrapped!)

b = np.array([200], dtype=np.uint8)
print(b)  # [200] OK for uint8

c = np.array([300 % 256], dtype=np.uint8)
print(c)  # [44]  300 mod 256 安全写法(NumPy 2.x 默认拒绝越界)
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。

np.iinfo()np.finfo() 查询范围:

PYTHON
import numpy as np

print(np.iinfo(np.int8))    # min=-128, max=127
print(np.iinfo(np.uint16))  # min=0, max=65535
print(np.finfo(np.float32)) # min=-3.4028235e+38, max=3.4028235e+38
print(np.finfo(np.float32).eps)  # 1.1920929e-07  machine epsilon
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。


(7) dtype 自动提升链

当不同 dtype 的数组运算时,NumPy 会自动提升到更"宽"的类型:

100%
graph LR
    A[bool] --> B[int8]
    B --> C[int16]
    C --> D[int32]
    D --> E[int64]
    E --> F[float16]
    F --> G[float32]
    G --> H[float64]
    I[uint8] --> J[uint16]
    J --> K[uint32]
    K --> L[uint64]
    A --> I
    L --> D
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
PYTHON
import numpy as np

a = np.array([1], dtype=np.int8)
b = np.array([1], dtype=np.float32)
c = a + b
print(c.dtype)  # float32

d = np.array([1], dtype=np.int32)
e = np.array([1], dtype=np.int64)
f = d + e
print(f.dtype)  # int64

print(np.promote_types(np.int8, np.float32))  # float32
print(np.promote_types(np.uint8, np.int8))    # int16
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。


(8) 结构化 dtype

结构化 dtype 允许数组中每个元素包含多个命名字段,类似 C 结构体或数据库行。

PYTHON
import numpy as np

dt = np.dtype([
    ('name', 'U20'),   # unicode string, max 20 chars
    ('age', 'i4'),     # 32-bit int
    ('score', 'f4'),   # 32-bit float
])

students = np.array([
    ('Alice', 20, 92.5),
    ('Bob', 22, 88.0),
], dtype=dt)

print(students['name'])   # ['Alice' 'Bob']
print(students['age'])    # [20 22]
print(students[0])        # ('Alice', 20, 92.5)
print(students[0]['score'])  # 92.5
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。

结构化 vs 普通 dtype:

特性 普通 dtype 结构化 dtype
每元素字段数 1 多个
字段命名
内存布局 连续同类型 按字段排列
访问方式 索引 字段名 + 索引
典型用途 数值计算 表格/记录数据

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。

:不同 dtype 内存对比(难度⭐)

PYTHON
import numpy as np

n = 1_000_000

a_int8 = np.zeros(n, dtype=np.int8)
a_int64 = np.zeros(n, dtype=np.int64)
a_float32 = np.zeros(n, dtype=np.float32)
a_float64 = np.zeros(n, dtype=np.float64)

print(f"int8    : {a_int8.nbytes / 1024 / 1024:.2f} MB")
print(f"int64   : {a_int64.nbytes / 1024 / 1024:.2f} MB")
print(f"float32 : {a_float32.nbytes / 1024 / 1024:.2f} MB")
print(f"float64 : {a_float64.nbytes / 1024 / 1024:.2f} MB")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。

输出:

TEXT 📖 仅展示
int8    : 0.95 MB
int64   : 7.63 MB
float32 : 3.81 MB
float64 : 7.63 MB

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。

:int8 溢出(难度⭐⭐)

PYTHON
import numpy as np

import numpy as np
import warnings
warnings.simplefilter("ignore")

# 安全写法:先把范围限制在 int8 范围内,再转 int8
a = np.array([120, 125, 127, ((128 + 256) % 256) - 256, ((130 + 256) % 256) - 256], dtype=np.int8)
# 128 wraps to -128, 130 wraps to -126
print(a)  # [ 120  125  127 -128 -126]

# NumPy 2.x 直接传入越界值会抛 OverflowError;按上面方式绕开

info = np.iinfo(np.int8)
print(f"int8 range: [{info.min}, {info.max}]")
# int8 range: [-128, 127]
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。


▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。

:float32 vs float64 精度(难度⭐⭐)

PYTHON
import numpy as np

pi_32 = np.float32(np.pi)
pi_64 = np.float64(np.pi)

print(f"float32 pi: {pi_32:.20f}")
print(f"float64 pi: {pi_64:.20f}")

# float32 has ~7 decimal digits of precision
# float64 has ~15 decimal digits of precision

a = np.array([0.1], dtype=np.float32)
b = np.array([0.1], dtype=np.float64)

print(f"float32 0.1: {a[0]:.25f}")
print(f"float64 0.1: {b[0]:.25f}")
# float32 loses precision after ~7 digits
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。


▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。

:结构化数组(难度⭐⭐⭐)

PYTHON
import numpy as np

dt = np.dtype([
    ('id', 'i4'),
    ('x', 'f8'),
    ('y', 'f8'),
    ('label', 'U10'),
])

points = np.array([
    (1, 1.5, 2.3, 'A'),
    (2, 3.1, 4.7, 'B'),
    (3, 0.8, 1.2, 'A'),
], dtype=dt)

# Access fields
print(points['x'])       # [1.5 3.1 0.8]
print(points['label'])   # ['A' 'B' 'A']

# Filter
mask = points['label'] == 'A'
print(points[mask])  # rows where label is 'A'

# Compute on field
print(f"Mean x: {points['x'].mean():.2f}")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。


▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。

:astype 数据丢失(难度⭐⭐)

PYTHON
import numpy as np

# float -> int: truncation
a = np.array([1.9, 2.1, -3.7, 0.5])
b = a.astype(np.int32)
print(f"Original : {a}")
print(f"As int32 : {b}")  # [ 1  2 -3  0]

# int64 -> int8: overflow
c = np.array([100, 200, 300], dtype=np.int64)
d = c.astype(np.int8)
print(f"Original : {c}")
print(f"As int8  : {d}")  # [100  -56   44]

# float64 -> float32: precision loss
e = np.array([1.123456789012345], dtype=np.float64)
f = e.astype(np.float32)
print(f"float64: {e[0]:.16f}")
print(f"float32: {f[0]:.16f}")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。


4. 综合示例

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。

:学生成绩表——结构化 dtype 实战(难度⭐⭐⭐)

PYTHON
import numpy as np

# Step 1: Define structured dtype
student_dt = np.dtype([
    ('name', 'U20'),
    ('age', 'i4'),
    ('score', 'f4'),
])

# Step 2: Create array with 5 records
students = np.array([
    ('Alice',   20, 95.5),
    ('Bob',     21, 87.0),
    ('Charlie', 19, 92.3),
    ('Carol',   22, 78.8),
    ('David',   20, 88.5),
], dtype=student_dt)

# Step 3: Access and analyze
print("All names:", students['name'])
print("All scores:", students['score'])
print(f"Average score: {students['score'].mean():.2f}")
print(f"Max score: {students['score'].max():.1f}")

# Step 4: Filter
high_mask = students['score'] >= 90
print("High scorers:", students[high_mask]['name'])

# Step 5: Type conversion & precision check
scores_64 = students['score'].astype(np.float64)
print(f"float32 score[0]: {students['score'][0]:.10f}")
print(f"float64 score[0]: {scores_64[0]:.10f}")

# Step 6: Check dtype info
print(f"Score dtype: {students['score'].dtype}")
print(f"Score itemsize: {students['score'].dtype.itemsize} bytes")
print(f"Array total size: {students.nbytes} bytes")

# Step 7: Safe conversion - age to uint8 (0-255 range is fine for ages)
age_uint8 = students['age'].astype(np.uint8)
age_info = np.iinfo(np.uint8)
print(f"uint8 range: [{age_info.min}, {age_info.max}]")
print(f"Ages as uint8: {age_uint8}")

# Step 8: Verify no data loss
assert np.array_equal(students['age'], age_uint8.astype(np.int32))
print("Age conversion: no data loss confirmed")
TEXT 📖 仅展示
> **输出:** 在本地 Python 环境运行 NumPy 2.x,输出 ndarray 数组内容。Piston 服务器未预装 NumPy,请在本机安装(`pip install numpy`)后实操对照。实际数值可能因 NumPy 版本、随机种子略有差异。
⚠️ 注意: 以下代码需在本地 Python 环境中运行。


❓ 常见问题

Q int8 能存多大?
A int8 范围为 -128 到 127,共 256 个值。uint8 范围为 0 到 255。超出范围的值会回绕(wrap around),不会报错。
Q float32 够用于深度学习吗?
A 大多数深度学习场景下 float32 足够,GPU 对 float32 有硬件加速。混合精度训练中甚至用 float16 做前向、float32 做梯度累加。一般不建议用 float64 训练模型。
Q 何时用 float64?
A 科学计算、金融计算、需要高精度的场景用 float64。默认 np.array([1.0]) 创建的就是 float64。如果精度不够,数值误差会快速累积。
Q 结构化数组有什么用?
A 结构化数组适合存储表格型数据(如数据库查询结果、CSV 读取内容),每个元素有多个命名字段。比 Python 列表更省内存,比 Pandas 更轻量。
Q astype 会修改原数组吗?
A 不会。.astype() 返回一个新数组,原数组不变。NumPy 中绝大多数操作都不修改原数组(inplace 操作需要显式指定,如 a *= 2)。
Q np.iinfo 和 np.finfo 有什么区别?
A np.iinfo 查询整数类型的范围(min/max),np.finfo 查询浮点类型的范围和精度(min/max/eps)。两者都需要传入 dtype 作为参数。

📖 小节

本节学习了 NumPy dtype 数据类型体系:

选对 dtype = 省内存 + 防溢出 + 保精度。dtype 不是小问题——选错类型,数据就毁了。


📝 作业

  1. 测试各整数范围:用 np.iinfo() 打印 int8、int16、int32、int64 的最小值和最大值。创建一个 int16 数组,尝试存入 40000,观察溢出结果。

  2. 创建产品结构化数组:定义结构化 dtype 包含字段:product_name(U30)、price(float32)、stock(int32)。创建 3 条产品记录,查询价格最高的产品名。

  3. float32/float64 计算 π 精度差异:分别用 np.float32(np.pi)np.float64(np.pi) 计算 π 的前 20 位小数,对比两者与 math.pi 的绝对误差,验证 float32 有效位数约 7 位。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏