机器学习练手(二):基于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. 加载数据

stock = pd.read_csv('./data/stocks-2.csv', index_col='Unnamed: 0')
stock.head()
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()
<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
# 2. 删除与分类数无关的特征列

new_stock = stock.drop(['symbol', 'code', 'name', 'ticktime'], axis=1)
new_stock.head()
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
([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])

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

导入必要的库

from sklearn.cluster import KMeans  
from sklearn.metrics import silhouette_score  

初始化变量

inertia = []  
silhouette_scores = []  
i_range = range(2, 11)  
  • 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_))  
  • 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  
  • 这将输出不同簇数下的簇内误差平方和(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 作为聚类数。


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

# 5. 分类

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

labels = kmeans_final.labels_
new_stock['cluster'] = labels
new_stock.head()
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()
cluster
0    4998
2     332
1      30
Name: count, dtype: int64
总结

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']
1304    0
Name: cluster, dtype: int32

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
([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])
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()


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

#填入你的答案并运行,注意大小写
a1 = 'A'  # 如 a1= 'A'
a2 = 'A'  # 如 a2= 'A'
a3 = 'B'  # 如 a3= 'A'
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的使用pcl::KMeans实现k-means聚类的示例代码: ```c++ #include <pcl/features/normal_3d.h> #include <pcl/filters/voxel_grid.h> #include <pcl/kdtree/kdtree_flann.h> #include <pcl/segmentation/extract_clusters.h> #include <pcl/features/fpfh.h> #include <pcl/kdtree/kmeans.h> // 定义点云类型 typedef pcl::PointXYZ PointType; typedef pcl::PointCloud<PointType> PointCloud; typedef pcl::Normal NormalType; typedef pcl::PointCloud<NormalType> NormalCloud; typedef pcl::FPFHSignature33 FeatureType; typedef pcl::PointCloud<FeatureType> FeatureCloud; int main() { // 加载点云数据 PointCloud::Ptr cloud(new PointCloud); pcl::io::loadPCDFile("input.pcd", *cloud); // 下采样 pcl::VoxelGrid<PointType> grid; grid.setLeafSize(0.01, 0.01, 0.01); grid.setInputCloud(cloud); PointCloud::Ptr cloud_downsampled(new PointCloud); grid.filter(*cloud_downsampled); // 计算法线 pcl::NormalEstimation<PointType, NormalType> ne; ne.setInputCloud(cloud_downsampled); pcl::search::KdTree<PointType>::Ptr tree(new pcl::search::KdTree<PointType>); ne.setSearchMethod(tree); NormalCloud::Ptr normals(new NormalCloud); ne.setRadiusSearch(0.03); ne.compute(*normals); // 计算特征 pcl::FPFHEstimation<PointType, NormalType, FeatureType> fpfh; fpfh.setInputCloud(cloud_downsampled); fpfh.setInputNormals(normals); fpfh.setSearchMethod(tree); FeatureCloud::Ptr features(new FeatureCloud); fpfh.setRadiusSearch(0.05); fpfh.compute(*features); // k-means聚类 int k = 3; pcl::KMeans<FeatureType> kmeans; kmeans.setClusterSize(k); kmeans.setEuclideanDistance(true); kmeans.setInputCloud(features); std::vector<pcl::PointIndices> cluster_indices; kmeans.extract(cluster_indices); // 输出聚类结果 for (int i = 0; i < cluster_indices.size(); i++) { std::cout << "Cluster " << i << ":\n"; for (int j = 0; j < cluster_indices[i].indices.size(); j++) { std::cout << cluster_indices[i].indices[j] << " "; } std::cout << std::endl; } return 0; } ``` 这段代码首先加载了一个点云数据,然后进行下采样、计算法线和特征等操作。接着使用pcl::KMeans对特征向量进行聚类,得到聚类结果。最后将聚类结果输出到控制台中。需要注意的是这里的特征类型是pcl::FPFHSignature33,如果使用其他类型的特征,需要将pcl::KMeans的模板参数替换成对应的类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值