六月的风里带着一点甜,朋友圈也被各种童趣内容刷屏。趁着这个万物皆可“玩”的季节,我们不妨来玩点更硬核的——学习如何用 NumPy 创建数组。说到底,NumPy 数组和乐高积木有些相似:都是用最基础的单元,组合出复杂而实用的数据结构。

为什么要学NumPy?
如果把机器学习、数据分析看作一段精彩的技术旅程,那么 NumPy 就是必须掌握的基础工具,几乎绕不过去。
只要你用 Python 做数据处理、科学计算或机器学习,基本都会看到这行经典代码:
import numpy as np
为什么 NumPy 如此重要?原因很简单:速度快、写法简洁。
- 向量化运算:不用频繁写 for 循环,一行代码就能完成批量计算
- 广播机制:不同形状的数组也能灵活参与运算,这是 Python 原生列表难以做到的
这篇文章会围绕NumPy 数组创建、数组变形和常见基础操作展开,帮你一次性打好基础。只有把数组这块地基打牢,后续学习数据分析和机器学习才会更加轻松。
一、np.array:从列表到数组,一步之遥
学习 NumPy 创建数组,最直接的方法就是把一个 Python 列表传给 np.array():
arr = np.array([1, 2, 3]) print(arr) # [1 2 3]
你可能会问:Python 列表 [1, 2, 3] 不是已经能存数据了吗?为什么还要专门创建 NumPy 数组?
因为 Python 列表和 NumPy 数组本质上并不是同一种东西:
| Python列表 | NumPy数组 | |
|---|---|---|
| 类型 | 可混合存储 int、str、dict 等多种类型 | 通常要求同一数组中的元素类型一致 |
| 运算 | [1,2]+[3,4] → [1,2,3,4](拼接) |
np.array([1,2])+np.array([3,4]) → [4 6](逐元素加) |
| 速度 | 依赖 Python 层循环,速度较慢 | 底层基于 C 实现,计算性能更高 |
# Python列表:+ 是拼接 [1, 2, 3] + [4, 5, 6] # [1, 2, 3, 4, 5, 6] # NumPy数组:+ 是数学运算 np.array([1, 2, 3]) + np.array([4, 5, 6]) # [5 7 9]
简单理解:Python 列表更适合“装数据”,而 NumPy 数组更擅长“算数据”。这也是很多 Python 科学计算场景必须使用 NumPy 的关键原因。
二、嵌套列表:给积木加个维度
一维数组像一条线,二维数组像一张表,三维数组则更像一个数据立方体。使用嵌套列表,就能逐步创建多维 NumPy 数组:
list1 = [list(range(i, i+3)) for i in [2, 4, 6]] print(list1) # [[2, 3, 4], [6, 7, 8], [10, 11, 12]] c = np.array([range(i, i+3) for i in [2, 4, 6]]) print(c) # [[ 2 3 4] # [ 6 7 8] # [10 11 12]]
可以把每一层列表理解为数组中的一层结构,层层嵌套后,就能构建出二维、三维甚至更高维度的 NumPy 数组。
三、快速创建:NumPy的“模板工厂”
在实际开发中,很多数组并不需要手动逐个填写元素。比如全 0 数组、全 1 数组、等差数列数组,NumPy 都提供了高效的创建方法。
np.zeros:全0数组
nz = np.zeros(10, dtype=int) print(nz) # [0 0 0 0 0 0 0 0 0 0]
这会快速生成一个包含 10 个 0 的 NumPy 数组。通过 dtype=int 可以指定数据类型为整数,否则默认通常是浮点型。
np.ones:全1数组
no = np.ones((3, 5)) print(no) # [[1. 1. 1. 1. 1.] # [1. 1. 1. 1. 1.] # [1. 1. 1. 1. 1.]]
这里创建的是一个 3 行 5 列的全 1 数组。默认结果是浮点数形式,如果你想创建整型数组,同样可以添加 dtype=int。
np.arange:等差数列
np.arange() 和 Python 内置的 range() 很像,但它返回的是 NumPy 数组,更适合后续数值计算。
na = np.arange(0, 20, 2) print(na) # [ 0 2 4 6 8 10 12 14 16 18]
对比来看更直观:
list(range(0, 20, 2)) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] ← 列表 np.arange(0, 20, 2) # [ 0 2 4 6 8 10 12 14 16 18] ← 数组
表面上它们结果相似,但底层实现完全不同。前者是 Python 列表,后者是适合数值运算的 NumPy 数组连续内存结构。
四、随机数组:开盲盒时间
在数据分析、机器学习和模拟实验中,随机数组是非常常见的需求。NumPy 提供了丰富的随机数生成方法。
基础随机
a1 = np.random.random() # 一个[0,1)的随机浮点数 a2 = np.random.random(1) # 包含一个元素的数组 a3 = np.random.random((3, 3)) # 3×3的随机数组 a4 = np.random.random((2, 3, 4)) # 2层×3行×4列,三维随机数组
指定范围
a5 = np.random.uniform(5, 7, (2, 2)) # [5,7)之间的随机浮点,2×2 a6 = np.random.randint(5, 7, (2, 2)) # [5,7)之间的随机整数,2×2
uniform 用来生成指定区间内的随机浮点数,randint 则用于生成指定范围内的随机整数,参数格式基本一致,使用起来很方便。
正态分布
正态分布是数据科学中最常见的概率分布之一,其特点是:大量数据集中在中间位置,越靠近两端越少。人的身高、考试成绩、测量误差等场景,通常都近似服从正态分布。
# 均值0,标准差1,3×3的正态分布随机数组 np.random.normal(0, 1, (3, 3)) # 简写版:标准正态分布 np.random.randn(3, 3)
种子:给随机数写剧本
所谓随机数,很多时候其实是“伪随机数”,也就是由算法按规则生成的结果。设置随机种子之后,就可以让每次程序运行时生成完全相同的数据。
np.random.seed(0) a = np.random.random(3) print(a) # 每次运行都输出相同的值
这个功能在调试代码、复现实验结果、机器学习训练时非常重要。只要输入数据保持一致,你就能稳定复现问题和结果。
五、特殊数组:预制件
np.eye:单位矩阵
ne = np.eye(3) print(ne) # [[1. 0. 0.] # [0. 1. 0.] # [0. 0. 1.]]
np.eye() 用于创建单位矩阵,也就是主对角线元素为 1、其他位置全为 0 的矩阵。在线性代数中,它相当于矩阵乘法里的“1”。
np.empty:未初始化的数组
ney = np.empty(3) print(ney) # 值是内存里的残留,不确定
np.empty() 会创建一个未初始化的数组,里面的值通常是内存中残留的数据,因此结果不确定。它的优势是创建速度非常快,适合后续会立刻对每个元素重新赋值的场景。
六、数组属性:读懂你的积木
数组创建完成后,我们通常还需要查看它的维度、形状、大小和类型等信息。这时就要用到 NumPy 数组属性。
x = np.random.randint(10, size=(3, 4, 5)) # 3层×4行×5列
| 属性 | 含义 | 本例值 |
|---|---|---|
ndim |
维度数 | 3 |
shape |
各维度大小 | (3, 4, 5) |
size |
元素总数 | 60 |
dtype |
数据类型 | int64 |
itemsize |
单个元素占的字节数 | 8 |
nbytes |
总字节数 | 480 |
print('维度:', x.ndim) # 3
print('形状:', x.shape) # (3, 4, 5)
print('大小:', x.size) # 60
print('类型:', x.dtype) # int64
其中,nbytes = itemsize × size,这个关系在估算数组内存占用时很有帮助。
七、切片:精准取值,小心副作用
数组切片是 NumPy 中最常用的操作之一,但初学者特别容易忽略一个关键点:NumPy 切片通常返回的是视图,而不是副本。
x = np.array([[3, 5, 2, 4],
[7, 6, 8, 8],
[1, 6, 7, 7]])
x_sub = x[:2, :2] # 前两行、前两列
print(x_sub)
# [[3 5]
# [7 6]]
所谓视图,就是切片和原数组共享同一份底层数据。
x_sub[0, :] = 9 print(x_sub) # [[9 9] [7 6]] print(x) # 原数组也被改了! # [[9 9 2 4] # [9 9 8 8] # [1 6 7 7]]
也就是说,当你修改切片结果时,原始数组往往也会同步发生变化。这在提升性能的同时,也容易带来意想不到的副作用。
如果你需要独立操作,就使用 .copy() 创建副本:
x_sub_copy = x[:2, :2].copy() x_sub_copy[0, :] = 1 print(x) # 原数组不受影响 print(x_sub_copy) # 副本独立变化
这样得到的是一份真正独立的数据副本,对它的修改不会影响原始 NumPy 数组。
八、变形:reshape与newaxis
reshape:改变形状,不改内容
grid = np.arange(1, 10) # [1 2 3 4 5 6 7 8 9] grid_reshape = grid.reshape((3, 3)) print(grid_reshape) # [[1 2 3] # [4 5 6] # [7 8 9]]
reshape() 可以修改数组形状,但不会改变元素本身。要注意的是,变形前后元素总数必须保持一致。比如 9 个元素可以重塑成 3×3,但不能变成 4×3。
np.newaxis:增加一个维度
把一维数组转换成二维数组,本质上就是插入一个长度为 1 的新维度,np.newaxis 或 None 都能实现这一点:
x = np.array([1, 2, 3, 4, 5]) # 变成一行(1×5) x.reshape(1, 5) # [[1 2 3 4 5]] x[np.newaxis, :] # 效果一样 # 变成一列(5×1) x.reshape(5, 1) # [[1] [2] [3] [4] [5]] x[:, np.newaxis] # 效果一样
在做矩阵运算、广播计算和维度对齐时,np.newaxis 非常实用。比如一个形状为 (5,) 的向量要与一个 (3,5) 的矩阵进行运算时,往往需要先调整维度。
九、拼接与分裂:合体与分身
拼接:多个变一个
x = np.array([1, 2, 3]) y = np.array([3, 2, 1]) z = np.array([99, 99, 99]) # 一维拼接 np.concatenate([x, y, z]) # [ 1 2 3 3 2 1 99 99 99]
如果是二维数组,则需要明确指定拼接方向:
grid = np.array([[1, 2, 3],
[4, 5, 6]])
# axis=0:上下叠(行方向)
np.concatenate([grid, grid])
# [[1 2 3]
# [4 5 6]
# [1 2 3]
# [4 5 6]]
# axis=1:左右拼(列方向)
np.concatenate([grid, grid], axis=1)
# [[1 2 3 1 2 3]
# [4 5 6 4 5 6]]
此外,还有两个常用快捷函数:
np.vstack([x, y, z, grid]) # 垂直栈,等价于axis=0 np.hstack([grid, grid]) # 水平栈,等价于axis=1
分裂:一个变多个
x = [0, 1, 2, 3, 4, 5, 6, 7, 8] x1, x2, x3 = np.split(x, [3, 5]) print(x1, x2, x3) # [0 1 2] [3 4] [5 6 7 8]
其基本规则是:np.split(数组, [断点列表]),如果有 N 个断点,就会切出 N+1 段结果。
对于二维数组,也有按行和按列拆分的专用方法:
grid = np.arange(16).reshape(4, 4) # 垂直切:按行分 upper, lower = np.vsplit(grid, [2]) # 水平切:按列分 left, right = np.hsplit(grid, [1])
十、通用函数:向量化的魔法
NumPy 最强大的地方之一,就是可以直接对整个数组进行运算,而不需要手写 for 循环,这就是常说的向量化计算。
x = np.arange(4)
print('x =', x)
print('x + 5 =', x + 5) # [5 6 7 8]
print('x - 5 =', x - 5) # [-5 -4 -3 -2]
print('x * 2 =', x * 2) # [0 2 4 6]
print('x / 2 =', x / 2) # [0. 0.5 1. 1.5]
print('x // 2 =', x // 2) # [0 0 1 1] 取商
print('x % 2 =', x % 2) # [0 1 0 1] 取余
print('x ** 2 =', x ** 2) # [0 1 4 9] 幂运算
print('-x =', -x) # [0 -1 -2 -3]
这些运算背后也都有对应的 NumPy 函数版本,例如:np.add、np.subtract、np.multiply、np.divide、np.power 等。
绝对值
x = np.array([-2, -1, 0, 1, 2]) print(np.abs(x)) # [2 1 0 1 2] print(np.absolute(x)) # 效果一样
复数的模
y = np.array([3 - 4j, 4 - 3j, 2 + 0j, 0 + 1j]) print(abs(y)) # [5. 5. 2. 1.]
对于复数来说,取绝对值返回的是模,计算方式是实部平方与虚部平方相加后再开根号。例如 3-4j 的模就是 √(9+16)=5。
小时候玩积木,一块一块往上搭,最后可以拼出城堡、飞船,甚至一整座城市。现在学习 NumPy,也是从一个个数组开始创建、变形、拼接,最终实现数据处理、模型训练和真实问题求解。
工具虽然变了,但底层逻辑没有变:从最基础的单元开始,持续搭建,最终看清完整结构。
写在最后
最后再回顾一下这篇 NumPy 数组创建指南的核心内容:
- 基础创建:
np.array可将列表快速转为数组,zeros/ones/arange适合高效生成常用模板数组 - 随机数组:
random、uniform、normal各有应用场景,seed则有助于结果复现与调试 - 变形与操作:
reshape用于数组变形,切片默认返回视图,拼接与分裂可以灵活组合数据 - 向量化运算:摆脱
for循环,直接完成批量数值计算,是 NumPy 高性能的关键
NumPy 数组创建只是起点,却是非常重要的一步。建议把这份内容当作入门速查参考,后续你还可以继续深入学习:
- 索引与切片进阶 — 花式索引、布尔索引等更高阶的数组取值方式
- 广播机制 — 不同形状的 NumPy 数组如何自动对齐并参与计算
- 线性代数 — 矩阵乘法、逆矩阵、特征值分解等核心能力
- 实战项目 — 使用 NumPy 处理图像、分析数据、支持机器学习任务
速查表
| 场景 | 函数 | 一句话说明 |
|---|---|---|
| 从列表创建 | np.array() |
NumPy 创建数组最基础的方法 |
| 全零 | np.zeros() |
常用于初始化和占位 |
| 全一 | np.ones() |
快速生成全 1 数组 |
| 等差数列 | np.arange() |
类似 range,但返回 NumPy 数组 |
| 随机浮点 | np.random.random() |
生成 [0,1) 区间随机数 |
| 指定范围随机 | np.random.uniform() |
生成自定义区间浮点数 |
| 随机整数 | np.random.randint() |
生成指定范围整数 |
| 正态分布 | np.random.normal() |
生成高斯分布随机数 |
| 固定种子 | np.random.seed() |
让随机结果可复现 |
| 单位矩阵 | np.eye() |
对角线为 1,其余为 0 |
| 未初始化 | np.empty() |
创建速度快,但值不确定 |
| 变形 | reshape |
只改形状,不改数据内容 |
| 加维度 | np.newaxis |
常用于一维转二维 |
| 拼接 | concatenate/vstack/hstack |
把多个数组合并起来 |
| 分裂 | split/vsplit/hsplit |
把一个数组拆成多个部分 |
