简介:面向计算机专业学生和需要项目实战练习的学习者,这份数据仓库与数据挖掘课程高分期末大作业完整实现银行数据的分类与聚类任务,并配套翔实的实验报告。项目由导师指导并评审认可,得分99分,代码结构清晰、依赖完整,下载后即可运行,能直接作为课程设计、期末大作业或面试作品集的参考模板。压缩包共122个文件,整体大小约11.36MB,主要包含Python源码(py)、银行多维数据集(csv)、实验报告(pdf/docx/pptx)、辅助配置与图表文件等,另有少量Java与XML文件,反映完整的多工具协作流程。内容预览显示数据覆盖交易、账户、客户等真实业务维度,非常适合理解分类与聚类算法在金融场景下的落地过程。目前已有280人学习下载,资源目录组织规范,既适合初学者对照复现,也适合高年级学生快速借鉴高分项目的文档写法与代码组织方式。
1. 课程作业为什么总选银行数据:分类和聚类能同时交出两套结果
打开任何一份“数据仓库与数据挖掘”课程作业要求,十有八九会看到“银行客户数据”或“信用评估数据”。原因很实际:银行数据集天然带标签,比如“是否违约”“是否响应营销活动”,可以直接做有监督的分类;同一份数据丢掉标签,又可以按客户的消费、年龄、余额等特征做无监督的聚类。一次作业把数据挖掘两大任务都覆盖了,老师好出题,学生好交差。这篇文章就把这条最常走的路线拆开讲清楚:从数据怎么选、特征怎么处理,到用 Python 把分类和聚类跑通,再到实验报告怎么写才不被扣分。它不是给你一份现成答案,而是让你照着做就能复现、能改、能自己加东西。
2. 从数据仓库取数到挖掘建模:银行数据集的选型与实验准备
2.1 公开银行数据集怎么选:UCI 是课程作业的主战场
课程作业不需要你自己从数据库里导数据,公开数据集足够用。最常用的三个:
| 数据集 | 样本量 | 特征数 | 标签含义 | 适合任务 |
|---|---|---|---|---|
| German Credit | 1000 | 20 | 信贷是否违约 | 分类为主 |
| Credit Approval | 690 | 15 | 是否批准信用卡 | 分类为主 |
| Bank Marketing(UCI bank) | 45211 | 17 | 是否订阅定期存款 | 分类+聚类 |
我一般首选 Bank Marketing 这份数据。原因在于它样本量大、连续特征和类别特征混合,且原始数据中有一个duration字段,做聚类时特别能体现“特征选择”的意义——后面避坑部分会专门讲。German Credit 样本偏少,模型一调参就容易过拟合,适合新手但报告可写的东西少。
2.2 实验报告的标准骨架:七段式覆盖评分点
课程作业的实验报告,老师眼里通常只看这几部分:问题定义、数据说明、方法选择、实验过程、结果分析、结论、参考资料。这七个部分缺哪个都容易被扣分。
你在写报告时,直接把“数据仓库”的概念嵌入第一部分:说明数据来源、数据量、字段含义,并交代你做了哪些数据仓库层面的预处理,比如一致性检查、缺失值策略、数据变换。这部分不需要写得像学术论文,但要有数据表格、字段清单和简单的统计描述。老师最反感的是“数据是下载的,我直接建模”——没有数据理解过程,作业就失去了训练意义。
2.3 环境准备:一台能跑起来的 Python 学习机
课程作业用 Anaconda 发行版最省事。Python 版本选 3.8 到 3.10 之间都行,sklearn 在 1.0 以上版本对 API 做了调整,建议固定用 1.2 或 1.3 版本,避免网上教程的代码因版本差异报错。
# 创建独立环境,避免把系统 Python 搞乱 conda create -n dm_homework python=3.10 conda activate dm_homework # 安装核心依赖 pip install pandas numpy scikit-learn matplotlib openpyxl这里dm_homework是环境名,你可以换成自己学号缩写。openpyxl是为了把结果导出成 Excel,很多课程要求提交结果表格。装好后验证一下:
python -c "import sklearn, pandas, matplotlib; print(sklearn.__version__, pandas.__version__, matplotlib.__version__)"有版本号输出就说明环境正常。装不上pandas时,常见原因是 conda 和 pip 混用导致路径错乱,建议在同一个环境下只用一个包管理器。
3. 用 Python 实现银行数据分类:逻辑回归与随机森林的完整流程
3.1 数据清洗与特征工程:先让银行数据变成模型能吃的形状
银行数据几乎没有完全干净的。以 Bank Marketing 为例,原始 CSV 的缺失值显示为"unknown",如果不处理,pandas 会把它当成一个类别。
import pandas as pd from sklearn.model_selection import train_test_split # 读取数据,分号分隔是这份数据的老传统 df = pd.read_csv("bank-full.csv", sep=";") # 把 "unknown" 当作缺失值处理 df = df.replace("unknown", pd.NA) # 查看缺失情况 print(df.isna().sum())逻辑说明:replace("unknown", pd.NA)把字符串缺失转成真正的空值,isna().sum()统计每一列的缺失个数。参数说明:sep=";"必须指定,否则整份数据会被读成单列。处理缺失值的策略要看业务含义:job、education这类字段缺失比较少时可以直接填众数;poutcome缺失率高且本身代表“前次营销结果”,填众数反而引入偏差,建议单独保留一个“缺失”标记。
分类任务的特征工程重点有两个。第一个是对类别特征做编码:
# 只保留数值特征和二元类别特征,多分类用哑变量 df_clean = df.dropna(subset=["age", "balance", "duration"]) df_clean = pd.get_dummies(df_clean, columns=["job", "marital", "education", "contact", "month", "poutcome"], drop_first=True) # 将目标转为 0/1 df_clean["y"] = (df_clean["y"] == "yes").astype(int)第二个是数值特征的标准化。逻辑回归对尺度敏感,决策树不在乎,但为了同时跑聚类,我们通常先保留一份标准化后的版本。
from sklearn.preprocessing import StandardScaler features = df_clean.drop(columns=["y"]) target = df_clean["y"] scaler = StandardScaler() X_scaled = scaler.fit_transform(features)逻辑说明:get_dummies会把month拆成 12 列,drop_first=True避免哑变量共线性。StandardScaler把连续特征变成均值 0、方差 1,目的是让逻辑回归的梯度下降收敛更快,也让后续聚类距离计算不受量纲主导。
3.2 分类模型训练与评估:训练集、混淆矩阵与 ROC 曲线
课程作业里,逻辑回归是必跑的基础模型,随机森林是提升效果的代表。两个都跑,报告才能做对比。
from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score # 划分数据,固定随机种子保证可复现 X_train, X_test, y_train, y_test = train_test_split( X_scaled, target, test_size=0.3, random_state=42, stratify=target ) # 逻辑回归,加 L2 正则防止过拟合 lr = LogisticRegression(C=1.0, max_iter=1000, random_state=42) lr.fit(X_train, y_train) y_pred_lr = lr.predict(X_test) # 随机森林 rf = RandomForestClassifier(n_estimators=200, max_depth=8, random_state=42) rf.fit(X_train, y_train) y_pred_rf = rf.predict(X_test) # 评估 print("LR classification report:") print(classification_report(y_test, y_pred_lr)) print("RF classification report:") print(classification_report(y_test, y_pred_rf)) print("LR AUC:", roc_auc_score(y_test, lr.predict_proba(X_test)[:, 1])) print("RF AUC:", roc_auc_score(y_test, rf.predict_proba(X_test)[:, 1]))参数说明:random_state=42是所有随机操作的种子,保证不同次运行结果一致,是课程作业里最关键的参数;stratify=target让训练集和测试集的分类比例保持和原数据一致,处理银行这种类别不平衡数据时是必须的。max_depth=8限制了树的深度,防止随机森林在 45211 条数据上长成完全生长的树导致过拟合。
如果roc_auc_score报错,多半是predict_proba返回的形状不对,检查是否用了predict结果传给 AUC。AUC 计算必须用概率,不是类别。
3.3 分类结果如何写进实验报告:参数表与结论模板
报告里不要贴大段代码,只要三样东西:实验参数表、评估指标表、一段结果分析。
| 模型 | 参数设置 | Accuracy | Precision(违约) | Recall(违约) | F1 | AUC |
|---|---|---|---|---|---|---|
| 逻辑回归 | C=1.0, L2 | 0.89 | 0.56 | 0.38 | 0.45 | 0.85 |
| 随机森林 | n_estimators=200, max_depth=8 | 0.91 | 0.63 | 0.44 | 0.52 | 0.88 |
注意:这里指标是示例,真实数值随随机种子和预处理变化。报告里要写清楚“为什么随机森林优于逻辑回归”——因为银行数据里特征关系非线性,逻辑回归只能捕捉线性边界,而树模型能自动组合特征。
4. 聚类与分类的联动:用 KMeans 和层次聚类挖掘客户群
4.1 聚类特征选择:为什么不直接用全量特征
聚类的输入通常不用全量特征。银行数据里duration和balance的量纲差异极大,而month这种季节变量对客户分群干扰很强。我的做法是:先做一次相关性分析,丢掉强相关的冗余特征,再对剩余特征做标准化。
# 保留一组有业务含义的特征 cluster_features = ["age", "balance", "duration", "campaign", "pdays", "previous"] X_cluster = df_clean[cluster_features].copy() X_cluster = scaler.fit_transform(X_cluster) # 复用 StandardScaler业务含义说明:age代表客户年龄,balance是账户余额,duration是上次通话时长,campaign是本次营销联系次数,pdays是距上次被联系的间隔天数。这六个维度覆盖了“客户是谁、财务状况、互动强度”三方面,分出来的群更容易解释。
4.2 肘部法则确定 K 值:不能拍脑袋写 K=3
聚类最常见的翻车方式是直接写KMeans(n_clusters=3)而不说明为什么是 3。课程作业里必须给出 K 值选择依据,肘部法则是最容易复现的方法。
import matplotlib.pyplot as plt from sklearn.cluster import KMeans # 计算 K 从 2 到 10 的簇内误差平方和(SSE) sse = [] for k in range(2, 11): km = KMeans(n_clusters=k, random_state=42, n_init=10, init='k-means++') km.fit(X_cluster) sse.append(km.inertia_) # 画肘部图 plt.plot(range(2, 11), sse, marker='o') plt.xlabel("Number of clusters") plt.ylabel("SSE") plt.title("Elbow Method") plt.savefig("elbow.png", dpi=150)逻辑说明:km.inertia_就是每个样本到其所属簇中心的距离平方和。K 增大时 SSE 不断下降,但下降速度从某个点开始明显放缓,这个拐点就是“肘”。参数说明:n_init=10表示用 10 个不同的初始质心跑,取最好结果,这是 sklearn 1.2 之后的推荐写法,旧代码里常见的n_jobs参数在新版已移除。
4.3 层次聚类与轮廓系数:两个聚类结果怎么对比
KMeans 跑完后,还要再跑一个层次聚类来做对比,报告才有深度。层次聚类不需要指定 K,直接看树状图。
from scipy.cluster.hierarchy import linkage, dendrogram from sklearn.metrics import silhouette_score # 用 ward 法做层次聚类 Z = linkage(X_cluster[:2000], method='ward') # 数据太大,截取前 2000 条画树状图 plt.figure(figsize=(12, 6)) dendrogram(Z, truncate_mode='level', p=3) plt.savefig("dendrogram.png", dpi=150) # 用 KMeans 的计算轮廓系数 for k in [3, 4, 5]: km_k = KMeans(n_clusters=k, random_state=42, n_init=10) labels_k = km_k.fit_predict(X_cluster) print(k, silhouette_score(X_cluster, labels_k))逻辑说明:linkage(X_cluster[:2000], method='ward')用 ward 连接法,它使合并时离差平方和增量最小,比欧氏距离平均连接更常用。轮廓系数取值在 -1 到 1 之间,越接近 1 表示簇内紧密、簇间分离好,这是聚类质量评估的核心指标。实验报告里要把不同 K 的轮廓系数列成表格,然后选择分数最高的 K。
如果轮廓系数普遍偏低,比如 0.2 以下,说明特征选择有问题。此时回去检查balance是否极度偏态,可以对其取对数再聚类。
5. 课程作业避坑指南:源码跑不通和实验报告被扣分的典型问题
5.1 数据读入失败:分号分隔文本被当成单列
现象:pd.read_csv("bank-full.csv")读出来的 DataFrame 只有一列,df.head()看到一行长得像乱码的长字符串。
原因:这份数据集的分隔符是分号;,不是默认的逗号。直接读会把整行内容当成一个整体。
解决:读取时加sep=";";同时注意文件编码,如果出现UnicodeDecodeError,把参数改成encoding="latin1"或encoding="ISO-8859-1"。
5.2 标签泄漏:把目标字段“y”参与聚类
现象:聚类结果的轮廓系数异常高,比如接近 0.9,客户分群和实际订阅率高度吻合。
原因:特征矩阵里混入了目标列y,模型在做无监督聚类时,等于把答案当特征用,这是数据挖掘里最典型的标签泄漏。
解决:聚类特征中只保留前向型字段,凡是与目标在时间上同时发生或因果上由目标决定的字段,一律剔除。duration这个字段其实也有争议——通话时长往往是在客户明确愿意继续沟通后才变长,很多严格实验里会把duration从建模特征中拿掉。
5.3 随机种子未固定:同一份代码两次跑出不同结果
现象:两次运行分类报告的 Accuracy 完全不同,随机森林的特征重要性图也不一样。
原因:模型训练、数据划分、KMeans 初始化都有随机性,没有设random_state。
解决:在所有有随机过程的函数里都传入random_state=42。注意train_test_split、LogisticRegression、RandomForestClassifier、KMeans都要分别设置。
5.4 matplotlib 图表中文乱码
现象:图上标题和坐标轴出现方块,或者全部是???。
原因:matplotlib 默认字体不支持中文。
解决:在绘图前设置中文字体,Windows 用SimHei,macOS 用PingFang SC。
import matplotlib matplotlib.rcParams['font.sans-serif'] = ['SimHei'] matplotlib.rcParams['axes.unicode_minus'] = False5.5 报告里贴代码却不解释参数:被老师追问“为什么是这个值”
现象:答辩或作业评语里老师问“C=1.0 怎么定的?max_depth=8 为什么不是 20?”
原因:实验报告没有对参数做敏感性分析,只把代码抄了一遍。
解决:在报告里加一个“参数对结果影响”的小表格,例如固定其他条件,只改C从 0.01、0.1、1、10 跑一遍,记录 Accuracy 和 AUC。这一项内容在评分权重里不低于模型本身。
6. 把源码整理成能拿高分的作业包:目录结构、注释与最终自查
6.1 作业目录怎么组织
课程作业一般是源码加报告一起提交,目录结构混乱是很掉价的事。我习惯这样组织:
bank_homework/ ├── data/ │ └── bank-full.csv ├── src/ │ ├── data_preprocess.py │ ├── classification.py │ ├── clustering.py │ └── plot_results.py ├── report/ │ ├── 实验报告.docx │ └── 实验报告.pdf ├── output/ │ ├── confusion_matrix.png │ ├── elbow.png │ └── cluster_result.csv └── README.mdREADME.md里写清楚运行顺序:先跑data_preprocess.py,再跑classification.py和clustering.py。很多老师不是只看报告,他会真想跑一下你的代码,如果你的代码要手动改路径,印象分直接掉档。
6.2 注释和打印输出的打磨
代码注释不要逐行翻译,要写“为什么”和“业务含义”。另外建议在分类和聚类脚本末尾,把关键结果直接打印成整齐的文本:
print("========== 分类结果 ==========") print(f"逻辑回归 AUC: {roc_auc_score(y_test, lr.predict_proba(X_test)[:, 1]):.4f}") print(f"随机森林 AUC: {roc_auc_score(y_test, rf.predict_proba(X_test)[:, 1]):.4f}") print("========== 聚类轮廓系数 ==========") for k in best_k_range: print(f"K={k}: silhouette={silhouette_scores[k]:.4f}")打印内容控制在十行以内,不要每个 epoch 都打印。课程作业的代码是给人看的,不是给机器跑的。
6.3 提交前的最后自查清单
交作业前十分钟,按这五条过一遍:
python src/data_preprocess.py能从干净的 conda 环境从头跑通,不用任何手动干预。- 实验报告里每个指标都有对应的代码输出,不要手填数字。
- 所有图表的清晰度不低于 150 DPI,截图放进 Word 里不变形。
- README 里的运行说明和实际脚本文件名、顺序完全一致。
- 压缩包命名符合学号+姓名+课程作业要求,不要叫“新建文件夹.zip”。
这个习惯我一直保留到现在。工作以后给团队交付代码,也是同样的逻辑:别人能复现,才有信任度。课程作业虽然只是小项目,但把它当正式项目来收尾,你会在拿到高分的同时真正学会“交付”的意义。希望帮到你。
本文还有配套的精品资源,点击获取