σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academy学习路线
Ctrl K
AO
+1.2k 位学习者
登录加入 ↗
学习路线
AO
+1.2k 位学习者
登录
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python 学习路线›模块 2 · 用于市场数据的 NumPy 与 pandas›主题 2.4·~40分钟入门

广播机制

路线进度模块 2/11 · 主题 4/20
2.4 广播机制
—实验得分— / 100总 XP—连续天数—

不同形状的数组一起运算

到目前为止我们组合的都是形状相同的数组。广播(broadcasting)是一套规则,让 NumPy 无需复制数据就能组合形状不同的数组。最简单的情形是标量:a + 10 给每个元素加 10,好像 10 被拉伸成了 a 的形状。数组之间也能这样拉伸:一个含三个数的向量可以加到三列矩阵的每一行上,每列一个值。减去每列的均值、按波动率缩放每个资产、构建所有两两差值的表,都可以不写一个循环。

import numpy as np

a = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])       # shape (2, 3)

print(a + 10)                          # a scalar is stretched to every element
# [[11. 12. 13.]
#  [14. 15. 16.]]

per_column = np.array([1, 10, 100])    # shape (3,): one value per column
print(a * per_column)
# [[  1.  20. 300.]
#  [  4.  50. 600.]]

print(a - a.mean(axis=0))              # subtract each column's mean
# [[-1.5 -1.5 -1.5]
#  [ 1.5  1.5  1.5]]

规则:从右向左比较形状

NumPy 把两个形状从右对齐。在每个位置上,两个大小要么相等,要么其中一个是 1(会被拉伸),要么其中一个数组在该位置没有维度(视为 1)。形状 (2, 3) 与 (3,) 可以:3 对 3,缺少的维度会沿 2 行拉伸。(2, 3) 与 (2,) 则失败,因为 3 对 2:NumPy 会引发 ValueError: operands could not be broadcast together。结果的形状在每个位置取较大的那个大小。

import numpy as np

a = np.ones((2, 3))
print((a + np.ones(3)).shape)        # (2, 3)   3 meets 3

try:
    a + np.ones(2)                   # 3 meets 2: no match
except ValueError as error:
    print(error)                     # operands could not be broadcast together with shapes (2,3) (2,)

# Size 1 stretches: (3, 1) with (1, 4) gives (3, 4)
print((np.ones((3, 1)) + np.ones((1, 4))).shape)   # (3, 4)

列向量与成对数据表

形状为 (n,) 的一维数组总是与最后一个轴对齐,所以它的作用像一行。要对每一行应用一个值,就需要形状为 (n, 1) 的列:写 x[:, np.newaxis] 或 x.reshape(-1, 1)。把列和行组合起来就能构建整张表:x[:, None] - x[None, :] 是所有差值 x[i] - x[j] 的 n×n 矩阵,corr * vols[:, None] * vols[None, :] 可把相关矩阵变为协方差矩阵。还要小心相反的错误:形状 (3,) 与 (3, 1) 相加会悄悄得到 3×3 的表,而不是含三个数的向量。

import numpy as np

x = np.array([1.0, 2.0, 4.0])
print(x[:, np.newaxis].shape, x.reshape(-1, 1).shape, x[None, :].shape)   # (3, 1) (3, 1) (1, 3)

# All pairwise differences x[i] - x[j]
print(x[:, None] - x[None, :])
# [[ 0. -1. -3.]
#  [ 1.  0. -2.]
#  [ 3.  2.  0.]]

# One factor per row needs a column
a = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
print(a * np.array([1.0, 2.0])[:, None])
# [[ 1.  2.  3.]
#  [ 8. 10. 12.]]

# Correlation matrix to covariance matrix
corr = np.array([[1.0, 0.5], [0.5, 1.0]])
vols = np.array([0.2, 0.3])
print(corr * vols[:, None] * vols[None, :])
# [[0.04 0.03]
#  [0.03 0.09]]

print((np.ones(3) + np.ones((3, 1))).shape)   # (3, 3)  a silent table, not a vector
import numpy as np

# Standardise every column at once: (x - mean) / std, column by column
a = np.array([[1.0, 10.0],
              [2.0, 20.0],
              [3.0, 30.0]])
z = (a - a.mean(axis=0)) / a.std(axis=0)   # (3, 2) - (2,) -> (3, 2), then divided by (2,)
print(z.round(3).tolist())                 # [[-1.225, -1.225], [0.0, 0.0], [1.225, 1.225]]
print(z.mean(axis=0).round(9).tolist())    # [0.0, 0.0]

# keepdims keeps the reduced axis, so the result can be broadcast back by row too
row_share = a / a.sum(axis=1, keepdims=True)
print(row_share.sum(axis=1).tolist())      # [1.0, 1.0, 1.0]
组合之前先打印形状只要形状碰巧匹配,广播从不报错,即使结果并非你的本意。组合数组之前,先写下各自的形状和你期望的结果形状;之后检查 result.shape。本该是 (n,) 却得到 (n, n),或用两个长向量得到数十亿元素的表,这是漏写或多写 [:, None] 的经典迹象。结果是在内存中构建的:两个各 10 万个值的向量会产生含 100 亿个元素的表。
  • 形状从右对齐:大小必须相等或为 1(或缺失);结果取较大者。
  • (n,) 相当于一行;用 x[:, None] 或 x.reshape(-1, 1) 得到列。
  • 列与行组合成表:x[:, None] - x[None, :];检查 result.shape,不匹配会引发 ValueError。

下一节:pandas Series,每个值都带有标签的一维数组。

实验:评分内容

  • 用 demean_columns 对每一列去均值25 分
  • 用 scale_rows 给每一行各自的系数25 分
  • 用 pairwise_differences 构建所有差值的表25 分
  • 用 covariance_from_correlation 把相关性转为协方差25 分
资源管理器 · lab-37-1
▾ lab-37-1
…
bash · 浏览器内
learner@quantcraft:~/lab-37-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

lab-37-1 中的所有内容将恢复为初始文件,终端也会被清空。你当前的解答将丢失,且无法撤销。

将被重置
  • main.py → 初始代码
  • 终端历史与测试输出
  • 未提交的更改
将被保留
  • 实验最高得分
  • 你的 XP 与连续天数
  • 讲解进度与笔记

登录后即可运行你的代码。

所有讲解均可免费阅读。创建账户或登录后,即可运行代码、运行测试并提交此实验。

  • 浏览器内 IDE 与 bash 终端,无需安装
  • 即时评分、XP 以及你在学习路线中的位置
创建账户登录
或
使用 GitHub 继续