Python学习笔记·NumPy篇:数组、矩阵与线性代数——零数学基础也能看懂的白话讲解

第1天(6月18日):数组的创建与基本操作

1.1 什么是NumPy?

大白话 :NumPy是Python的"科学计算器"。Python的列表只能一个一个处理数据,NumPy的数组可以一次性对所有数据做数学运算,而且速度飞快。

1.2 创建数组
python 复制代码
import numpy as np

# 从列表创建数组(最常用)
np.array([1, 2, 3, 4, 5])

# 从元组创建
np.array((1, 2, 3, 4, 5))

# 从range创建
np.array(range(5))

# 二维数组(表格形式)
np.array([[1, 2, 3], [4, 5, 6]])
1.3 快速生成数组的常用函数
python 复制代码
np.arange(8)              # 像range一样,生成0到7的数组
np.arange(1, 10, 2)       # 起始1,结束10,步长2 → [1,3,5,7,9]
np.linspace(0, 10, 11)    # 0到10之间生成11个等间距的数
np.zeros(3)               # 生成3个0 → [0, 0, 0]
np.zeros((3, 3))          # 3行3列的零矩阵
np.ones(3)                # 生成3个1
np.identity(3)            # 3行3列的单位矩阵(对角线为1,其余为0)
np.diag([1, 2, 3, 4])     # 对角矩阵,对角线上的值是1,2,3,4

# 随机数数组
np.random.randint(0, 50, 5)        # 0-50之间随机5个整数
np.random.randint(0, 50, (3, 5))   # 0-50之间随机,3行5列
np.random.rand(10)                 # 10个0-1之间的随机小数
np.random.standard_normal(5)       # 5个符合标准正态分布的随机数

记忆技巧

  • arange = array + range(数组版的range)
  • linspace = linear + space(线性空间,等间距)
  • zeros = 全是零
  • ones = 全是一
1.4 修改数组中的元素
python 复制代码
x = np.arange(8)

# 追加元素(返回新数组,不改变原数组)
np.append(x, 8)             # 末尾加一个
np.append(x, [9, 10])       # 末尾加多个

# 原地修改(会改变原数组)
x[3] = 8                    # 直接改下标3的值

# 插入元素(返回新数组)
np.insert(x, 1, 8)          # 在下标1处插入值8

# 二维数组的修改
x = np.array([[1,2,3], [4,5,6], [7,8,9]])
x[0, 2] = 4                 # 改第0行第2列为4
x[1:, 1:] = 1               # 行≥1且列≥1的区域全改成1
x[1:, 1:] = [[1,2],[3,4]]   # 同时改多个值(注意形状要匹配)
1.5 数组的运算

数组与常量运算 :数组里的每个元素都和常量做运算。

python 复制代码
x = np.array([1, 2, 3, 4, 5])

# 数组在前,常量在后
x * 2      # [2, 4, 6, 8, 10]  每个元素乘2
x + 2      # [3, 4, 5, 6, 7]   每个元素加2
x ** 3     # [1, 8, 27, 64, 125] 每个元素的3次方

# 常量在前,数组在后(注意结果不同!)
2 ** x     # [2, 4, 8, 16, 32]  2的x次方
2 / x      # 2除以每个元素

数组与数组运算

python 复制代码
# 等长数组:对应位置元素运算
np.array([1,2,3]) + np.array([4,3,2])  # [5,5,5]

# 不等长但可广播:短的会"扩展"
a = np.array([1,2,3])
b = np.array([[1,2,3],[4,5,6],[7,8,9]])
a * b   # a的每个元素乘b对应列的元素
a + b   # a的每个元素加b对应列的元素

广播原则:如果两个数组维度不同,NumPy会尝试把小数组"扩展"到和大数组一样的形状。如果形状不兼容,就会报错。

1.6 数组的排序
python 复制代码
x = np.array([3, 1, 2, 4])

# argsort()返回排序后的下标(不改变原数组)
np.argsort(x)      # [1, 2, 0, 3]  对应 1→2→3→4
x[np.argsort(x)]   # [1, 2, 3, 4]  用下标取出排序后的值

# 最大/最小值的下标
x.argmax()         # 3(最大值4的下标)
x.argmin()         # 1(最小值1的下标)

# sort()是原地排序(改变原数组)
x.sort()           # x 变成 [1, 2, 3, 4]
1.7 访问数组元素
python 复制代码
b = np.array([[1,2,3],[4,5,6],[7,8,9]])

# 基础访问
b[0]           # 第0行所有元素 → [1,2,3]
b[0, 2]        # 第0行第2列 → 3
b[0][2]        # 等价写法

# 花式索引(用列表做下标)
b[[0, 1]]                    # 第0行和第1行
b[[0, 2, 1], [2, 1, 0]]     # 分别取(0,2), (2,1), (1,0)

# 切片访问
a = np.arange(10)
a[::-1]        # 反向 → [9,8,7,6,5,4,3,2,1,0]
a[::2]         # 隔一个取一个 → [0,2,4,6,8]
a[:5]          # 前5个 → [0,1,2,3,4]
1.8 改变数组形状
python 复制代码
x = np.arange(1, 11)  # [1,2,3,...,10]

# 方法1:修改shape属性(原地修改)
x.shape = 2, 5          # 变成2行5列
x.shape = 5, -1         # 5行,-1表示自动计算列数

# 方法2:reshape()(返回新数组,原数组不变)
x.reshape(2, 5)

# 方法3:resize()(可改变元素总数)
x.resize((1, 10))       # 变成1行10列,多的填0
np.resize(x, (1, 3))    # numpy的resize返回新数组

三种方法对比

方法 是否修改原数组 能否改变元素总数
x.shape = ... 原地修改 不能
x.reshape(...) 返回新数组 不能
x.resize(...) 原地修改 可以(多出填0)
1.9 数组的内积运算

大白话:内积 = 对应位置元素相乘,再把乘积全部加起来。

python 复制代码
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])

# 三种等价写法
np.dot(x, y)     # 32
x.dot(y)         # 32
sum(x * y)       # 32

# 计算过程:1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32
1.10 比较两个数组
python 复制代码
x = np.array([1, 2, 3, 4.001, 5])
y = np.array([1, 1.999, 3, 4.01, 5.1])

np.allclose(x, y)              # 所有元素都接近?返回True/False
np.allclose(x, y, atol=0.2)    # 设置绝对误差范围
np.isclose(x, y)               # 逐个位置判断,返回布尔数组

第2天(6月22日):函数运算、布尔筛选与条件处理

2.1 数组对函数运算的支持

大白话:NumPy可以对整个数组一次性做数学函数运算,不需要写for循环。

python 复制代码
x = np.arange(0, 100, 10, dtype=np.floating)

np.sin(x)          # 每个元素求正弦
np.cos(x)          # 每个元素求余弦
np.round(np.cos(x))  # 先求余弦再四舍五入
np.ceil(x/2)       # 每个元素除以2后向上取整

比喻:就像你有一筐苹果,NumPy帮你一次性全部削好皮,而不是一个一个削。

2.2 布尔运算(条件筛选)

大白话:用一个条件去"过滤"数组,只留下你想要的那些元素。

python 复制代码
x = np.random.rand(10)    # 10个随机数

# 数组与常量比较(返回True/False数组)
x > 0.5                   # 每个元素判断是否大于0.5

# 用布尔数组做索引(取出满足条件的元素)
x[x > 0.5]                # 只保留大于0.5的元素

# 组合条件(必须用 & 和 |,不能用 and 和 or)
sum((x > 0.4) & (x < 0.6))  # 大于0.4且小于0.6的元素个数
# True=1, False=0,sum求和就是计数

# 整体判断
np.all(x < 1)             # 所有元素都小于1?
np.any(x > 0.8)           # 存在大于0.8的元素?

# 两个数组逐个比较
a = np.array([1, 2, 3])
b = np.array([3, 2, 1])
a > b                     # [False, False, True]
a[a > b]                  # 取出a中大于b对应位置的元素 → [3]
a[a == b]                 # 取出a中等于b对应位置的元素 → [2]

# 多条件筛选
x = np.arange(1, 10)
x[(x % 2 == 0) & (x > 5)]    # 大于5的偶数
x[(x % 2 == 0) | (x > 5)]    # 大于5或者是偶数

关键提醒 :组合条件时用 &|(位运算符),不是 andor。每个条件必须用括号括起来。

2.3 分段函数

大白话:根据条件把数据分成几类,每类做不同的处理。

python 复制代码
x = np.random.randint(0, 10, size=(1, 10))

# np.where():二分类(像Excel的IF函数)
np.where(x < 5, 0, 1)    # 小于5→0,大于等于5→1

# np.piecewise():多分类(像Excel的多层IF嵌套)
np.piecewise(x, [x < 4, x > 7], [lambda n: n**2, lambda n: n*3])
# 小于4 → 求平方
# 大于7 → 乘以3
# 其他所有元素 → 变成0

np.piecewise(x, [x < 3, (3 < x) & (x < 5), x > 7], [-1, 1, lambda n: n*4])
# 小于3 → -1
# 大于3且小于5 → 1
# 大于7 → 乘以4
# 其他 → 0

piecewise()参数解析

  • 第1个参数:要操作的数组
  • 第2个参数:条件列表 [条件1, 条件2, ...]
  • 第3个参数:处理列表 [处理1, 处理2, ...],可以是具体值或lambda函数
  • 条件没有覆盖到的元素自动变为0

比喻 :就像快递分拣------小件放A区,大件放B区,不符合条件的直接退回。np.where是两分类,np.piecewise是多分类。

第3天(6月23日):矩阵运算与线性代数

3.1 数组与矩阵的区别
特性 数组(ndarray) 矩阵(matrix)
维度 可以是一维、二维、三维或更高 只能是二维
数据类型 可包含数字、字符串等 只能包含数字
python 复制代码
x = np.matrix([[1,2,3], [4,5,6]])  # 创建矩阵

比喻:数组是瑞士军刀(什么都能装),矩阵是专门的数字表格(只能装数字,只有行和列)。

3.2 矩阵转置

大白话 :把矩阵的行变成列,列变成行,就像把表格旋转90度。

python 复制代码
x = np.matrix([[1,2,3], [4,5,6]])
# [[1,2,3],
#  [4,5,6]]

print(x.T)
# [[1,4],
#  [2,5],
#  [3,6]]
3.3 矩阵的统计操作
python 复制代码
x = np.matrix([[1,2,3], [4,5,6]])

x.mean()              # 所有元素的平均值 = 3.5
x.mean(axis=0)        # 纵向平均值(按列算) → [2.5, 3.5, 4.5]
x.mean(axis=1)        # 横向平均值(按行算) → [2.0, 5.0]
x.sum()               # 所有元素的和 = 21
x.max(axis=0)         # 纵向最大值
x.max(axis=1)         # 横向最大值
x.argmax(axis=0)      # 纵向最大值的下标
x.diagonal()          # 对角线元素
x.nonzero()           # 非0元素的下标

axis记忆法:有一张学生成绩表,每行是一个学生,每列是一个科目。

  • axis=0(纵向):按列计算 → 每个科目的全班平均分
  • axis=1(横向):按行计算 → 每个学生的个人平均分
3.4 相关系数(衡量两个人走路方向是否一致)

大白话:相关系数衡量两个变量变化方向的关系。

  • 接近 1:你涨我也涨(正相关)
  • 接近 -1:你涨我跌(负相关)
  • 接近 0:你走你的我走我的(无关)
python 复制代码
# 正相关:变化方向一致
np.corrcoef([1,2,3,4], [1,2,3,4])     # → 接近1

# 负相关:变化方向相反
np.corrcoef([1,2,3,4], [4,3,2,1])     # → 接近-1

# 正相关但幅度不同
np.corrcoef([1,2,3,4], [1,2,3,40])    # → 仍接近1
3.5 方差和标准差(衡量一群人是不是整齐)

大白话:衡量数据离散程度的指标。

  • 方差/标准差小:大家都差不多,队伍整齐
  • 方差/标准差大:有人很高有人很矮,参差不齐
python 复制代码
np.cov([1,1,1,1,1])     # 都一样 → 方差为0
np.cov([1,5,3,8,2])     # 有高有低 → 方差较大
np.std([1,1,1,1])       # 标准差也是0

标准差和方差的区别:标准差是方差的平方根,标准差的单位与原始数据一致,更好理解。

3.6 协方差(衡量两个人走路是否同步)

大白话:协方差衡量两个变量的变化是否同步。

  • 大于0:你往前走我也往前走(同步)
  • 小于0:你往前走我往后退(反向)
  • 接近0:各走各的互不影响
python 复制代码
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]   # y一直是x的两倍

np.cov(x, y)            # 正数,说明同步变化

协方差 vs 相关系数:相关系数是"标准化的协方差",数值始终在-1到1之间,只看方向不看幅度。

3.7 特征值与特征向量

大白话 :矩阵乘以一个向量 = 对这个向量做变换(旋转+缩放)。大多数向量会改变方向,但有些特殊向量只改变长度不改变方向

  • 特征向量:这些方向不变的特殊向量
  • 特征值:这个向量变长/变短了多少倍
python 复制代码
A = np.array([[1, -3, 3], [3, -5, 3], [6, -6, 4]])

e, v = np.linalg.eig(A)        # e是特征值,v是特征向量
np.dot(A, v)                    # 矩阵 × 特征向量
e * v                           # 特征值 × 特征向量
np.isclose(np.dot(A, v), e*v)   # 验证二者相等

# 行列式|A-λE|的值应为0
np.linalg.det(A - np.eye(3,3) * e)

比喻:就像照哈哈镜。大部分东西照出来都变形了,但有一条直线照出来还是直的,只是变长了------这条线就是特征向量,变长的倍数就是特征值。

你现在只需要知道np.linalg.eig() 是计算特征值和特征向量的工具。不需要手算,NumPy会帮你完成。

3.8 逆矩阵

大白话:逆矩阵就是矩阵的"倒数"。

  • 数字的倒数:3 × (1/3) = 1
  • 矩阵的逆:A × A⁻¹ = 单位矩阵(相当于数字1)
python 复制代码
x = np.matrix([[1,2,3],[4,5,6],[7,8,0]])
y = np.linalg.inv(x)   # 计算逆矩阵
x * y                   # 验证:矩阵 × 它的逆 = 单位矩阵
3.9 解线性方程组

大白话:一步解出方程组的答案。

比如:

复制代码
3x + y = 9
x + 2y = 8
python 复制代码
a = np.array([[3, 1], [1, 2]])     # 系数矩阵
b = np.array([9, 8])                # 等号右边的值
x = np.linalg.solve(a, b)           # 求解 → [2, 3]

np.dot(a, x)                         # 验证:结果应该等于b → [9, 8]

# 最小二乘解(方程个数多于未知数个数时)
np.linalg.lstsq(a, b)
3.10 奇异值分解(SVD)

大白话:把一个矩阵拆成三个部分,这三个部分乘起来能还原原来的矩阵。

python 复制代码
a = np.matrix([[1,2,3],[4,5,6],[7,8,9]])

u, s, v = np.linalg.svd(a)     # 奇异值分解

# 验证:u × 对角矩阵(s) × v 应该等于原矩阵
u * np.diag(s) * v              # 还原

你只需要知道 :SVD用于数据压缩、降维、推荐系统等场景。np.linalg.svd() 就是做这件事的函数。

三天学习总结

天数 核心内容 一句话概括
第1天(6.18) 数组创建、运算、筛选、形状变换 把列表变成能快速计算的数组,还能随便变形和筛选
第2天(6.22) 函数运算、布尔筛选、分段函数 一次性对所有数据做数学运算,按条件分类处理
第3天(6.23) 矩阵运算、统计、线性代数 用矩阵处理数字表格,计算统计指标,解方程

最重要的心态

  1. 你不需要推导公式,只需要知道哪个函数干什么用
  2. 数学概念用比喻理解就够了,工作中没人让你手算特征值
  3. NumPy是工具箱,你现在在认识每个工具的用途,而不是学怎么造工具
  4. 函数忘了就查,开发时没人要求你全部背下来

注:已经使用DeepSeek进行整理精简核心内容,些许不理解的配合个人笔记进行理解。