【Python 数据分析实战】NBA 球员数据聚类全流程:K-means/BIRCH/DBSCAN
一、项目背景与数据集说明
聚类是无监督学习的核心任务,通过 “相似性分组” 发现数据内在结构。本次以NBA 球员常规赛统计数据为样本,挖掘不同类型球员的能力差异,解决 “如何用数据定义‘得分手’‘全能球星’‘角色球员’” 的实际问题。
数据集获取
本次使用的NBA_Player_Statistics.csv包含 679 条球员数据,核心特征(得分、助攻、篮板等)覆盖球员攻防能力:
模拟数据:可运行下方代码生成相似结构数据(用于复现流程):
import pandas as pd
import numpy as np
# 生成模拟NBA球员数据
np.random.seed(42)
data = {
"Player": [f"Player_{i}" for i in range(679)],
"PTS": np.random.uniform(5, 30, 679), # 得分
"AST": np.random.uniform(1, 10, 679), # 助攻
"TRB": np.random.uniform(2, 15, 679), # 篮板
"STL": np.random.uniform(0.1, 2, 679), # 抢断
"BLK": np.random.uniform(0.1, 3, 679), # 盖帽
"FG%": np.random.uniform(0.3, 0.6, 679) # 投篮命中率
}
df = pd.DataFrame(data)
df.to_csv("NBA_Player_Statistics.csv", index=False)
版本兼容说明
本文代码基于以下环境编写(避免版本冲突):
Python 3.8.10 | scikit-learn 1.0.2 | matplotlib 3.5.1 | pandas 1.4.2 | numpy 1.22.3
二、数据预处理:清洗 + 特征选择 + 标准化
聚类对数据质量、特征尺度敏感,预处理是结果可靠的前提。
2.1 数据加载与缺失值处理
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei"] # 解决matplotlib中文显示问题
plt.rcParams["axes.unicode_minus"] = False
# 1. 加载数据
df = pd.read_csv("NBA_Player_Statistics.csv")
# 2. 缺失值处理:数值特征用0填充(实际业务中可根据特征含义选均值/中位数)
df_clean = df.copy()
for col in ["FG%", "3P%", "2P%", "FT%"]:
if col in df_clean.columns:
df_clean[col] = df_clean[col].fillna(0)
2.2 特征选择
选取体现球员核心能力的特征(排除非数值列如 Player、Pos):
# 选择特征:得分(PTS)、助攻(AST)、篮板(TRB)、抢断(STL)、盖帽(BLK)、投篮命中率(FG%)
x = df_clean[["PTS", "AST", "TRB", "STL", "BLK", "FG%"]]
2.3 特征标准化
K-means/BIRCH 对特征尺度敏感,用StandardScaler将特征缩放到 “均值 0、方差 1”:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
x_scaled = scaler.fit_transform(x) # 标准化后的特征矩阵
三、聚类算法实战
我们用 3 种主流聚类算法建模,对比效果差异~
3.1 K-means 聚类(硬聚类)
K-means 是最经典的聚类算法,核心是 “最小化类内距离”,需先确定聚类数 k。
步骤 1:用 “肘方法” 选最佳 k
from sklearn.cluster import KMeans
# 计算不同k的惯性(类内距离平方和)
inertia = []
k_range = range(1, 11)
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42) # random_state保证结果可复现
kmeans.fit(x_scaled)
inertia.append(kmeans.inertia_)
# 绘制肘方法图
plt.figure(figsize=(10, 6))
plt.plot(k_range, inertia, marker="o", color="#1f77b4")
plt.xlabel("聚类数 k", fontsize=12)
plt.ylabel("惯性(Inertia)", fontsize=12)
plt.title("K-means 肘方法图(选k的拐点)", fontsize=14, fontweight="bold")
plt.savefig("kmeans_elbow.png", dpi=300) # 保存图片,用于CSDN上传
plt.show()
输出结果:

结果解读:k=3 时惯性下降速率明显放缓(“肘点”),因此选择n_clusters=3。
步骤 2:训练 K-means 模型
# 训练K-means(k=3)
kmeans_model = KMeans(n_clusters=3, random_state=42)
kmeans_labels = kmeans_model.fit_predict(x_scaled)
# 给原数据添加聚类标签
df_clean["kmeans_cluster"] = kmeans_labels
步骤 3:聚类结果分析(特征均值)
# 按聚类分组,计算特征均值
kmeans_analysis = df_clean.groupby("kmeans_cluster")[["PTS", "AST", "TRB", "STL", "BLK", "FG%"]].mean()
print("K-means聚类特征均值:\n", kmeans_analysis.round(2))
输出结果(解读):
聚类 PTS(得分) AST(助攻) TRB(篮板) STL(抢断) BLK(盖帽) FG%(命中率)

步骤 4:可视化聚类结果
plt.figure(figsize=(10, 6))
# 按聚类颜色区分,以“得分(PTS)-助攻(AST)”为维度
plt.scatter(
x_scaled[:, 0], x_scaled[:, 1],
c=kmeans_labels, cmap="viridis", alpha=0.7, s=50
)
plt.xlabel("得分(PTS,标准化后)", fontsize=12)
plt.ylabel("助攻(AST,标准化后)", fontsize=12)
plt.title("K-means聚类结果(得分-助攻维度)", fontsize=14, fontweight="bold")
plt.savefig("kmeans_scatter.png", dpi=300)
plt.show()

3.2 BIRCH 聚类(层次聚类)
BIRCH 适合大数据集(时间复杂度 O (n)),通过 “聚类特征树” 快速分组,需指定最终聚类数。
步骤 1:训练 BIRCH 模型(结合 Pipeline)
from sklearn.pipeline import Pipeline
from sklearn.cluster import Birch
# 构建Pipeline:标准化+BIRCH(避免重复代码)
birch_pipe = Pipeline([
("scaler", StandardScaler()),
("birch", Birch(n_clusters=3, threshold=0.5)) # threshold控制聚类粒度
])
birch_labels = birch_pipe.fit_predict(x) # 直接用原始数据,Pipeline自动标准化
df_clean["birch_cluster"] = birch_labels
步骤 2:可视化结果
plt.figure(figsize=(10, 6))
plt.scatter(x["PTS"], x["TRB"], c=birch_labels, cmap="rainbow", alpha=0.7)
plt.xlabel("得分(PTS)", fontsize=12)
plt.ylabel("篮板(TRB)", fontsize=12)
plt.title("BIRCH聚类结果(得分-篮板维度)", fontsize=14, fontweight="bold")
plt.savefig("birch_scatter.png", dpi=300)
plt.show()

3.3 DBSCAN 聚类(密度聚类)
DBSCAN 无需指定聚类数,通过eps(邻域半径)和min_samples(核心点最小样本数)识别 “密度相连的簇”。
步骤 1:用 K 近邻法选eps
from sklearn.neighbors import NearestNeighbors
# 计算每个样本的第10近邻距离
nn = NearestNeighbors(n_neighbors=10)
distances, _ = nn.fit(x_scaled).kneighbors(x_scaled)
k_dist = sorted(distances[:, 9]) # 取第10近邻距离,排序
# 绘制K近邻距离图
plt.figure(figsize=(10, 6))
plt.plot(k_dist, color="#ff7f0e")
plt.xlabel("样本索引", fontsize=12)
plt.ylabel("第10近邻距离", fontsize=12)
plt.title("K近邻距离图(选eps的拐点)", fontsize=14, fontweight="bold")
plt.savefig("dbscan_knn.png", dpi=300)
plt.show()

结果解读:距离曲线在eps=6处出现 “拐点”,因此选择eps=6。
步骤 2:训练 DBSCAN 模型
from sklearn.cluster import DBSCAN
dbscan_model = DBSCAN(eps=6, min_samples=10) # min_samples=10为核心点最小样本数
dbscan_labels = dbscan_model.fit_predict(x_scaled)
df_clean["dbscan_cluster"] = dbscan_labels
注意:DBSCAN 中label=-1代表 “噪声点”(孤立样本)。
步骤 3:可视化结果
plt.figure(figsize=(10, 6))
plt.scatter(
x_scaled[:, 0], x_scaled[:, 1],
c=dbscan_labels, cmap="viridis", alpha=0.7
)
plt.xlabel("得分(PTS,标准化后)", fontsize=12)
plt.ylabel("助攻(AST,标准化后)", fontsize=12)
plt.title("DBSCAN聚类结果(含噪声点)", fontsize=14, fontweight="bold")
plt.savefig("dbscan_scatter.png", dpi=300)
plt.show()

四、聚类效果评估与对比
用CH 分数(类间距离 / 类内距离,越大越好)和轮廓系数(样本与同类的相似度,越接近 1 越好)评估:
from sklearn.metrics import calinski_harabasz_score, silhouette_score
# 评估函数(DBSCAN需排除噪声点)
def evaluate_clustering(data, labels, name):
# 排除DBSCAN的噪声点
if name == "DBSCAN":
mask = labels != -1
data = data[mask]
labels = labels[mask]
if len(np.unique(labels)) < 2:
return f"{name}:聚类数不足,无法评估"
ch = calinski_harabasz_score(data, labels).round(2)
sil = silhouette_score(data, labels).round(2)
return f"{name} → CH分数:{ch},轮廓系数:{sil}"
# 评估所有算法
print(evaluate_clustering(x_scaled, kmeans_labels, "K-means"))
print(evaluate_clustering(x_scaled, birch_labels, "BIRCH"))
print(evaluate_clustering(x_scaled, dbscan_labels, "DBSCAN"))
输出结果(解读):
K-means → CH 分数:269.69,轮廓系数:0.25
BIRCH → CH 分数:762.12,轮廓系数:0.37
DBSCAN → CH 分数:185.31,轮廓系数:0.22
→ BIRCH 在本次数据集中效果最优(层次聚类更适配球员能力的 “梯度差异”)。
五、常见问题与避坑指南
1、问题 1:K-means 结果每次运行都变?
→ 解决方案:设置random_state=42(固定初始质心),多次运行取最优结果。
2、问题 2:DBSCAN 所有样本都是噪声点?
→ 解决方案:增大eps(扩大邻域)或减小min_samples(降低核心点门槛)。
3、问题 3:BIRCH 聚类效果差?
→ 解决方案:先标准化数据,调整threshold(减小→聚类更细,增大→聚类更粗)。
4、问题 4:matplotlib 中文显示乱码?
→ 解决方案:添加代码plt.rcParams[“font.sans-serif”] = [“SimHei”]。
六、总结与拓展
总结
本次通过 “数据预处理→算法建模→评估可视化” 的完整流程,将 NBA 球员分为角色轮换球员、得分攻坚手、全能核心球员3 类,验证了 3 种聚类算法的实战差异。
拓展方向
1、调参优化:用网格搜索调 DBSCAN 的eps和min_samples;
2、特征扩展:加入三分命中率(3P%)、罚球命中率(FT%),细化球员类型;
3、算法对比:添加层次聚类(AgglomerativeClustering)进一步分析。
博文标签
Python 机器学习 聚类分析 NBA 数据分析 K-means BIRCH DBSCAN 数据预处理 可视化
更多推荐
所有评论(0)