Day_17 无监督算法中的聚类

发布于:2025-05-14 ⋅ 阅读:(13) ⋅ 点赞:(0)
"""
无监督算法中的聚类,常利用聚类来发现数据中的模式,并对每一个聚类后的类别特征进行可视化,
以此得到新的特征---赋予实际含义。
"""

# 先运行之前预处理好的代码
import pandas as pd
import pandas as pd  # 用于数据处理和分析,可处理表格数据。
import numpy as np  # 用于数值计算,提供了高效的数组操作。
import matplotlib.pyplot as plt  # 用于绘制各种类型的图表
import seaborn as sns  # 基于matplotlib的高级绘图库,能绘制更美观的统计图形。
import warnings

warnings.filterwarnings("ignore")

# 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统常用黑体字体
plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号
data = pd.read_csv('data.csv')  # 读取数据

# 先筛选字符串变量
discrete_features = data.select_dtypes(include=['object']).columns.tolist()
# Home Ownership 标签编码
home_ownership_mapping = {
    'Own Home': 1,
    'Rent': 2,
    'Have Mortgage': 3,
    'Home Mortgage': 4
}
data['Home Ownership'] = data['Home Ownership'].map(home_ownership_mapping)

# Years in current job 标签编码
years_in_job_mapping = {
    '< 1 year': 1,
    '1 year': 2,
    '2 years': 3,
    '3 years': 4,
    '4 years': 5,
    '5 years': 6,
    '6 years': 7,
    '7 years': 8,
    '8 years': 9,
    '9 years': 10,
    '10+ years': 11
}
data['Years in current job'] = data['Years in current job'].map(years_in_job_mapping)

# Purpose 独热编码,记得需要将bool类型转换为数值
data = pd.get_dummies(data, columns=['Purpose'])
data2 = pd.read_csv("data.csv")  # 重新读取数据,用来做列名对比
list_final = []  # 新建一个空列表,用于存放独热编码后新增的特征名
for i in data.columns:
    if i not in data2.columns:
        list_final.append(i)  # 这里打印出来的就是独热编码后的特征名
for i in list_final:
    data[i] = data[i].astype(int)  # 这里的i就是独热编码后的特征名

# Term 0 - 1 映射
term_mapping = {
    'Short Term': 0,
    'Long Term': 1
}
data['Term'] = data['Term'].map(term_mapping)
data.rename(columns={'Term': 'Long Term'}, inplace=True)  # 重命名列
continuous_features = data.select_dtypes(include=['int64', 'float64']).columns.tolist()  # 把筛选出来的列名转换成列表

# 连续特征用中位数补全
for feature in continuous_features:
    mode_value = data[feature].mode()[0]  # 获取该列的众数。
    data[feature].fillna(mode_value, inplace=True)  # 用众数填充该列的缺失值,inplace=True表示直接在原数据上修改。

# 最开始也说了 很多调参函数自带交叉验证,甚至是必选的参数,你如果想要不交叉反而实现起来会麻烦很多
# 所以这里我们还是只划分一次数据集
from sklearn.model_selection import train_test_split

X = data.drop(['Credit Default'], axis=1)  # 特征,axis=1表示按列删除
y = data['Credit Default']  # 标签
# # 按照8:2划分训练集和测试集
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)  # 80%训练集,20%测试集


import numpy as np
import pandas as pd
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import seaborn as sns

# 标准化数据(聚类前通常需要标准化)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
""""
在机器学习和数据分析中,X_scaled = scaler.fit_transform(X)是一个常见的数据预处理步骤,
用于对特征矩阵X进行标准化或归一化处理。
    fit:计算数据的统计特征(如均值、标准差、最小值、最大值等)。
    transform:使用计算得到的统计特征对数据进行标准化或归一化。
"""

import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
"""
这行代码导入了三种常用的聚类评估指标,用于评估聚类算法(如 K-means)的性能。
这些指标均无需真实标签(适用于无监督学习),但评估角度不同。
    silhouette_score(轮廓系数)
    calinski_harabasz_score(Calinski-Harabasz指数,CH指数)
    davies_bouldin_score(Davies-Bouldin 指数,DB 指数)
"""
import matplotlib.pyplot as plt
import seaborn as sns

# 评估不同K值下的指标
k_range = range(2, 11) # 测试k从2到10
inertia_values = []
silhouette_values = []
ch_values = []
dh_scores = []
for k in k_range:
    kmeans = KMeans(n_clusters=k, random_state=42)
    """
    功能:创建 K-means 聚类模型实例。
    参数:
        n_clusters=k:指定要划分的簇(cluster)数量,需预先设定(如k=3表示将数据分为 3 类)。
        random_state=42:固定随机数种子,确保每次运行结果一致(便于复现实验)。
    """
    kmeans_labels = kmeans.fit_predict(X_scaled)
    """
    功能:对标准化后的数据X_scaled进行聚类,并返回每个样本的簇标签。
    方法分解:
        fit(X_scaled):训练模型,通过迭代找到最优簇中心(即"质心"),最小化所有样本到其所属簇质心的距离平方和(称为惯性,inertia)。
        predict(X_scaled):基于训练得到的簇中心,为每个样本分配所属簇的标签(如 0、1、2...)。
    返回值:kmeans_labels是一维数组,存储每个样本的簇索引(如[0, 1, 0, 2, ...])。
    """
    inertia_values.append(kmeans.inertia_) # 惯性(肘部法则)
    silhouette = silhouette_score(X_scaled, kmeans_labels) # 轮廓系数 函数第二个参数为kmeans_labels
    silhouette_values.append(silhouette)
    ch = calinski_harabasz_score(X_scaled, kmeans_labels) # CH指数
    ch_values.append(ch)
    dh = davies_bouldin_score(X_scaled, kmeans_labels) # DB指数
    dh_scores.append(dh)
    print(f"k={k},惯性:{kmeans.inertia_:.2f},轮廓系数:{silhouette:.3f},CH指数:{ch:.3f},DB指数:{dh:.3f}")


# 绘制评估指标图
plt.figure(figsize=(15, 10))

# 肘部法则图(Inertia)
plt.subplot(2, 2, 1)
plt.plot(k_range, inertia_values, marker='o')
plt.title('肘部法则确定最优聚类数k(惯性越小越好)')
plt.xlabel('聚类数(k)')
plt.ylabel('惯性')
plt.grid(True)

# 轮廓系数图
plt.subplot(2, 2, 2)
plt.plot(k_range, silhouette_values, marker='o', color='orange')
plt.title('轮廓系数确定最优聚类数k(越大越好)')
plt.xlabel('聚类数(k)')
plt.ylabel('<轮廓系数')
plt.grid(True)

# CH指数图
plt.subplot(2, 2, 3)
plt.plot(k_range, ch_values, marker='o', color='green')
plt.title('calinski_harabasz指数确定最优聚类数k(越大越好)')
plt.xlabel('聚类数(k)')
plt.ylabel('CH 指数')
plt.grid(True)

# DB 指数图
plt.subplot(2, 2, 4)
plt.plot(k_range, dh_scores, marker='o', color='red')
plt.title('davies_bouldin确定最优聚类数k(越小越好)')
plt.xlabel('聚类数(k)')
plt.ylabel('DB 指数')
plt.grid(True)

plt.tight_layout()
plt.show()


"""
上面这几个图怎么看,
1. 肘部法则图: 找下降速率变慢的拐点,这里都差不多
2. 轮廓系数图:找局部最高点,这里选6不能选7
3. CH指数图: 找局部最高点,这里选7之前的都还行
4. DB指数图:找局部最低点,这里选6 7 9 10都行

综上,选择6比较合适。
- 为什么选择局部最优的点,因为比如簇间差异,分得越细越好,但是k太细了没价值,所以要有取舍。
- 比如k可以选3和5,推荐选择5,能包含更多信息
"""

# 提示用户选择k值
selected_k = 6

# 使用选择的k值进行KMeans聚类
kmeans = KMeans(n_clusters=selected_k, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)
# 返回值:kmeans_labels是一维数组,存储每个样本的簇索引(如[0, 1, 0, 2, ...])。
X["KMeans_Cluster"] = kmeans_labels # 将聚类结果添加到原始数据中


# 使用PCA降维到2D进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
"""
pca = PCA(n_components=2)
    功能:创建PCA模型实例,指定降维后的维度为2。
    参数:n_components=2:将原始数据的特征维度从n维降至2维,常用于可视化(如散点图)。
X_pca = pca.fit_transform(X_scaled)
    功能:执行 PCA 降维,返回降维后的数据矩阵X_pca。
    方法分解:
        fit(X_scaled):计算数据的主成分(即特征向量),并按方差解释率排序。
        主成分:数据中方差最大的方向,是原始特征的线性组合。
        方差解释率:每个主成分解释的原始数据方差比例,反映该成分的重要性。
        transform(X_scaled):将原始数据投影到已计算的主成分上,得到降维后的数据。
    返回值:X_pca是形状为(n_samples, 2)的二维数组,每行代表一个样本在新坐标系下的坐标。
"""

# KMeans聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=kmeans_labels, palette="viridis")
"""
x=X_pca[:, 0]:指定 x 轴数据为PCA降维后的第一主成分(解释方差最大的方向)。
y=X_pca[:, 1]:指定 y 轴数据为PCA降维后的第二主成分。
hue=kmeans_labels:根据K-means聚类的标签对散点着色,同一簇的数据点颜色相同。
palette='viridis':使用viridis颜色映射(渐变色),使不同簇的颜色区分明显且视觉和谐。
"""
plt.title(f'KMeans Clustering with k={selected_k}(PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()

# 打印 KMeans 聚类标签的前几行
print(f"KMeans Cluster labels (k={selected_k}) added to X:")
print(X[['KMeans_Cluster']].value_counts())
print(X['KMeans_Cluster'].value_counts()) # 两行代码效果一样
"""
X[['KMeans_Cluster']]:从DataFrame X中选取聚类标签列。外层方括号[]表示选取操作,内层 ['KMeans_Cluster']是列名列表。
value_counts():统计该列中每个唯一值(即每个簇标签)的出现次数,返回降序排列的结果。
"""





网站公告

今日签到

点亮在社区的每一天
去签到