Day 15:KMeans聚类与股票风格分类
Day 15:KMeans聚类与股票风格分类
📋 目录
- 聚类分析概述
- KMeans算法原理
- K值选择方法(肘部法则、轮廓系数)
- 初始化问题与KMeans++
- 数据预处理与标准化
- 聚类的评估与解释
第一部分:聚类分析概述
1.1 什么是聚类?
聚类(Clustering)是一种无监督学习方法,将数据分成若干个组(簇),使得组内相似度高,组间相似度低。
与分类的区别:
| 对比项 | 分类 | 聚类 |
|---|---|---|
| 学习类型 | 监督学习 | 无监督学习 |
| 标签 | 有标签 | 无标签 |
| 目标 | 预测新样本类别 | 发现数据内在结构 |
| 评估 | 准确率、混淆矩阵 | 轮廓系数、内部指标 |
1.2 量化交易中的应用场景
| 应用场景 | 说明 |
|---|---|
| 股票风格分类 | 根据基本面因子划分价值/成长/动量等风格 |
| 市场状态识别 | 识别牛/熊/震荡市 |
| 风险分群 | 识别相似风险特征的股票 |
| 行业轮动 | 聚类发现行业轮动规律 |
| 异常检测 | 识别偏离正常模式的异常行为 |
1.3 常用聚类算法对比
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| KMeans | 基于质心 | 快速、简单 | 需要指定K,对异常值敏感 |
| 层次聚类 | 树状结构 | 不需要K,可解释 | 计算量大 |
| DBSCAN | 基于密度 | 发现任意形状,处理异常值 | 参数敏感 |
| GMM | 概率分布 | 软聚类,可处理椭圆形状 | 计算复杂 |
第二部分:KMeans算法原理
2.1 算法核心思想
目标:将nnn个数据点分成KKK个簇,使得簇内平方和(Inertia)最小。
簇内平方和公式:
Inertia=∑i=1nminμj∈C∥xi−μj∥2=∑j=1K∑xi∈Cj∥xi−μj∥2 \text{Inertia} = \sum_{i=1}^n \min_{\mu_j \in C} \|x_i - \mu_j \|^2 = \sum_{j=1}^K \sum_{x_i \in C_j} \|x_i - \mu_j \|^2Inertia=i=1∑nμj∈Cmin∥xi−μj∥2=j=1∑Kxi∈Cj∑∥xi−μj∥2
其中μj\mu_jμj是第jjj个簇的质心(中心点)。
2.2 算法步骤
输入:数据集XXX,簇数KKK,最大迭代次数 max_iters
输出:簇分配结果
随机初始化KKK个质心μ1,μ2,⋯ ,μK\mu_1, \mu_2, \cdots, \mu_Kμ1,μ2,⋯,μK
重复直到收敛或达到最大迭代次数:
2.1 分配步骤:将每个样本分配到最近的质心
c(i)=argminj∥xi−μj∥2c(i) = \arg\min_j \|x_i - \mu_j\|^2c(i)=argminj∥xi−μj∥22.2 更新步骤:重新计算每个簇的质心
μj=1∣Cj∣∑i∈Cjxi\mu_j = \cfrac{1}{|C_j|}\sum_{i \in C_j} x_iμj=∣Cj∣1∑i∈Cjxi返回簇分配结果
2.3 收敛性
KMeans算法保证在有限步内收敛,但可能收敛到局部最优解而非全局最优。
影响因素:
- 初始质心选择
- 数据分布
- K值选择
2.4 距离度量
KMeans通常使用欧氏距离:
d(x,y)=∑i=1p(xi−yi)2 d(x,y) = \sqrt{\sum_{i=1}^p(x_i-y_i)^2}d(x,y)=i=1∑p(xi−yi)2
重要:使用欧氏距离前必须对特征进行标准化,否则量纲大的特征会主导距离计算。
第三部分:K值选择方法
3.1 肘部法则(Elbow Method)
原理:随着 K 增加,Inertia(簇内平方和)会下降。找到 Inertia 下降速度变缓的"肘点"。
# 肘部法则示例inertias=[]forkinrange(1,11):kmeans=KMeans(n_clusters=k,random_state=42)kmeans.fit(X_scaled)inertias.append(kmeans.inertia_)plt.plot(range(1,11),inertias,'bo-')plt.xlabel('K值')plt.ylabel('Inertia')plt.title('肘部法则选择K值')特点:
- 简单直观
- 肘点有时不明显(需要主观判断)
3.2 轮廓系数(Silhouette Coefficient)
原理:结合簇内凝聚度和簇间分离度,取值范围[−1,1][-1, 1][−1,1]。
单个样本的轮廓系数:
s(i)=b(i)−a(i)max(a(i),b(i)) s(i) = \cfrac{b(i) - a(i)}{\max(a(i), b(i))}s(i)=max(a(i),b(i))b(i)−a(i)
其中:
- a(i)a(i)a(i):样本到同簇其他样本的平均距离(簇内不相似度)
- b(i)b(i)b(i):样本到其他簇的最小平均距离(簇间不相似度)
整体轮廓系数:所有样本轮廓系数的平均值
解读:
| 轮廓系数 | 含义 |
|---|---|
| s>0.5s > 0.5s>0.5 | 聚类效果好 |
| 0.2<s<0.50.2 < s < 0.50.2<s<0.5 | 聚类效果一般 |
| s<0.2s < 0.2s<0.2 | 聚类效果差 |
| s≈0s \approx 0s≈0 | 簇之间重叠 |
| s<0s < 0s<0 | 样本可能被错误分配 |
fromsklearn.metricsimportsilhouette_score silhouette_scores=[]forkinrange(2,11):kmeans=KMeans(n_clusters=k,random_state=42)labels=kmeans.fit_predict(X_scaled)score=silhouette_score(X_scaled,labels)silhouette_scores.append(score)3.3 轮廓系数可视化
fromsklearn.metricsimportsilhouette_samples# 绘制单个K值的轮廓图silhouette_vals=silhouette_samples(X_scaled,labels)y_lower=10foriinrange(k):cluster_vals=silhouette_vals[labels==i]cluster_vals.sort()y_upper=y_lower+len(cluster_vals)plt.fill_betweenx(np.arange(y_lower,y_upper),0,cluster_vals)y_lower=y_upper+103.4 K值选择方法对比
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 肘部法则 | Inertia下降拐点 | 简单直观 | 肘点主观 |
| 轮廓系数 | 聚合/分离度 | 客观数值 | 计算量大 |
| Gap统计量 | 与随机数据对比 | 统计严谨 | 计算复杂 |
| Calinski-Harabasz | 簇间/簇内方差比 | 快速 | 偏向凸簇 |
第四部分:初始化问题与KMeans++
4.1 随机初始化的陷阱
问题:不同的初始质心可能导致不同的聚类结果(局部最优)。
# 运行多次,结果可能不同kmeans1=KMeans(n_clusters=3,random_state=0)kmeans2=KMeans(n_clusters=3,random_state=42)# 可能得到不同的簇分配4.2 KMeans++ 初始化
KMeans++是一种智能初始化方法,能有效提高收敛到全局最优的概率。
算法步骤:
- 随机选择第一个质心
- 对于每个未选点,计算到最近已有质心的距离D(x)D(x)D(x)
- 以概率D(x)2∑D(x)2\frac{D(x)^2}{\sum D(x)^2}∑D(x)2D(x)2选择下一个质心
- 重复直到选满KKK个质心
sklearn 中的实现:
fromsklearn.clusterimportKMeans# KMeans++ 是默认初始化方法kmeans=KMeans(n_clusters=5,init='k-means++',random_state=42)# 也可以使用随机初始化kmeans=KMeans(n_clusters=5,init='random',random_state=42)4.3 多次运行取最优
best_inertia=float('inf')best_kmeans=Noneforiinrange(10):kmeans=KMeans(n_clusters=5,random_state=i)kmeans.fit(X_scaled)ifkmeans.inertia_<best_inertia:best_inertia=kmeans.inertia_ best_kmeans=kmeans第五部分:数据预处理与标准化
5.1 为什么需要标准化?
问题:不同特征的量纲差异会影响距离计算。
示例:
- 市盈率(PE):5-50
- 换手率:1%-20%
- ROE:-30% - 30%
若不标准化,PE 的数值会主导距离计算。
5.2 标准化方法
| 方法 | 公式 | 适用场景 |
|---|---|---|
| StandardScaler | (x−μ)/σ(x - \mu)/\sigma(x−μ)/σ | 特征分布近似正态 |
| MinMaxScaler | (x−min)/(max−min)(x - \min)/(\max - \min)(x−min)/(max−min) | 需要落在[0,1] |
| RobustScaler | (x−median)/IQR(x - \text{median})/IQR(x−median)/IQR | 有异常值 |
fromsklearn.preprocessingimportStandardScaler scaler=StandardScaler()X_scaled=scaler.fit_transform(X)5.3 处理异常值
# 使用分位数去除极端值lower=df['pe'].quantile(0.01)upper=df['pe'].quantile(0.99)df=df[(df['pe']>lower)&(df['pe']<upper)]第六部分:聚类的评估与解释
6.1 内部评估指标
| 指标 | 公式 | 说明 |
|---|---|---|
| Inertia | $\sum | x - \mu |
| 轮廓系数 | (b−a)/max(a,b)(b-a)/\max(a,b)(b−a)/max(a,b) | 越接近1越好 |
| Davies-Bouldin | 簇间/簇内距离比 | 越小越好 |
| Calinski-Harabasz | 簇间方差/簇内方差 | 越大越好 |
6.2 聚类结果的可视化
PCA降维可视化:
fromsklearn.decompositionimportPCA pca=PCA(n_components=2)X_pca=pca.fit_transform(X_scaled)plt.scatter(X_pca[:,0],X_pca[:,1],c=labels,cmap='viridis')plt.xlabel('PC1')plt.ylabel('PC2')plt.title('聚类结果可视化(PCA降维)')6.3 簇特征分析
# 计算每个簇的特征均值cluster_means=pd.DataFrame(X_scaled,columns=feature_names)cluster_means['cluster']=labels cluster_profile=cluster_means.groupby('cluster').mean()# 找出每个簇的最显著特征forclusterincluster_profile.index:top_features=cluster_profile.loc[cluster].abs().sort_values(ascending=False).head(5)print(f"簇{cluster}的特征:{top_features.index.tolist()}")