当前位置:   article > 正文

机器学习练手(二):基于KMeans的股票分类

机器学习练手(二):基于KMeans的股票分类

总结:本文为和鲸python 机器学习原理与实践·闯关训练营资料整理而来,加入了自己的理解(by GPT4o)

原活动链接

在前一关我们学习了逻辑回归,学会如何训练模型、数据基础性分析、如何处理空值等操作,下面我们开始新的一关 KMeans

KMeans

KMeans 是我们最常用的基于欧式距离的聚类算法,其认为两个目标的距离越近,相似度越大。

KMeans 算法的思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本集划分为 K 个簇,其目的是让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。

基于 KMeans 的股票分类

以往的量化投资中对于股票的划分分类,通常取决于行业、市值、地域等等指标划分,而这些分类指标并不能很好的区分公司的好坏。而现在可以通过每日的交易行情实时划分分类,通过计算当日前一个月的分类从而确定该股票分类,更好的降低投资风险,提供风险对冲。该数据集有 2024-05-06 的全部上市公司股票交易行情信息,其中包含日期、开盘价、收盘价、最高价、最低价、成交量、成交额等特征信息,另外该模型使用的数据为真实数据,可以在实际操作中使用。

股市有风险,入市需谨慎!

引入依赖
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from sklearn.cluster import KMeans
from sklearn.metrics import accuracy_score, silhouette_score
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
加载数据
# 1. 加载数据

stock = pd.read_csv('./data/stocks-2.csv', index_col='Unnamed: 0')
stock.head()
  • 1
  • 2
  • 3
  • 4
symbolcodenametradepricechangechangepercentbuysellsettlementopenhighlowvolumeamountticktimeperpbmktcapnmcturnoverratio
0sz0000011平安银行10.890.100.92710.8810.8910.7910.9611.1110.84178410057195381749315:00:004.8400.5082.113304e+072.113264e+070.91938
1sz0000022万 科A7.460.050.6757.457.467.417.637.887.44524493788399692170315:00:007.2430.3558.900309e+067.248834e+065.39773
2sz0000044国华网安10.440.232.25310.4310.4410.219.9910.469.97988544010205984215:00:00-8.8217.4781.382050e+051.318448e+057.82769
3sz0000066深振业A3.870.000.0003.873.883.873.964.013.86231871869134876515:00:00-6.5090.7525.224481e+055.224451e+051.71759
4sz0000077*ST全新4.09-0.15-3.5384.094.104.244.264.264.0424405501002858915:00:0039.40311.2121.416972e+051.263597e+050.78995
stock.info()
  • 1
<class 'pandas.core.frame.DataFrame'>
Index: 5360 entries, 0 to 5359
Data columns (total 20 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   symbol         5360 non-null   object 
 1   code           5360 non-null   int64  
 2   name           5360 non-null   object 
 3   trade          5360 non-null   float64
 4   pricechange    5360 non-null   float64
 5   changepercent  5360 non-null   float64
 6   buy            5360 non-null   float64
 7   sell           5360 non-null   float64
 8   settlement     5360 non-null   float64
 9   open           5360 non-null   float64
 10  high           5360 non-null   float64
 11  low            5360 non-null   float64
 12  volume         5360 non-null   int64  
 13  amount         5360 non-null   int64  
 14  ticktime       5360 non-null   object 
 15  per            5360 non-null   float64
 16  pb             5360 non-null   float64
 17  mktcap         5360 non-null   float64
 18  nmc            5360 non-null   float64
 19  turnoverratio  5360 non-null   float64
dtypes: float64(14), int64(3), object(3)
memory usage: 879.4+ KB
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
# 2. 删除与分类数无关的特征列

new_stock = stock.drop(['symbol', 'code', 'name', 'ticktime'], axis=1)
new_stock.head()
  • 1
  • 2
  • 3
  • 4
tradepricechangechangepercentbuysellsettlementopenhighlowvolumeamountperpbmktcapnmcturnoverratio
010.890.100.92710.8810.8910.7910.9611.1110.8417841005719538174934.8400.5082.113304e+072.113264e+070.91938
17.460.050.6757.457.467.417.637.887.4452449378839969217037.2430.3558.900309e+067.248834e+065.39773
210.440.232.25310.4310.4410.219.9910.469.979885440102059842-8.8217.4781.382050e+051.318448e+057.82769
33.870.000.0003.873.883.873.964.013.862318718691348765-6.5090.7525.224481e+055.224451e+051.71759
44.09-0.15-3.5384.094.104.244.264.264.0424405501002858939.40311.2121.416972e+051.263597e+050.78995
确定分类个数
# 3. 利用肘部法则确定分类数

inertia = []
silhouette_scores = []
i_range = range(2, 11)
for i in i_range:
    kmeans = KMeans(n_clusters=i, random_state=10).fit(new_stock)
    inertia.append(kmeans.inertia_)
    silhouette_scores.append(silhouette_score(new_stock, kmeans.labels_))

inertia, silhouette_scores
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
([4.1450149552461185e+20,
  2.189263003520667e+20,
  1.6730094412041477e+20,
  9.618885942140525e+19,
  6.943786093529641e+19,
  5.561627387942571e+19,
  4.014992267655058e+19,
  3.2416675726264095e+19,
  2.4597061039181627e+19],
 [0.8944521948807374,
  0.8260147612056037,
  0.7907694574915884,
  0.7490320699906337,
  0.6649888612149094,
  0.6339363805356698,
  0.6338265053972817,
  0.6300107391392652,
  0.6195255140687659])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这段代码使用肘部法则和轮廓分数(silhouette score)来确定数据集的最佳分类数(簇数)。下面是对代码的详细解析:

导入必要的库

from sklearn.cluster import KMeans  
from sklearn.metrics import silhouette_score  
  • 1
  • 2

初始化变量

inertia = []  
silhouette_scores = []  
i_range = range(2, 11)  
  • 1
  • 2
  • 3
  • inertia:用来存储不同簇数下的簇内误差平方和(SSE)。
  • silhouette_scores:用来存储不同簇数下的轮廓分数。
  • i_range:簇数的范围,从2到10(包括2和10)。

迭代不同的簇数

for i in i_range:  
    kmeans = KMeans(n_clusters=i, random_state=10).fit(new_stock)  
    inertia.append(kmeans.inertia_)  
    silhouette_scores.append(silhouette_score(new_stock, kmeans.labels_))  
  • 1
  • 2
  • 3
  • 4
  • for i in i_range:遍历簇数范围,从2到10。
  • kmeans = KMeans(n_clusters=i, random_state=10).fit(new_stock):为每个簇数创建并训练一个KMeans模型。
    • n_clusters=i:设置当前簇数。
    • random_state=10:设置随机种子,以确保结果可复现。
    • fit(new_stock):对数据集 new_stock 进行聚类训练。
  • inertia.append(kmeans.inertia_):将当前簇数下的簇内误差平方和(SSE)添加到 inertia 列表中。
  • silhouette_scores.append(silhouette_score(new_stock, kmeans.labels_)):计算当前簇数下的轮廓分数,并添加到 silhouette_scores 列表中。

输出结果

inertia, silhouette_scores  
  • 1
  • 这将输出不同簇数下的簇内误差平方和(SSE)和轮廓分数。

肘部法则
肘部法则(Elbow Method)通过绘制簇数与SSE的关系图来帮助确定最佳簇数。最佳簇数通常是在SSE曲线开始明显变平的位置,即肘部位置。

轮廓分数
轮廓分数(Silhouette Score)用于评估聚类的质量,其值在-1到1之间。值越高表示聚类效果越好。通过比较不同簇数下的轮廓分数,可以选择分数最高的簇数作为最佳簇数。

总结
这段代码的目的是通过计算不同簇数下的簇内误差平方和(SSE)和轮廓分数,帮助选择数据集的最佳分类数。结合肘部法则和轮廓分数可以更全面地评估聚类效果,从而确定最合适的簇数。

# 4. 确定分类数
plt.figure(figsize=(15,5))

plt.subplot(1, 2, 1)
plt.plot(i_range, inertia, marker='o')

plt.subplot(1, 2, 2)
plt.plot(i_range, silhouette_scores, marker='o')

plt.tight_layout()
plt.show()

# 左图在 2 到 5 的时候,曲线下降速率明显下降。
# 右图在 2,3,4,5 时,轮廓系数比较高。
# 结合两图,选择 3 作为聚类数。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

# 5. 分类

kmeans_final = KMeans(n_clusters=3, random_state=10).fit(new_stock)

labels = kmeans_final.labels_
new_stock['cluster'] = labels
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
new_stock.head()
  • 1
tradepricechangechangepercentbuysellsettlementopenhighlowvolumeamountperpbmktcapnmcturnoverratiocluster
010.890.100.92710.8810.8910.7910.9611.1110.8417841005719538174934.8400.5082.113304e+072.113264e+070.919382
17.460.050.6757.457.467.417.637.887.4452449378839969217037.2430.3558.900309e+067.248834e+065.397731
210.440.232.25310.4310.4410.219.9910.469.979885440102059842-8.8217.4781.382050e+051.318448e+057.827690
33.870.000.0003.873.883.873.964.013.862318718691348765-6.5090.7525.224481e+055.224451e+051.717590
44.09-0.15-3.5384.094.104.244.264.264.0424405501002858939.40311.2121.416972e+051.263597e+050.789950
查看分类结果
# 6. 查看分类情况

new_stock['cluster'].value_counts()
  • 1
  • 2
  • 3
cluster
0    4998
2     332
1      30
Name: count, dtype: int64
  • 1
  • 2
  • 3
  • 4
  • 5
总结

KMeans 在确定分类个数计算时,无法使用 object 类型的数据,应当提前删除或对特征进行 one-hot 处理。

闯关题

STEP1:请根据要求完成题目

Q1. KMeans 中某个参数的含义是正确的?
A. n_clusters 分类个数
B. inertia_ 轮廓系数
C. silhouette_scores 曲线下降速率

Q2. 修改KMeans的划分集群个数为 4个,那么 002829 股票的分类是哪个?
A. 0
B. 1
C. 2
D. 3

kmeans_final2 = KMeans(n_clusters=4, random_state=10).fit(new_stock)

labels = kmeans_final2.labels_
stock['cluster'] = labels
stock[stock['symbol'] == 'sz002829']['cluster']
  • 1
  • 2
  • 3
  • 4
  • 5
1304    0
Name: cluster, dtype: int32
  • 1
  • 2

Q3. 前300个股票数据集划分集群的最优个数是多少?
A. 1
B. 3
C. 5
D. 10

new_stock = new_stock[0:300]

inertia = []
silhouette_scores = []
i_range = range(2, 11)
for i in i_range:
    # 计算分类并保存指标
    kmeans = KMeans(n_clusters=i, random_state=10).fit(new_stock)
    inertia.append(kmeans.inertia_)
    silhouette_scores.append(silhouette_score(new_stock, kmeans.labels_))
inertia, silhouette_scores
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
([2.5308780913486823e+19,
  1.3473879858220839e+19,
  7.413489715471633e+18,
  6.109726555261718e+18,
  3.463054550988757e+18,
  2.604280833562603e+18,
  2.0732638975060705e+18,
  1.6982759851707302e+18,
  1.5100566906400458e+18],
 [0.8998406279029784,
  0.7527373456851054,
  0.692685627034619,
  0.6892926502877917,
  0.6522651603158817,
  0.6047949381607308,
  0.5696962854320331,
  0.5676513528559564,
  0.5655907482205398])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
plt.figure(figsize=(15,5))

plt.subplot(1, 2, 1)
plt.plot(i_range, inertia, marker='o')

plt.subplot(1, 2, 2)
plt.plot(i_range, silhouette_scores, marker='o')

plt.tight_layout()
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#填入你的答案并运行,注意大小写
a1 = 'A'  # 如 a1= 'A'
a2 = 'A'  # 如 a2= 'A'
a3 = 'B'  # 如 a3= 'A'
  • 1
  • 2
  • 3
  • 4
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/929971
推荐阅读
相关标签
  

闽ICP备14008679号