先导入数据处理与可视化
#加载包
import numpy as np
import pandas as pd
from plotnine import*
import matplotlib as mpl
import matplotlib.pyplot as plt
#中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# notebook嵌入图片
%matplotlib inline
# 提高分辨率
%config InlineBackend.figure_format='retina'
# 忽略警告
import warnings
warnings.filterwarnings('ignore')
导入附件一数据
# 导入csv文件
data = pd.read_csv("C:/Users/lenovo/Desktop/data1.csv", encoding="utf-8", header = None)
# 打印数据前5行
data.head(5)
绘制各波段下药材吸光度折线图
# 绘制图形观察
plt.figure(dpi = 600,figsize = (24,8))
for i in range(1,data.shape[0]):
plt.plot(data.iloc[0,:],data.iloc[i,:])
plt.title("红外光谱曲线图",fontsize=20) # 图像标题
plt.xlabel("波数",fontsize=20) # X轴标题
plt.ylabel("吸光度",fontsize=20) # Y轴标题
plt.tick_params(labelsize=16)
plt.savefig("红外光谱图.png")
观察到有3条红外光谱偏离,假定其为异常样本,将其剔除
data_yc = data.iloc[1:,1]
# 排序
yc_id = data_yc.sort_values(ascending=False)
# 将在652波段吸光度最高的三个样本删除
data.drop(yc_id[:3].index, inplace=True)
再次绘制各波段下药材吸光度折线图
# 绘制图形观察
plt.figure(dpi = 600,figsize = (24,8))
for i in range(1,data.shape[0]):
plt.plot(data.iloc[0,:],data.iloc[i,:])
plt.title("红外光谱曲线图",fontsize=20) # 图像标题
plt.xlabel("波数",fontsize=20) # X轴标题
plt.ylabel("吸光度",fontsize=20) # Y轴标题
plt.tick_params(labelsize=16)
plt.savefig("红外光谱图(剔除异常).png")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3hoGy2u1-1651724311938)(D:\Jupyter%20Notebook\红外光谱图(剔除异常)].png)
数据处理到此结束
题目要求根据附件1中红外光谱数据,研究不同种类药材的特性和差异性,并鉴别药材种类。由于题目没给出已知的药材种类,所以这是一个聚类问题。
中红外光谱中波数为特征,但波数太多,特征高达3348个,如不进行特征降维将极大程度影响模型训练所需时间。
根据聚类中心与各类药材中红外光谱数据研究特性和差异性,可以从平均值,各波段平均极差等统计数值进行探究。
降维算法大致分为线性降维与非线性降维,比较流行的算法有两种,即:PCA(主成分分析)、KPAC(基于核函数的主成分分析),但由于KPAC调参是在有监督学习情况下使用机器学习算法进行检验的,在这里并不合适,所以选择PCA进行降维。
这里保证新特征保留以往特征95%的信息:
from sklearn.decomposition import PCA #主成分分析法
#PCA方法降维
#保持90%的信息
pca = PCA(n_components=0.95)
pca_95 = pca.fit_transform(data.iloc[1:,:])
pca_95.shape
降维后特征数为2,共422个样本
查看降维后特征的信息量占原特征信息量的比例:
pca.explained_variance_ratio_
pca.explained_variance_ratio_.sum()
输出0.96086,说明新特征的信息量占原特征信息量的96.086%
新特征可视化:
plt.figure(dpi = 600,figsize = (5,3))
plt.scatter(pca_95[:,0],pca_95[:,1])
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BWuk4wqh-1651724311940)(D:\Jupyter%20Notebook\新特征散点图.png)]
常用的聚类方法有两种,分别是K-Means与DBSCAN,其中K-Means需要调整集群数与迭代次数。DBSCAN需要调整学习率与叶子数
迭代次数使用默认值100次即可,最关键的参数是集群数,这里采用两种方法结合判断,即:手肘法与轮廓分数法
假定集群数在2~7类间,以2为初始值,1为步长进行网格搜索,并绘制手肘图与轮廓分数图
# K-Means聚类
# 惯性值
SSE = []
# 轮廓分数
SIL = []
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
for i in range(2,8):
kmeans = KMeans(n_clusters = i,n_init = 100)
y_pred = kmeans.fit_predict(pca_95)
SSE.append([i,kmeans.inertia_])
SIL.append([i,silhouette_score(pca_95,kmeans.labels_)])
# 将list转为DataFrame
sse = pd.DataFrame(SSE,columns = ["k","SSE"])
sil = pd.DataFrame(SIL,columns = ["k","SIL"])
手肘法:随着聚类数k的增大,样本划分会更加精细,每个簇的聚合程度会逐渐提高,误差平方与SSE也会逐渐变小。并且当k小于真实聚类数时,在增加k所得到的聚合程度回报会迅速变小,所以SSE的下降幅度会骤减,然后随着k值的继续增大二趋于平缓。值越小越好
# 绘制手肘图
plt.figure(dpi = 600,figsize = (5,3))
plt.plot(sse["k"],sse["SSE"])
plt.scatter(sse["k"],sse["SSE"])
plt.savefig("手肘图.png")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ttsJo2H7-1651724311943)(D:\Jupyter%20Notebook\手肘图.png)]
可以看到当 k = 3 k = 3 k=3时SSE数值已经有了放缓的趋势,这里是一个转折点,当然凭这一张图理由并不是那么的充分。
轮廓分数法:实例的轮廓系数等于(b-a)/max(a,b),其中a是与同一集群中其他实例的平均距离,b是平均最近集群距离。值越大越好
# 轮廓值判断
plt.figure(dpi = 600,figsize = (5,3))
plt.plot(sil["k"],sil["SIL"])
plt.scatter(sil["k"],sil["SIL"])
plt.savefig("轮廓分数图.png")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Jt9PLtS-1651724311945)(D:\Jupyter%20Notebook\轮廓分数图.png)]
可以看到当 k = 2 k = 2 k=2或 k = 3 k = 3 k=3时,结果都是可以接受的,若 k k k值继续增大效果将不再理想。
综上所述, k k k值应当选取3,这样得到的K_Means模型效果才是最优的。
使用最优参训练模型,并输出分类各样本分类:
kmeans = KMeans(n_clusters = 3,n_init = 100)
y_pred = kmeans.fit_predict(pca_90)
y_pred
输出质心:
# 输出中心点
kmeans.cluster_centers_
由于在DBSCAN聚类算法中,学习率与叶子数是相互影响的,所以要将它们一起调整,学习率以0.001为初始值,0.001为步长;叶子数以1为初始值,1为步长进行网格搜索,搜索停止的目标是,标签中没有 − 1 -1 −1(算法认为此样本为异常),标签种类为 3 3 3种。
# DBSCAN聚类
from sklearn.cluster import DBSCAN
count = 0
e = 0
for i in np.arange(0.0001,1,0.0001):
for j in np.arange(1,400,1):
db = DBSCAN(eps=i, min_samples = j)
db.fit(pca_90)
labels = db.labels_
count = labels[labels == -1].shape[0]
if count == 0 & np.unique(labels).shape[0] == 3:
e = i
break
e