在机器学习实践过程中,NumPy是一个绕不开的基础工具,它提供了高效的数组运算能力,是进行数据处理的必备技能。本指南将详细介绍如何从零开始创建NumPy数组,涵盖多种创建方式与实用技巧,帮助你快速掌握这一核心强项。
将机器学习比作一场冒险旅行,那么NumPy就是出发前必须掌握的基础技能课程,其重要性不言而喻。

在处理Python数据时,你很可能见过这一行类似“神咒”的代码:
import numpy as np
如此受推崇的原因可以总结为两点:速度快和操作简。
本文将带你从数组创建到变形运算,系统性地打牢NumPy基础——地基稳固,后续学习才能从容不迫。
最直接的方式,就是将Python列表作为参数传入:
arr = np.array([1, 2, 3])
print(arr) # [1 2 3]
可能会有人问,Python列表[1, 2, 3]也能用,为什么需要多此一举?
因为它们本质上是不同的数据类型:
| Python列表 | NumPy数组 | |
|---|---|---|
| 类型 | 可以容纳多种类型,例如int、str、dict可以混装 | 类型统一,一个数组只能包含一种数据类型 |
| 运算 | [1,2]+[3,4] 结果为 [1,2,3,4](拼接操作) | np.array([1,2])+np.array([3,4]) 结果为 [4 6](逐元素相加) |
| 速度 | 依赖于循环处理,速度较慢 | 底层采用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]
简单来说,列表侧重于“存储数据”,而数组侧重于“运算数据”。两者功能不同,不能混淆使用。
一维数组像一条线,二维数组像一张表,三维数组则像一个立体箱子。通过嵌套列表可以逐层构建更高维度的结构:
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已经预先准备好了相应的创建函数。
nz = np.zeros(10, dtype=int)
print(nz) # [0 0 0 0 0 0 0 0 0 0]
这行代码生成了十个零,排列整齐。指定dtype=int可以确保输出整数类型而非浮点数。
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.而非1)。如果需要整数,可以添加dtype=int参数。
该函数与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) # 每次运行都会输出相同的值
何时需要设置种子?在代码调试阶段。如果你的程序出现错误,需要复现问题来分析原因,就必须确保输入数据不变。种子机制就像游戏的“存档点”,帮助你在相同的条件下重复实验。
ne = np.eye(3)
print(ne)
# [[1. 0. 0.]
# [0. 1. 0.]
# [0. 0. 1.]]
主对角线上的元素为1,其余位置均为0。在线性代数中,单位矩阵是“乘法单位元”——任何矩阵与其相乘,结果保持不变。
ney = np.empty(3)
print(ney) # 输出的值是内存中的残留数据,不确定
使用np.empty就像打开一个未知内容的盒子——你无法预知里面会有什么数值。但它的创建速度极快,因为它跳过了初始化步骤,适合用于后期会逐个赋值的场景。
数组创建后,如何了解其结构?可以通过查看其属性:
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最常用的操作之一,但有一个需要特别注意的地方:切片返回的是视图,而非数据的副本。
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) # 副本独立变化
这相当于给原数据拍了一张照片,对照片的修改不会影响真实的场景。
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]]
9个元素可以重新排列成3×3的矩阵,但无法变成4×3——因为元素总数必须保持不变。
将一维数组转换为二维数组,本质是通过None插入一个长度为1的新维度:
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] # 效果相同
何时需要使用?当进行矩阵运算需要维度对齐的时候。例如,一个形状为(5,)的向量需要与一个形状为(3,5)的矩阵进行运算,首先需要将向量变为(1,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]]
NumPy还提供了两个快捷函数:
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]
每种运算都有对应的函数形式: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。
回顾一下今天搭建的“积木”知识体系:
np.array 实现从列表到数组的快速转换,zeros/ones/arange 用于快速生成标准模板random、uniform、normal 各有不同用途,seed 是调试过程中的“存档点”reshape 改变数组形状、切片返回数据视图、拼接分裂实现数据组合for 循环,用单行代码完成批量计算数组创建是 NumPy 学习道路的起点,而非终点。你可以将上面的速查表收藏备用,接下来可以继续探索以下内容:
| 场景 | 函数 | 一句话说明 |
|---|---|---|
| 从列表创建 | np.array() | 最基础的创建方式,可接受任意嵌套列表 |
| 全零 | np.zeros() | 用于初始化占位 |
| 全一 | np.ones() | 同上 |
| 等差数列 | np.arange() | 类似range函数,但直接返回数组 |
| 随机浮点 | 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 | 一个数组分割为多个 |
经过本次学习,你已经掌握了 NumPy 数组创建的核心技巧,可以在此基础上进行更深入的数据分析工作。