案例:沪深300指数成分股收益率的主成分分析¶
案例背景¶
本小节的内容讲解如何使用Python对数据进行PCA主成分分析,使用到的数据有2022年4月到9月的沪深300成分股数据和沪深300指数数据
- 沪深300成分股数据:data.csv
- 沪深300指数数据:HS300.csv
内容主要包括两个部分:
- 对沪深300成分股数据进行PCA分析,得到各个成分股的线性组合系数
- 可视化对比沪深300成分股经过线性组合系数加权求和后的整体行情趋势与沪深300指数
数据读取与划分¶
首先导入 pandas 库读取数据
# 导入 pandas 库
import pandas as pd
# 读取股票数据
url = 'https://www.getwage.xyz/courses/multi_variance/PCA/HS300PCA/data.csv'
stock_data = pd.read_csv(url)
stock_data
| Tradedate | 1 | 2 | 63 | 66 | 69 | 100 | 157 | 166 | 301 | ... | 688036 | 688065 | 688111 | 688126 | 688169 | 688363 | 688396 | 688561 | 688599 | 688981 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2022-04-12 | 15.92 | 20.17 | 22.24 | 11.12 | 7.36 | 4.84 | 6.98 | 4.47 | 13.61 | ... | 87.83 | 99.78 | 179.30 | 22.12 | 528.10 | 105.80 | 48.60 | 53.63 | 49.90 | 43.89 |
| 1 | 2022-04-13 | 15.80 | 20.50 | 22.00 | 10.74 | 7.23 | 4.77 | 6.79 | 4.42 | 13.30 | ... | 86.04 | 95.75 | 177.70 | 22.30 | 511.02 | 102.50 | 46.80 | 51.80 | 48.74 | 43.51 |
| 2 | 2022-04-14 | 16.04 | 21.00 | 22.33 | 10.88 | 7.55 | 4.80 | 6.86 | 4.45 | 14.07 | ... | 89.00 | 95.33 | 184.10 | 22.80 | 533.00 | 114.68 | 48.20 | 52.68 | 48.37 | 43.67 |
| 3 | 2022-04-15 | 16.42 | 21.30 | 22.69 | 10.49 | 7.64 | 4.76 | 6.68 | 4.39 | 13.76 | ... | 87.49 | 93.27 | 181.36 | 22.50 | 516.61 | 116.00 | 48.60 | 51.21 | 48.20 | 43.25 |
| 4 | 2022-04-18 | 15.90 | 20.53 | 23.17 | 10.44 | 7.13 | 4.76 | 6.56 | 4.33 | 13.66 | ... | 94.66 | 92.60 | 185.45 | 22.78 | 531.17 | 119.43 | 49.70 | 52.55 | 53.90 | 43.69 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 95 | 2022-08-29 | 12.42 | 15.57 | 24.08 | 9.17 | 5.33 | 4.24 | 5.83 | 4.14 | 21.01 | ... | 70.63 | 72.57 | 163.00 | 20.00 | 316.00 | 141.97 | 52.18 | 49.24 | 73.60 | 40.08 |
| 96 | 2022-08-30 | 12.48 | 15.89 | 24.50 | 9.23 | 5.40 | 4.20 | 5.84 | 4.15 | 20.23 | ... | 69.64 | 70.03 | 173.96 | 20.17 | 313.82 | 145.43 | 52.40 | 49.24 | 73.44 | 40.10 |
| 97 | 2022-08-31 | 12.75 | 16.63 | 24.50 | 9.08 | 5.35 | 4.15 | 5.77 | 4.18 | 19.26 | ... | 68.26 | 69.46 | 180.12 | 20.60 | 320.03 | 143.05 | 52.40 | 49.77 | 71.49 | 40.21 |
| 98 | 2022-09-01 | 12.61 | 16.84 | 24.03 | 9.13 | 5.32 | 4.08 | 5.82 | 4.20 | 18.81 | ... | 67.13 | 69.40 | 190.61 | 20.05 | 325.81 | 139.96 | 51.16 | 49.70 | 73.12 | 39.95 |
| 99 | 2022-09-02 | 12.51 | 16.80 | 24.27 | 9.26 | 5.16 | 4.10 | 5.82 | 4.16 | 19.16 | ... | 67.20 | 69.45 | 188.35 | 20.16 | 313.36 | 141.48 | 51.64 | 50.46 | 72.80 | 40.40 |
100 rows × 301 columns
接下来对数据进行清洗,处理数据中的异常值。对于整列缺失的数据,也就是全部数据丢失的股票,整列删除;对于其他列中缺失的个别数据,用前一个非缺失的数据来填充。
stock_data = stock_data.dropna(axis=1, how='all')
stock_data.fillna(method='pad', inplace=True)
主成分分析(PCA)¶
import numpy as np
前面所读取的数据是一段时间内沪深300成分股的收盘价,我们通过某天和前一天的收盘价可以计算当天的对数收益率。将当天的收盘价记作 $price_{t}$,前一天的收盘价记作 $price_{t-1}$,则对数收益率的计算公式为
$$ln(\frac{price_{t}}{price_{t-1}}) = ln(price_{t}) - ln(price_{t-1})$$stock_returns = stock_data.copy()
stock_returns.iloc[:,1:] = stock_returns.iloc[:,1:].apply(np.log).diff(1)
导入 sklearn 库进行PCA分析
from sklearn.decomposition import PCA
# 数据的第一行是第一天的数据,收益率无法计算为空值,应该去掉
stock_returns = stock_returns[1:]
# 数据的第一列是日期,进行PCA只需保留除了日期列之外的股票数据
pca = PCA(1).fit(stock_returns.iloc[:,1:])
数据可视化¶
导入 matplotlib 库将PCA的结果可视化
import matplotlib.pyplot as plt
y_index = stock_data.columns[1:]
以股票代码为横坐标,各个股票的线性组合系数为纵坐标,可以将各个股票所占的比重可视化,数值越高的股票在沪深300成分股中的重要性越高
pc1 = pd.Series(index=y_index, data=pca.components_[0])
pc1.plot(figsize=(10,6), xticks=[], grid=True)
plt.tight_layout()
PCA主成分分析获得了沪深300成分股票各自的线性组合系数,每个系数绝对值除以所有系数绝对值之和,计算出各个股票在沪深300全体股票中的权重比例。对各个股票的收益率按照该权重比例进行加权求和,结果可以反映沪深300全体股票的行情变化。之后按照日期先后顺序进行可视化。
# 根据主成分分析结果计算各个股票的权重比例
weights = abs(pc1)/sum(abs(pc1))
# 对全体股票收益率进行加权求和
myrs = (weights*stock_returns[y_index]).sum(1)
# 从求和结果计算收益情况并可视化
myrs.cumsum().apply(np.exp).plot(figsize=(10,6))
结果验证¶
沪深300指数是由中国证券指数公司基于上海证券交易所上市公司和深圳证券交易所上市公司股票数据编制的指数,反映股票市场整体走势。其数值来自于证券指数公司的专家根据各只股票的重要性制定每只股票的权重参数,并根据参数对成分股票的数据进行加权求和。影响力越大、越重要的股票对沪深300指数所起的作用越大。
为了说明沪深300成分股按照线性组合系数加权求和后的整体行情趋势与沪深300指数的关系,将沪深300指数收盘价可视化
# 读取沪深300指数数据
url = 'https://www.getwage.xyz/courses/multi_variance/PCA/HS300PCA/HS300.csv'
HS300 = pd.read_csv(url)
# 收盘价数据作为纵坐标
y_data = HS300['close']
# 可视化沪深300指数收盘价
HS300_close = pd.Series(index=range(100), data=HS300['close'])
HS300_close.plot(figsize=(10,6), xticks=[])
对比沪深300成分股经过线性组合系数加权求和后的整体行情趋势和沪深300指数的数据图像,可以发现虽然数值单位不同,但曲线各个部位的变化趋势、极点是相同的。这是因为经过PCA分析后得到的线性组合系数能够从数值上反映各个成分对总体指标所产生的影响,因此根据线性组合系数对成分股进行加权求和能够表达沪深300指数中的大部分信息。但由于沪深300指数的实际计算过程还需要考虑其他因素,因此两者并不完全相同,而只是有着大致相近的变化趋势。