lst-portfolio-calc、lst-wide-long-convert,完成axis统计、逐行点积和宽长转换。Python原生列表的局限性:
NumPy数组的设计哲学:
flags 实测金融应用场景:
| 策略 | 方法 | 适用场景 |
|---|---|---|
| 从序列转换 | np.array([1,2,3]) |
小规模数据 |
| 生成函数 | np.zeros(), np.ones() |
初始化、模拟 |
| 从文件读取 | np.loadtxt() |
导入外部数据 |
常用生成函数:
np.arange(10):类似range()的数组版本np.random.*:随机数生成系列函数stocks、days、stock_day 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。stock_day 的结果;同时写出方向、数量级或表格/图形结构。运行后核对:核对 stocks、days、stock_day 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。
拓展练习:把对象或期间改为一家长三角上市公司或一组 A 股资产;先预测指标方向,再说明结果能支持和不能支持的决策。
| 属性 | 说明 | 示例 |
|---|---|---|
shape |
数组维度(行×列) | (2000, 500) |
ndim |
维度数量 | 2 |
size |
元素总数 | 1000000 |
dtype |
数据类型 | float64 |
shape返回元组,表示各维度大小size等于shape各维度的乘积import numpy as np
arr_1d = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
print('基本索引:')
print(f' arr_1d[0] = {arr_1d[0]}') # 正索引:第一个元素
print(f' arr_1d[-1] = {arr_1d[-1]}') # 负索引:最后一个元素
print('\n切片操作:')
print(f' arr_1d[2:7] = {arr_1d[2:7]}') # 索引2到6
print(f' arr_1d[::2] = {arr_1d[::2]}') # 步长为2基本索引:
arr_1d[0] = 10
arr_1d[-1] = 100
切片操作:
arr_1d[2:7] = [30 40 50 60 70]
arr_1d[::2] = [10 30 50 70 90]
[start:stop:step]start:起始索引(包含),省略则从头开始stop:结束索引(不包含),省略则到末尾step:步长,省略默认为1金融应用:
arr[2:7]:提取第3天到第7天的数据arr[::2]:从日频数据中提取隔日数据二维数组索引:
arr_2d[0, 0] = 1
arr_2d[1, :] = [5 6 7 8]
arr_2d[:, 2] = [ 3 7 11]
arr_2d[0:2, 1:3] =
[[2 3]
[6 7]]
花式索引 arr_1d[[0, 2, 4]]: [10 30 50]
布尔索引 arr_1d[>50]: [ 60 70 80 90 100]
金融应用:
关键区别:基本切片通常返回共享内存的视图,高级索引通常返回副本;是否连续用 arr.flags 检查,.copy()显式创建独立副本。
向量化(Vectorization):用数组表达式代替显式循环
import numpy as np
import time
n = 10_000_000
arr1 = np.random.randn(n)
arr2 = np.random.randn(n)
# Python循环
start = time.time()
result_loop = [arr1[i] + arr2[i] for i in range(n)]
time_loop = time.time() - start
# NumPy向量化
start = time.time()
result_vec = arr1 + arr2
time_vec = time.time() - start
print(f'数组大小: {n:,} 元素')
print(f'Python循环: {time_loop:.4f}秒')
print(f'向量化运算: {time_vec:.4f}秒')
print(f'性能提升: {time_loop / time_vec:.1f}倍')
print(f'结果一致: {np.allclose(result_loop, result_vec)}')数组大小: 10,000,000 元素
Python循环: 1.9893秒
向量化运算: 0.0110秒
性能提升: 180.2倍
结果一致: True
import numpy as np
n_stocks = 100 # 100只股票
n_days = 252 # 1年交易日
# 随机权重归一化
weights = np.random.random(n_stocks)
weights = weights / weights.sum()
# 模拟日收益率矩阵
np.random.seed(42)
returns = np.random.randn(n_stocks, n_days) * 0.02
# 矩阵乘法计算组合收益率
portfolio_returns = weights @ returns # (100,) @ (100,252) = (252,)print('投资组合统计:')
print(f' 年化收益率: {portfolio_returns.mean() * 252:.4f}')
print(f' 年化波动率: {portfolio_returns.std() * np.sqrt(252):.4f}')
# 夏普比率 = 年化收益率 / 年化波动率(假设无风险利率为0)
sharpe = (portfolio_returns.mean() * 252) / (portfolio_returns.std() * np.sqrt(252))
print(f' 夏普比率: {sharpe:.4f}')
# 累计收益率
cum_returns = np.cumprod(1 + portfolio_returns) - 1
print(f' 年累计收益: {cum_returns[-1]:.4f}')投资组合统计:
年化收益率: 0.0062
年化波动率: 0.0359
夏普比率: 0.1736
年累计收益: 0.0056
@ 运算符与广播机制@ 运算符:矩阵乘法
np.matmul()weights @ returns:权重向量 × 收益率矩阵广播机制:不同形状数组的自动对齐
(100,) @ (100, 252) → (252,)| 特性 | NumPy数组 | Pandas Series |
|---|---|---|
| 索引 | 整数位置 | 自定义标签 |
| 缺失值 | 需用NaN手动表示 | 原生支持 |
| 数据对齐 | 手动对齐 | 自动对齐 |
| 功能 | 纯数值计算 | 统计、可视化 |
Series:
0 1
1 3
2 5
3 6
4 10
5 23
dtype: int64
索引: RangeIndex(start=0, stop=6, step=1)
值: [ 1 3 5 6 10 23]
类型: int64
带日期索引的Series:
2023-01-01 100.5
2023-01-02 102.3
2023-01-03 101.8
2023-01-04 103.2
2023-01-05 104.5
2023-01-06 103.9
Freq: D, dtype: float64
按日期切片(1月2日到4日):
2023-01-02 102.3
2023-01-03 101.8
2023-01-04 103.2
Freq: D, dtype: float64
索引选择方式:
.iloc[].loc[]series[条件]lst、s1 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。s1 的结果;同时写出方向、数量级或表格/图形结构。运行后核对:核对 lst、s1 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。
拓展练习:只改变一个关键输入或业务场景,先预测输出如何变化,再运行验证并解释变化原因。
现代投资组合理论假设收益率服从正态分布,但实证发现:
关键统计量:
import numpy as np
from scipy.stats import skew, kurtosis
np.random.seed(42)
returns = np.random.randn(252) * 0.02 # 模拟一年日收益率
print('收益率描述统计:')
print(f' 均值: {returns.mean():.6f}')
print(f' 标准差: {returns.std():.6f}')
print(f' 最小值: {returns.min():.6f}')
print(f' 最大值: {returns.max():.6f}')
print(f' 中位数: {np.median(returns):.6f}')收益率描述统计:
均值: -0.000075
标准差: 0.019306
最小值: -0.052395
最大值: 0.077055
中位数: 0.001184
分位数:
Q1(25%): -0.013711
Q2(50%): 0.001184
Q3(75%): 0.011861
分布形状:
偏度: 0.3002
峰度: 0.5796
annual_return = returns.mean() * 252 # 年化收益率
annual_vol = returns.std() * np.sqrt(252) # 年化波动率
sharpe_ratio = annual_return / annual_vol # 夏普比率
print('年化指标:')
print(f' 年化收益率: {annual_return:.4f} ({annual_return:.2%})')
print(f' 年化波动率: {annual_vol:.4f} ({annual_vol:.2%})')
print(f' 夏普比率: {sharpe_ratio:.4f}')年化指标:
年化收益率: -0.0190 (-1.90%)
年化波动率: 0.3065 (30.65%)
夏普比率: -0.0619
年化公式:
import numpy as np
np.random.seed(42)
# 模拟100只基金的年化收益率(均值8%,标准差15%)
fund_returns = np.random.randn(100) * 0.15 + 0.08
print('基金业绩分位数:')
print(f' 中位数(50%): {np.percentile(fund_returns, 50):.4f}')
print(f' 前25%(75%): {np.percentile(fund_returns, 75):.4f}')
print(f' 前10%(90%): {np.percentile(fund_returns, 90):.4f}')
print(f' 前1%(99%): {np.percentile(fund_returns, 99):.4f}')基金业绩分位数:
中位数(50%): 0.0610
前25%(75%): 0.1409
前10%(90%): 0.2309
前1%(99%): 0.3173
业绩分档阈值:
0%分位: -0.3130
25%分位: -0.0101
50%分位: 0.0610
75%分位: 0.1409
100%分位: 0.3578
各档基金数量:
第1档: 0只基金
第2档: 25只基金
第3档: 25只基金
第4档: 25只基金
第5档: 24只基金
原始数组(1维): [ 1 2 3 4 5 6 7 8 9 10 11 12]
重塑为3×4矩阵:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
重塑为4×3矩阵:
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
注意:元素总数必须匹配(12 = 3×4 = 4×3 = 2×6)
3×2矩阵:
[[1 2]
[3 4]
[5 6]]
转置(.T) → 2×3:
[[1 3 5]
[2 4 6]]
ravel()展平(视图): [1 2 3 4 5 6]
flatten()展平(副本): [1 2 3 4 5 6]
ravel vs flatten:
ravel():尽可能返回视图,但在非连续布局下也可能复制;用 np.shares_memory 核对flatten():返回副本,修改不影响原数组import pandas as pd
import numpy as np
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=5)
stocks = ['贵州茅台', '五粮液', '招商银行']
df_wide = pd.DataFrame(
np.random.randn(5, 3).round(4),
index=dates, columns=stocks
)
print('宽格式(日期×股票):')
print(df_wide)
print('\n长格式(股票×日期) — 通过转置:')
print(df_wide.T)宽格式(日期×股票):
贵州茅台 五粮液 招商银行
2023-01-01 0.4967 -0.1383 0.6477
2023-01-02 1.5230 -0.2342 -0.2341
2023-01-03 1.5792 0.7674 -0.4695
2023-01-04 0.5426 -0.4634 -0.4657
2023-01-05 0.2420 -1.9133 -1.7249
长格式(股票×日期) — 通过转置:
2023-01-01 2023-01-02 2023-01-03 2023-01-04 2023-01-05
贵州茅台 0.4967 1.5230 1.5792 0.5426 0.2420
五粮液 -0.1383 -0.2342 0.7674 -0.4634 -1.9133
招商银行 0.6477 -0.2341 -0.4695 -0.4657 -1.7249
| 主题 | 核心要点 |
|---|---|
| 数组创建 | np.array(), np.random, np.arange() |
| 索引切片 | [start:stop:step], 花式索引, 布尔索引 |
| 向量化运算 | 避免循环,性能提升数十倍 |
| Series | 带标签的一维数组,自动对齐 |
| 统计分析 | 均值、标准差、偏度、峰度 |
| 重塑转置 | reshape(), .T, ravel() |
lst-portfolio-calc、lst-wide-long-convert,完成axis统计、逐行点积和宽长转换。returns 形状 (3日,2资产),权重 weights 形状 (2,);均为有限浮点数。import numpy as np # 导入数组工具以完成矩阵运算与数值检查
returns=np.array([[.01,.02],[-.02,.01],[.03,-.01]],dtype=float) # 建立确定性收益输入
weights=np.array([.6,.4]) # 按资产列顺序固定组合权重
portfolio_returns=returns@weights # 用矩阵乘法计算逐日组合收益
demeaned_returns=returns-returns.mean(axis=0) # 按资产列去均值并满足条件合法广播
manual_column_means=np.array([returns[:,column_index].sum()/len(returns) for column_index in range(returns.shape[1])]) # 逐列手算两项资产均值
manual_portfolio_returns=np.array([sum(return_value*weight_value for return_value,weight_value in zip(return_row,weights)) for return_row in returns]) # 逐行点积核对组合收益
assert returns.shape==(3,2) and weights.shape==(2,) # 核对日期×资产与权重维度约定
assert np.allclose(returns.mean(axis=0),manual_column_means) # 用逐列手算核对轴向均值
assert np.allclose(portfolio_returns,manual_portfolio_returns) # 用逐行点积核对矩阵乘法
print(portfolio_returns,demeaned_returns,returns.flags['C_CONTIGUOUS']) # 输出组合收益、广播结果与连续性标志@,用 np.shares_memory 检查切片是视图还是副本。* 当矩阵乘法;维度静默广播;把特定基准倍数或“所有视图连续”当事实。[商业大数据分析与应用]