7  NumPy 矩阵处理

7.1 引言矩阵在金融中的核心地位

矩阵(Matrix)是金融数学的核心工具。从投资组合优化到风险因子模型,从期权定价到主成分分析,矩阵运算无处不在。NumPy提供了完整的线性代数工具,使得复杂的金融计算变得简洁高效。

7.2 本章学习目标

通过本章学习,你将能够:

  1. 使用 np.dot()@np.matmul() 计算向量点积与矩阵乘法,并检查”左列数=右行数”的维度相容条件
  2. 用权重向量与收益率矩阵的乘积计算投资组合每日收益与累计收益
  3. 使用 np.cov()np.corrcoef() 构建协方差矩阵与相关系数矩阵,用 w @ Σ @ w.T 计算组合方差并解释分散化效应
  4. 使用 np.linalg.solve()inv()det()matrix_rank() 求解线性方程组并判断矩阵可逆性,用 pinv() 求最小二乘解
  5. 使用 np.linalg.eigh() 对协方差矩阵做特征值分解,计算主成分解释方差比例

先修:章节 1(第1章 常用基本数据类型)、章节 2(第2章 列表、字典和元组)

7.3 矩阵基础

理论背景:线性代数与金融

在学习矩阵的具体操作之前,先看这些运算为何在金融中无处不在。线性代数在金融中的应用源于多维数据结构:

  • 投资组合: N只资产的收益率可以表示为N维向量
  • 协方差矩阵: N×N矩阵描述资产间的风险关系
  • 因子模型: 收益率 = 因子载荷 × 因子收益 + 特质收益

理解矩阵运算对掌握现代投资组合理论(MPT)、资本资产定价模型(CAPM)等金融理论至关重要。

7.3.1 矩阵的创建

列表 7.1: 创建NumPy矩阵的不同方法
# =============================================================================
# 题目:创建NumPy矩阵的不同方法
# =============================================================================
# 本节演示多种创建矩阵的方法,包括从二维数组创建、特殊矩阵创建等
# 在金融中,矩阵常用于表示多资产收益率、协方差矩阵等

# ==================== 导入NumPy库 ====================
import numpy as np  # 导入NumPy库,用于矩阵运算和线性代数操作

# ==================== 方法1:使用二维数组创建矩阵(推荐) ====================
matrix1 = np.array([  # 使用array函数创建二维数组作为矩阵
    [1, 2, 3],  # 第一行数据,包含3个元素
    [4, 5, 6],  # 第二行数据,包含3个元素
    [7, 8, 9]   # 第三行数据,包含3个元素
])  # 创建一个3×3的矩阵
print("3×3矩阵:")
print(matrix1)  # 打印矩阵内容,显示为3行3列的二维数组

# ==================== 方法2:使用特殊矩阵函数创建 ====================
identity = np.eye(3)  # eye函数创建单位矩阵(对角线为1,其余为0),参数3表示3×3
print("\n单位矩阵:")
print(identity)  # 打印单位矩阵,在金融中常作为初始权重或转换基准

zeros = np.zeros((2, 3))  # zeros函数创建全0矩阵,参数(2,3)表示2行3列
print("\n零矩阵:")
print(zeros)  # 打印零矩阵,常用于初始化累加变量或占位

ones = np.ones((2, 4))  # ones函数创建全1矩阵,参数(2,4)表示2行4列
print("\n全1矩阵:")
print(ones)  # 打印全1矩阵,可用于权重初始化或广播运算

# ==================== 方法3:创建对角矩阵 ====================
diag = np.diag([1, 2, 3])  # diag函数创建对角矩阵,参数[1,2,3]为对角线元素
print("\n对角矩阵:")
print(diag)  # 打印对角矩阵,在金融中可用于表示波动率或因子权重

补充说明:矩阵 vs 数组

特性 np.matrix np.ndarray
运算符行为 *表示矩阵乘法 *表示元素乘法
维度 始终2维 可以任意维
状态 官方不推荐(1.20 起构造时发出弃用类警告) 推荐使用
建议 不要使用 始终使用

最佳实践: 始终使用np.ndarray表示矩阵,使用@运算符进行矩阵乘法。

7.4 矩阵乘法

7.4.1 点积(Dot Product)

数学定义: 两个向量\(\mathbf{a} = [a_1, a_2, ..., a_n]\)\(\mathbf{b} = [b_1, b_2, ..., b_n]\)的点积为:

\[ \mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^{n} a_i b_i = a_1b_1 + a_2b_2 + \cdots + a_nb_n \]

金融含义: 点积常用于计算加权平均,如投资组合收益率:

\[ R_p = \sum_{i=1}^{n} w_i R_i = \mathbf{w} \cdot \mathbf{R} \]

其中\(\mathbf{w}\)是权重向量,\(\mathbf{R}\)是收益率向量。

任务要求:已知某投资组合 4 只资产 5 月 26 日的收益率列表 return_0526 与权重列表 weight(权重和为 1),用 np.dot() 计算组合日收益率,保留三位小数并输出。请将代码原样输入教学平台(注释除外),判定以平台为准。

列表 7.2: 平台原始代码
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import numpy as np  # 导入NumPy数值计算库
#使用列表定义5月26日收益率数据 return_0526、权重数据 weight。
return_0526=[0.0172,0.0243,-0.0029,0.0021]
weight=[0.15,0.20,0.25,0.4]  # 定义列表weight
#计算投资组合日收益率
return_daily=np.dot(return_0526,weight).round(3)
print(f'投资组合5月26日收益率为:{return_daily}')  # 输出投资组合5月26日收益率为

预期输出(本机 Python 实际运行结果,具体以平台运行结果为准):

投资组合5月26日收益率为:0.008

判读要点:点积结果 0.008 是一个标量,即 \(\sum w_i R_i\) 的加权和;未舍入值约为 0.007555,.round(3) 将其四舍五入为 0.008,即当日组合收益约 0.8%。

代码深度解析:

  1. 点积的几何意义: \[ \mathbf{a} \cdot \mathbf{b} = \|\mathbf{a}\| \|\mathbf{b}\| \cos\theta \] 其中\(\theta\)是两个向量的夹角。

  2. 金融应用:

    • 投资组合收益: \(\sum w_i R_i\)
    • 因子收益: \(\sum \beta_i F_i\)
    • 内积作为相似度: 衡量两个投资组合的相似程度

7.4.2 矩阵乘法

数学定义: 矩阵\(\mathbf{A}\)(\(m \times n\))与矩阵\(\mathbf{B}\)(\(n \times p\))的乘积\(\mathbf{C}\)(\(m \times p\))为:

\[ c_{ij} = \sum_{k=1}^{n} a_{ik} b_{kj} \]

重要性质:

  • 不满足交换律: \(\mathbf{AB} \neq \mathbf{BA}\)
  • 满足结合律: \((\mathbf{AB})\mathbf{C} = \mathbf{A}(\mathbf{BC})\)
  • 维度要求: \(\mathbf{A}\)的列数必须等于\(\mathbf{B}\)的行数
列表 7.3: NumPy矩阵乘法的多种方法
# =============================================================================
# 题目:NumPy矩阵乘法的多种方法
# =============================================================================
# 演示三种矩阵乘法方法:@运算符、np.matmul()函数、np.dot()函数
# 矩阵乘法要求:左矩阵的列数 = 右矩阵的行数

import numpy as np  # 导入NumPy库

# ==================== 定义两个矩阵用于乘法演示 ====================
A = np.array([  # 定义第一个矩阵A,2行3列
    [1, 2, 3],  # A的第1行
    [4, 5, 6]   # A的第2行
])  # A是一个2×3矩阵

B = np.array([  # 定义第二个矩阵B,3行2列
    [7, 8],     # B的第1行
    [9, 10],    # B的第2行
    [11, 12]    # B的第3行
])  # B是一个3×2矩阵
# 注意:A的列数(3)等于B的行数(3),可以相乘,结果将是2×2矩阵

# ==================== 方法1:@运算符(推荐,Python 3.5+) ====================
C1 = A @ B  # @运算符执行矩阵乘法,结果为2×2矩阵
print("方法1 - @运算符:")
print(C1)  # 打印乘积矩阵

# ==================== 方法2:np.matmul()函数 ====================
C2 = np.matmul(A, B)  # matmul函数执行矩阵乘法,与@运算符等价
print("\n方法2 - matmul函数:")
print(C2)  # 打印乘积矩阵,结果与方法1相同

# ==================== 方法3:np.dot()函数(也支持矩阵乘法) ====================
C3 = np.dot(A, B)  # dot函数也可用于矩阵乘法(虽然主要用于点积)
print("\n方法3 - dot函数:")
print(C3)  # 打印乘积矩阵,三种方法结果完全一致

# ==================== 验证三种方法的结果一致性 ====================
print(f"\n结果一致: {np.array_equal(C1, C2) and np.array_equal(C2, C3)}")  # array_equal逐元素比较,确认结果相同

金融应用:多期投资组合收益

列表 7.4: 矩阵运算计算多期投资组合收益
# =============================================================================
# 题目:矩阵运算计算多期投资组合收益
# =============================================================================
# 场景:3只股票在4个交易日的收益率矩阵,计算投资组合每天的收益率
# 数学原理:组合收益率 = 权重向量 × 收益率矩阵

import numpy as np  # 导入NumPy库

# ==================== 数据准备:收益率矩阵 ====================
returns = np.array([  # 创建3×4的收益率矩阵
    [0.01, 0.02, -0.01, 0.03],  # 股票A的4日收益率
    [0.02, 0.01, 0.03, -0.01],  # 股票B的4日收益率
    [-0.01, 0.03, 0.02, 0.04]   # 股票C的4日收益率
])  # returns是3×4矩阵,每行代表一只股票,每列代表一个交易日

# ==================== 投资组合权重向量 ====================
weights = np.array([0.3, 0.5, 0.2])  # 定义投资组合权重,3只股票权重分别为30%、50%、20%
# 权重总和为1,表示满仓投资

# ==================== 计算组合每日收益率 ====================
portfolio_returns = weights @ returns  # 矩阵乘法:权重向量(1×3) × 收益率矩阵(3×4) = 组合收益向量(1×4)
print(f"投资组合每日收益率: {portfolio_returns}")  # 打印4个交易日的组合收益率

# ==================== 计算期间累计收益率 ====================
cumulative_return = np.prod(1 + portfolio_returns) - 1  # prod计算连乘,累计收益率公式:(1+r1)×(1+r2)×...×(1+rn) - 1
print(f"期间累计收益率: {cumulative_return:.4%}")  # 打印累计收益率,:.4%格式化为百分比

7.5 协方差矩阵与相关系数矩阵

协方差矩阵(Covariance Matrix)是金融风险管理的核心工具。

数学定义: 对于随机向量\(\mathbf{X} = [X_1, X_2, ..., X_n]^T\),协方差矩阵\(\mathbf{\Sigma}\)为:

\[ \mathbf{\Sigma}_{ij} = \text{Cov}(X_i, X_j) = E[(X_i - \mu_i)(X_j - \mu_j)] \]

性质:

  1. 对称性: \(\mathbf{\Sigma}_{ij} = \mathbf{\Sigma}_{ji}\)
  2. 半正定性: \(\mathbf{a}^T\mathbf{\Sigma}\mathbf{a} \geq 0\)对所有\(\mathbf{a}\)
  3. 对角线元素: \(\mathbf{\Sigma}_{ii} = \text{Var}(X_i)\)
列表 7.5: 计算协方差矩阵与相关系数矩阵
# =============================================================================
# 题目:计算协方差矩阵与相关系数矩阵
# =============================================================================
# 协方差矩阵衡量多资产之间的风险相关性,是投资组合优化的核心输入
# 相关系数矩阵是标准化的协方差矩阵,取值范围[-1, 1]

import numpy as np  # 导入NumPy库

# ==================== 数据准备:模拟3只股票的日收益率 ====================
np.random.seed(42)  # 设置随机种子为42,确保结果可重现
returns = np.random.randn(100, 3) * 0.02  # 生成100天×3只股票的随机收益率,标准差2%
# randn生成标准正态分布,乘以0.02调整波动率

# ==================== 计算协方差矩阵 ====================
cov_matrix = np.cov(returns, rowvar=False)  # cov计算协方差矩阵,rowvar=False表示每列是一个变量
print("协方差矩阵:")
print(cov_matrix)  # 打印3×3协方差矩阵,对角线是各股票方差,非对角线是协方差

# ==================== 计算相关系数矩阵 ====================
corr_matrix = np.corrcoef(returns, rowvar=False)  # corrcoef计算相关系数矩阵
print("\n相关系数矩阵:")
print(corr_matrix)  # 打印相关系数矩阵,值域为[-1, 1]

# ==================== 从协方差矩阵推导相关系数矩阵 ====================
# 公式: Corr(X,Y) = Cov(X,Y) / (Std(X) * Std(Y))
std_devs = np.sqrt(np.diag(cov_matrix))  # sqrt对角线元素开方得到标准差向量
corr_from_cov = cov_matrix / np.outer(std_devs, std_devs)  # outer计算外积,广播得到分母矩阵
print("\n从协方差计算的相关系数矩阵:")
print(corr_from_cov)  # 打印推导的相关系数矩阵

# ==================== 验证两种方法结果一致性 ====================
print(f"\n两种方法一致: {np.allclose(corr_matrix, corr_from_cov)}")  # allclose在容差范围内比较浮点数

金融应用:投资组合方差

马科维茨投资组合理论的核心公式:

\[ \sigma_p^2 = \mathbf{w}^T \mathbf{\Sigma} \mathbf{w} = \sum_{i=1}^{n}\sum_{j=1}^{n} w_i w_j \sigma_{ij} \]

列表 7.6: 使用矩阵运算计算投资组合方差
# =============================================================================
# 题目:使用矩阵运算计算投资组合方差
# =============================================================================
# 投资组合方差 = 权重向量 × 协方差矩阵 × 权重向量转置
# 这是马科维茨现代投资组合理论(MPT)的核心公式

import numpy as np  # 导入NumPy库

# ==================== 数据准备:3只股票的协方差矩阵 ====================
cov_matrix = np.array([  # 定义3×3协方差矩阵
    [0.0100, 0.0018, 0.0011],  # 股票A:方差0.01,与B协方差0.0018,与C协方差0.0011
    [0.0018, 0.0081, 0.0009],  # 股票B:与A协方差0.0018,方差0.0081,与C协方差0.0009
    [0.0011, 0.0009, 0.0064]   # 股票C:与A协方差0.0011,与B协方差0.0009,方差0.0064
])  # 对称矩阵,反映3只股票之间的风险关系

# ==================== 投资组合权重 ====================
weights = np.array([0.3, 0.5, 0.2])  # 定义投资组合权重:股票A 30%, B 50%, C 20%

# ==================== 方法1:矩阵运算计算组合方差(简洁高效) ====================
portfolio_var = weights @ cov_matrix @ weights.T  # 公式:wΣw',权重×协方差矩阵×权重转置
portfolio_std = np.sqrt(portfolio_var)  # sqrt开方得到标准差(波动率)

print(f"投资组合方差: {portfolio_var:.6f}")  # 打印方差
print(f"投资组合标准差(波动率): {portfolio_std:.4%}")  # 打印波动率,:.4%格式化为百分比

# ==================== 方法2:双重求和(展示数学原理) ====================
n = len(weights)  # 获取资产数量n=3
portfolio_var2 = 0  # 初始化组合方差为0
for i in range(n):  # 外层循环遍历所有资产i
    for j in range(n):  # 内层循环遍历所有资产j
        portfolio_var2 += weights[i] * weights[j] * cov_matrix[i, j]  # 累加:wi*wj*σij

print(f"\n验证-投资组合方差: {portfolio_var2:.6f}")  # 打印双重求和计算的结果
print(f"两种方法一致: {np.isclose(portfolio_var, portfolio_var2)}")  # isclose比较两个浮点数是否近似相等

# ==================== 分散化效应:组合风险 < 加权平均风险 ====================
individual_stds = np.sqrt(np.diag(cov_matrix))  # diag提取对角线元素(方差),sqrt开方得到各资产标准差
weighted_avg_std = np.sum(weights * individual_stds)  # sum计算加权平均标准差
print(f"\n加权平均标准差: {weighted_avg_std:.4%}")  # 打印加权平均标准差
print(f"组合标准差: {portfolio_std:.4%}")  # 打印组合标准差
print(f"风险降低: {(weighted_avg_std - portfolio_std) / weighted_avg_std:.2%}")  # 计算并打印风险降低比例
# 分散化效应:组合标准差 < 加权平均标准差,体现了"不要把鸡蛋放在同一个篮子里"的原理

7.6 线性方程组求解

在金融中,许多问题可以表示为线性方程组\(\mathbf{Ax} = \mathbf{b}\)

应用场景:

  • 因子收益计算: 从资产收益反推因子收益
  • 套利定价理论: 求解无风险组合权重
  • 状态估计: Kalman滤波中的状态更新
列表 7.7: 求解线性方程组在金融中的应用
# =============================================================================
# 题目:求解线性方程组在金融中的应用
# =============================================================================
# 套利定价理论(APT)场景:已知资产收益率和因子载荷,求解因子收益
# 数学形式: Ax = b,其中A是因子载荷矩阵,x是因子收益,b是资产收益

import numpy as np  # 导入NumPy库

# ==================== 数据准备:因子载荷矩阵 ====================
A = np.array([  # 定义3×3因子载荷矩阵
    [1.2, 0.8, 0.5],  # 资产1对3个因子的敏感度(贝塔值)
    [0.9, 1.1, 0.3],  # 资产2对3个因子的敏感度
    [0.7, 0.6, 0.9]   # 资产3对3个因子的敏感度
])  # A矩阵每行代表一只资产,每列代表一个因子

# ==================== 资产收益率(超收益) ====================
b = np.array([0.08, 0.06, 0.07])  # 定义3只资产的收益率向量(超收益部分)

# ==================== 求解因子收益:Ax = b ====================
factor_returns = np.linalg.solve(A, b)  # solve函数求解线性方程组,返回因子收益向量x
print("因子收益率:")
print(f"  因子1: {factor_returns[0]:.4%}")  # 打印因子1的收益率
print(f"  因子2: {factor_returns[1]:.4%}")  # 打印因子2的收益率
print(f"  因子3: {factor_returns[2]:.4%}")  # 打印因子3的收益率

# ==================== 验证解的正确性 ====================
b_reconstructed = A @ factor_returns  # 将求解的因子收益代回方程:Ax
print(f"\n重构收益率: {b_reconstructed}")  # 打印重构的资产收益
print(f"原始收益率: {b}")  # 打印原始收益
print(f"验证通过: {np.allclose(b, b_reconstructed)}")  # allclose验证重构值与原始值是否一致

7.7 矩阵的秩与逆

矩阵的秩(Rank): 矩阵的行(列)向量中线性无关的最大数目。

可逆矩阵(非奇异矩阵): 方阵\(\mathbf{A}\)可逆当且仅当:

  1. \(\det(\mathbf{A}) \neq 0\)
  2. \(\text{rank}(\mathbf{A}) = n\)
  3. \(\mathbf{Ax} = \mathbf{0}\)只有零解
列表 7.8: 矩阵的秩与逆运算
# =============================================================================
# 题目:矩阵的秩与逆运算
# =============================================================================
# 可逆矩阵是线性代数的重要概念,在金融中用于求解线性方程组和计算组合权重
# 矩阵可逆的充要条件:行列式≠0 且 秩=n

import numpy as np  # 导入NumPy库

# ==================== 定义一个可逆矩阵 ====================
A = np.array([  # 定义2×2方阵
    [4, 7],  # 第1行
    [2, 6]   # 第2行
])  # 这是一个可逆矩阵

print("矩阵A:")
print(A)  # 打印矩阵A

# ==================== 计算行列式(Determinant) ====================
det_A = np.linalg.det(A)  # det函数计算行列式,可逆矩阵行列式≠0
print(f"\n行列式: {det_A:.4f}")  # 打印行列式值

# ==================== 计算矩阵的秩 ====================
rank_A = np.linalg.matrix_rank(A)  # matrix_rank计算矩阵的秩(线性无关的行/列数)
print(f"秩: {rank_A}")  # 打印秩,满秩矩阵秩等于维度

# ==================== 计算逆矩阵 ====================
A_inv = np.linalg.inv(A)  # inv函数计算逆矩阵,A^(-1)满足A×A^(-1)=I
print("\n逆矩阵 A^(-1):")
print(A_inv)  # 打印逆矩阵

# ==================== 验证: A @ A^(-1) = I (单位矩阵) ====================
identity = A @ A_inv  # 矩阵乘法:A × A^(-1)
print("\nA @ A^(-1):")
print(identity)  # 打印乘积,理论上应等于单位矩阵
print(f"是否为单位矩阵: {np.allclose(identity, np.eye(2))}")  # allclose验证是否为单位矩阵
# np.eye(2)创建2×2单位矩阵

# ==================== 求解线性方程组(使用逆矩阵方法) ====================
# 方程组: Ax = b,解为: x = A^(-1) @ b
b = np.array([1, 2])  # 定义方程组右侧向量b
x = A_inv @ b  # 利用逆矩阵求解:x = A^(-1) × b
print(f"\n方程组Ax=b的解: {x}")  # 打印解向量x

补充说明:数值稳定性

在实际计算中,接近奇异的矩阵(行列式接近0)会导致数值不稳定。NumPy会抛出LinAlgError

金融意义: 协方差矩阵接近奇异意味着存在完全相关的资产,这在实际中很少见(因为资产价格总有噪声)。

7.8 特征值与特征向量

定义: 对于方阵\(\mathbf{A}\),如果存在非零向量\(\mathbf{v}\)和标量\(\lambda\)使得:

\[ \mathbf{Av} = \lambda\mathbf{v} \]

\(\lambda\)称为特征值,\(\mathbf{v}\)称为特征向量。

金融应用:

  1. 主成分分析(PCA): 降维和因子提取
  2. 协方差矩阵对角化: 找到不相关的风险因子
  3. 马尔可夫链: 稳态分布计算
列表 7.9: 特征值分解在主成分分析中的应用
# =============================================================================
# 题目:特征值分解在主成分分析中的应用
# =============================================================================
# 特征值和特征向量是主成分分析(PCA)的数学基础
# 特征值大小代表主成分解释的方差量,特征向量代表主成分方向

import numpy as np  # 导入NumPy库

# ==================== 数据准备:协方差矩阵 ====================
cov_matrix = np.array([  # 定义3×3协方差矩阵
    [0.0100, 0.0018, 0.0011],  # 资产1的协方差
    [0.0018, 0.0081, 0.0009],  # 资产2的协方差
    [0.0011, 0.0009, 0.0064]   # 资产3的协方差
])  # 协方差矩阵是实对称矩阵,特征值均为实数

# ==================== 计算特征值和特征向量 ====================
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)  # eigh计算对称矩阵的特征值分解
# eigh返回:特征值数组(升序排列),特征向量矩阵(每列是一个特征向量)

print("特征值:")
for i, val in enumerate(eigenvalues):  # enumerate遍历特征值索引和值
    print(f"  λ{i+1} = {val:.6f}")  # 打印每个特征值,从大到小排列代表主成分重要性

print("\n特征向量(每列是一个特征向量):")
print(eigenvectors)  # 打印特征向量矩阵,第i列对应第i个特征值

# ==================== 计算解释方差比例 ====================
explained_var_ratio = eigenvalues / eigenvalues.sum()  # 每个特征值占总和的比例
print(f"\n解释方差比例:")
for i, ratio in enumerate(explained_var_ratio):  # 遍历解释方差比例
    print(f"  PC{i+1}: {ratio:.2%}")  # 打印每个主成分解释的方差比例

# ==================== 计算累计解释方差 ====================
cumulative_var = np.cumsum(explained_var_ratio)  # cumsum计算累积和
print(f"\n累计解释方差:")
for i, cum_var in enumerate(cumulative_var):  # 遍历累积方差
    print(f"  前{i+1}个主成分: {cum_var:.2%}")  # 打印前k个主成分的累计解释方差比例

7.9 广义逆矩阵

对于非方阵或奇异矩阵,可以使用Moore-Penrose伪逆。

列表 7.10: 伪逆在超定系统中的应用
# =============================================================================
# 题目:伪逆在超定系统中的应用
# =============================================================================
# 伪逆(广义逆)用于处理非方阵或奇异矩阵的求逆问题
# 在最小二乘回归中,伪逆提供最优解:x = A^+ × b

import numpy as np  # 导入NumPy库

# ==================== 定义超定系统(方程数>未知数) ====================
A = np.array([  # 定义4×2矩阵,4个方程2个未知数
    [1, 2],  # 第1行
    [3, 4],  # 第2行
    [5, 6],  # 第3行
    [7, 8]   # 第4行
])  # 这是一个超定系统,通常无精确解,只能求最小二乘解

b = np.array([1, 2, 3, 4])  # 定义右侧向量b,4个元素

# ==================== 计算Moore-Penrose伪逆 ====================
A_pinv = np.linalg.pinv(A)  # pinv计算伪逆(Moore-Penrose广义逆)

# ==================== 求解最小二乘解 ====================
x = A_pinv @ b  # 伪逆 × b,得到最小二乘解,最小化||Ax - b||²
print(f"最小二乘解: {x}")  # 打印解向量

# ==================== 验证:计算残差和均方误差 ====================
residual = b - (A @ x)  # 计算残差向量:b - Ax
mse = np.mean(residual ** 2)  # mean计算残差平方的均值(均方误差)
print(f"均方误差: {mse:.6f}")  # 打印均方误差,值越小拟合越好

7.10 本章小结

要点:

  • 点积 np.dot(w, R) = ΣwᵢRᵢ 是组合收益率的向量表达;矩阵乘法要求左矩阵列数等于右矩阵行数
  • @np.matmul()np.dot() 对二维矩阵等价;* 是元素乘法,依赖广播而非矩阵乘法
  • 协方差矩阵 Σ 配 w @ Σ @ w.T 得组合方差;相关系数矩阵是标准化的协方差矩阵
  • Ax = b 优先用 np.linalg.solve();可逆条件是 det(A) ≠ 0 且秩等于 n;非方阵或奇异系统用 pinv() 求最小二乘解
  • eigh() 面对对称矩阵(如协方差矩阵)返回升序特征值,特征值占比即主成分解释方差比例

易错点:

  • A * B 不是矩阵乘法;形状不匹配时要么报错,要么被广播成意料之外的结果
  • np.cov() 默认每行是一个变量(rowvar=True),传入”每行一只股票”的数据才能得到正确的协方差矩阵
  • 用逆矩阵 inv(A) @ b 解方程既慢又不稳定,应优先 solve()
  • eigh() 返回的特征向量按存放,按行读取会得到错误的主成分方向

7.11 动手与思考

以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。

  1. 输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。

    import numpy as np
    R = np.array([[0.01, 0.02], [0.03, -0.01]])
    w = np.array([0.5, 0.5])
    print(R * w)
    print(R @ w)
    print(w @ R)
    print(R.shape, R.T.shape)

    参考答案(先写下你的预测再点开)

    解题思路:逐行推演。R 是 2×2 矩阵(两日两资产),w 是长度为 2 的权重向量。第 1 个 R * w 是元素乘法:w 形状 (2,) 沿行方向广播到每一行,每列同乘一个权重,结果仍是 2×2 矩阵 [[0.005, 0.01], [0.015, -0.005]]。第 2 个 R @ w 是矩阵乘法:R(2×2) @ w(2,) 按行加权求和,第 1 行 0.5×0.01+0.5×0.02=0.015、第 2 行 0.5×0.03+0.5×(-0.01)=0.01,得长度为 2 的向量 [0.015, 0.01](每“日”的组合收益)。第 3 个 w @ R 换成按列加权,第 1 列 0.5×0.01+0.5×0.03=0.02、第 2 列 0.5×0.02+0.5×(-0.01)=0.005,得 [0.02, 0.005]——@ 不满足交换律,两个方向结果不同。第 4 个 R.shapeR.T.shape 都是 (2, 2),方阵转置形状不变。注意 NumPy 打印时 0.01 显示为 0.01(列对齐留空格)。

    # 验证脚本:对比元素乘法与矩阵乘法
    import numpy as np  # 导入NumPy库
    R = np.array([[0.01, 0.02], [0.03, -0.01]])  # 两日两资产收益率矩阵
    w = np.array([0.5, 0.5])  # 等权权重向量
    print(R * w)  # 元素乘法:w广播到每一行
    print(R @ w)  # 矩阵乘法:按行加权求和
    print(w @ R)  # 矩阵乘法:按列加权求和
    print(R.shape, R.T.shape)  # 方阵转置形状不变

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    [[ 0.005  0.01 ]
     [ 0.015 -0.005]]
    [0.015 0.01 ]
    [0.02  0.005]
    (2, 2) (2, 2)

    回扣本章:对应本章小结“要点”第 1、2 条与“易错点”第 1 条——* 是元素乘法依赖广播,@ 才是矩阵乘法,二者结果形状与数值都不同。

  2. 概念辨析:R * wR @ w 的结果为什么形状不同、数值也不同,各自的计算规则是什么?求解 Ax = bnp.linalg.solve(A, b)np.linalg.inv(A) @ b 都能得到解,为什么推荐前者?再说明:协方差矩阵与相关系数矩阵的关系,以及”相关系数矩阵是标准化的协方差矩阵”标准化的是什么。

    参考答案(点开前请先独立完成)

    解题思路:逐点作答。第一,R * w 按元素乘法(广播)执行:w 被扩展成与 R 同形状的矩阵,对应位置相乘,结果形状与 R 相同(2×2);R @ w 按矩阵乘法执行:左矩阵的行与右向量做内积,\(c_{ij}=\sum_k a_{ik}b_{kj}\),结果形状由“左行数 × 右列数”决定(此处为长度 2 的向量)。规则不同,形状与数值自然都不同;金融上 w @ R 才是“权重 × 各资产收益率”的组合收益率计算,R * w 只是给各列缩放。第二,solve(A, b) 基于 LU 分解直接解方程,不做显式求逆;inv(A) @ b 要先算出完整逆矩阵再相乘,计算量更大,且当 A 接近奇异(条件数大)时求逆会放大浮点误差,数值上更不稳定,故推荐 solve()。第三,协方差矩阵 \(\Sigma_{ij}=\text{Cov}(X_i,X_j)\) 的元素带量纲,数值大小同时受相关程度与各自波动率影响;“标准化”指的是把每个元素除以对应两个资产标准差的乘积,即 \(\rho_{ij}=\Sigma_{ij}/(\sigma_i\sigma_j)\),把各元素统一压缩到 \([-1, 1]\),剔除量纲与波动率尺度的影响,只留下纯粹的线性相关强度——所以说相关系数矩阵是标准化(去量纲)后的协方差矩阵,其对角线恒为 1。

    回扣本章:对应本章小结“要点”第 2、3、4 条与“易错点”第 3 条——*@ 的规则差异、相关系数矩阵与协方差矩阵的关系、solve() 优先于 inv(A) @ b

  3. 变式任务(平台任务同型改造):已知 5 月 27 日 4 只资产收益率 return_0527 = [0.0095, -0.0031, 0.0126, 0.0044],先沿用平台任务的权重 weight = [0.15, 0.20, 0.25, 0.4]np.dot() 计算当日组合收益(保留三位小数);再把三日两资产数据 R = np.array([[0.0172, 0.0095, 0.0120], [-0.0029, -0.0031, 0.0015]]) 与权重 w2 = np.array([0.6, 0.4]) 相乘,输出每日组合收益率。

    参考答案(点开前请先独立完成)

    解题思路:第一部分与平台任务同型,把 5 月 27 日的四资产收益率与权重做点积:\(0.15\times0.0095+0.20\times(-0.0031)+0.25\times0.0126+0.4\times0.0044=0.005715\),保留三位小数得 0.006。第二部分用矩阵形式一次算出三日收益:R 是 2×3 矩阵(每行一只资产、每列一个交易日),w2 是长度 2 的权重向量,w2 @ R\((1\times2)\times(2\times3)\) 得长度 3 的向量,第 \(t\) 个元素为 \(0.6\times R_{1t}+0.4\times R_{2t}\),三日的组合收益率依次约为 0.00916、0.00446、0.0078。

    # 变式程序:点积计算单日组合收益与矩阵乘法计算三日组合收益
    import numpy as np  # 导入NumPy库
    return_0527 = [0.0095, -0.0031, 0.0126, 0.0044]  # 5月27日四资产收益率
    weight = [0.15, 0.20, 0.25, 0.4]  # 组合权重(和为1)
    print(np.dot(return_0527, weight).round(3))  # 点积计算当日组合收益,保留三位小数
    R = np.array([[0.0172, 0.0095, 0.0120], [-0.0029, -0.0031, 0.0015]])  # 两资产三日收益率矩阵
    w2 = np.array([0.6, 0.4])  # 两资产权重
    print(w2 @ R)  # 矩阵乘法一次得到三日组合收益率

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    0.006
    [0.00916 0.00446 0.0078 ]

    注意:以上为本题变式的独立代码;列表 7.2 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。

    回扣本章:对应本章小结“要点”第 1、2 条——np.dot(w, R) 是组合收益率的向量表达,权重向量与收益率矩阵相乘可一次得到多期组合收益。

  4. 动手验证:对协方差矩阵 Σ = np.array([[0.04, 0.01], [0.01, 0.02]]),用 np.linalg.eigh() 求特征值与特征向量,计算每个主成分的解释方差比例与累计比例;再用权重 w = np.array([0.5, 0.5]) 计算 w @ Σ @ w.T,与加权平均标准差 0.5*np.sqrt(0.04) + 0.5*np.sqrt(0.02) 比较,验证分散化效应。

    参考答案(点开前请先独立完成)

    解题思路eigh() 面对对称矩阵返回升序特征值,本例为 0.01585786 与 0.04414214;每个特征值占总和(0.06,恰等于两资产方差之和)的比例即该主成分的解释方差比例,分别约 26.43% 与 73.57%,累计比例为 26.43% 与 100%。组合方差 \(w^T\Sigma w=0.25\times0.04+0.25\times0.01+0.25\times0.01+0.25\times0.02=0.02\),开方得组合标准差约 0.1414;而加权平均标准差为 \(0.5\times\sqrt{0.04}+0.5\times\sqrt{0.02}=0.1+0.0707=0.1707\)。组合标准差 0.1414 明显低于加权平均标准差 0.1707(低约 17%),两者不相等的原因是两资产协方差(0.01)小于“完全同涨同跌”情形下的乘积 \(\sigma_1\sigma_2\approx0.0283\),风险相互抵消了一部分——这就是分散化效应。

    # 验证脚本:特征值分解计算主成分方差比例与组合方差
    import numpy as np  # 导入NumPy库
    cov_matrix = np.array([[0.04, 0.01], [0.01, 0.02]])  # 两资产协方差矩阵
    eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)  # 对称矩阵特征值分解(升序)
    print(eigenvalues)  # 输出两个特征值
    explained_ratio = eigenvalues / eigenvalues.sum()  # 各主成分解释方差比例
    print(explained_ratio)  # 输出比例
    print(np.cumsum(explained_ratio))  # 输出累计比例
    w_eq = np.array([0.5, 0.5])  # 等权权重
    portfolio_std = np.sqrt(w_eq @ cov_matrix @ w_eq.T)  # 组合标准差
    weighted_avg_std = 0.5 * np.sqrt(0.04) + 0.5 * np.sqrt(0.02)  # 加权平均标准差
    print(round(portfolio_std, 4), round(weighted_avg_std, 4))  # 两者对比

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    [0.01585786 0.04414214]
    [0.26429774 0.73570226]
    [0.26429774 1.        ]
    0.1414 0.1707

    回扣本章:对应本章小结“要点”第 3、5 条——w @ Σ @ w.T 得组合方差,分散化使组合标准差低于加权平均标准差;eigh() 返回升序特征值,占比即主成分解释方差比例。