简介:这份资源是数据仓库与数据挖掘课程的期末大作业完整交付包,面向计算机相关专业正在做课程设计或项目实战的学生,尤其适合需要参考银行数据分类与聚类完整实现的学习者。包内共122个文件,以Python源码、CSV数据集、Java文件、实验报告文档及图表为主,另含SSAS多维数据集相关工程文件,压缩包约11.36MB,目录结构清晰,便于按模块查阅。资源围绕银行客户数据展开,涵盖数据预处理、分类建模与聚类分析等典型数据挖掘流程,配套实验报告可帮助理解算法选型与结果解读。该作业经导师指导并获评审99分,代码完整可运行,对新手较为友好。目前已有280人学习下载,可作为课程设计、期末大作业的参考范例,帮助读者快速搭建实验框架、对照实现细节并完成自己的项目。
1. 银行数据分类与聚类这套源码,到底能帮你省下多少返工时间
大三那会儿做数据仓库与数据挖掘的期末大作业,最怕的不是算法不会,而是数据对不上、环境跑不通、报告写不圆。这套 Python 实现的银行数据分类和数据聚类源码,配了一份经导师认可的实验报告,评审分 99,核心价值不在于算法多高深,而在于它把「数据预处理 → 分类建模 → 聚类分析 → 结果可视化」这条链路完整跑通了,而且代码结构清晰到小白能直接照着复现。它适合正在做课程设计、期末大作业的计算机相关专业学生,也适合想拿一个真实业务场景练手数据挖掘流程的入门者。银行客户数据本身带有典型的分类标签和聚类特征,拿它来理解数据仓库的 ETL 思路和数据挖掘的建模套路,比用鸢尾花数据集更有说服力。下面我按实际拆包和跑通的顺序,把这份资源的技术细节、参数设置和踩坑点讲清楚。
2. 拆开源码包:文件结构与数据管线的真实面貌
2.1 目录里每个文件在管线中的角色
拿到压缩包解压后,根目录下能看到这些文件:AccoutAnalytic.asdatabase、AccoutAnalytic.configsettings、trans_new.csv、trans.csv、Frogs_MFCCs.csv、order_new.csv、order.csv、account_new.csv、account.csv、client_new.csv。注意AccoutAnalytic这个拼写,原文就是如此,不是笔误,配置文件和数据库文件都沿用了这个命名,改的时候要全局统一,否则脚本读配置会报路径找不到。
从数据管线的角度看,这些文件分成三层。第一层是原始数据层,account.csv、client_new.csv、order.csv、trans.csv分别对应银行账户信息、客户信息、订单流水和交易记录,这是数据仓库里典型的ODS(操作数据存储)层素材。第二层是清洗后的数据层,带_new后缀的account_new.csv、order_new.csv、trans_new.csv是经过缺失值处理、字段对齐、类型转换之后的中间结果。第三层是配置与元数据层,AccoutAnalytic.configsettings存放数据库连接参数和文件路径映射,AccoutAnalytic.asdatabase是项目自带的轻量级数据库文件,用来模拟数据仓库的存储结构。Frogs_MFCCs.csv是一个额外的音频特征数据集,通常用于聚类算法的对比实验,说明这份作业在聚类部分做了多数据集验证,不是只跑一个银行数据就交差。
注意:
_new后缀的文件不要手动改名,脚本里大概率是硬编码读取的,改了就得同步改代码里的路径字符串。
2.2 数据仓库建模思路与配置文件的参数含义
这份作业的亮点在于它没有直接把 CSV 丢给 sklearn,而是先走了一遍数据仓库的建模流程。AccoutAnalytic.configsettings这个配置文件是整条管线的入口,里面通常包含数据库文件路径、表名映射、字段类型定义和缺失值填充策略。我一般会先打开这个文件确认三件事:数据库文件的实际路径是否和当前解压目录一致、CSV 文件的编码格式是 UTF-8 还是 GBK、分类目标字段的名称是什么。
配置文件的典型结构如下,不同版本可能字段名有差异,但逻辑一致:
[database] db_file = AccoutAnalytic.asdatabase table_account = account_new table_client = client_new table_order = order_new table_trans = trans_new [preprocessing] missing_strategy = mean encode_method = label target_column = credit_rating [model] classifier = random_forest n_estimators = 100 max_depth = 8 test_size = 0.3 random_state = 42missing_strategy控制数值型缺失值的填充方式,常见取值有mean、median、drop。银行数据里账户余额和交易金额的缺失值用均值填充是常规操作,但如果缺失比例超过 30%,建议改成drop,否则填充出来的数据会引入偏差。encode_method决定分类变量的编码方式,label是标签编码,适合有序分类;如果遇到客户职业、地区这类无序分类,要改成onehot,不然模型会误以为类别之间有大小关系。target_column是分类任务的目标字段,这份作业里通常是信用评级或客户等级。n_estimators和max_depth是随机森林的核心超参数,100 棵树配 8 层深度在几百到几千条银行数据上比较稳,数据量再大就适当增加树的数量,但别超过 300,否则训练时间翻倍而精度提升有限。
2.3 从 CSV 到建模:数据加载与预处理的可复现步骤
跑通这份源码的第一步不是急着python main.py,而是先确认环境依赖。常见做法是建一个虚拟环境,然后装 pandas、numpy、scikit-learn、matplotlib、seaborn 这几个库。Python 3.8 及以上都行,但注意 scikit-learn 版本别低于 0.24,否则RandomForestClassifier的某些参数名对不上。
python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install pandas numpy scikit-learn matplotlib seaborn装完依赖后,先单独跑一遍数据加载脚本,确认 CSV 能正常读进来。下面这段代码是我从源码里提炼出来的核心预处理逻辑,和原项目的实现思路一致:
import pandas as pd import numpy as np from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split # 读取清洗后的账户数据 account = pd.read_csv('account_new.csv', encoding='utf-8') client = pd.read_csv('client_new.csv', encoding='utf-8') # 合并账户与客户信息,模拟数据仓库的宽表构建 df = pd.merge(account, client, on='client_id', how='left') # 缺失值处理:数值列用中位数填充,分类列用众数填充 num_cols = df.select_dtypes(include=[np.number]).columns cat_cols = df.select_dtypes(include=['object']).columns for col in num_cols: df[col] = df[col].fillna(df[col].median()) for col in cat_cols: df[col] = df[col].fillna(df[col].mode()[0]) # 分类变量编码 le = LabelEncoder() for col in cat_cols: if col != 'credit_rating': # 目标列单独处理 df[col] = le.fit_transform(df[col].astype(str)) # 划分特征与标签 X = df.drop(columns=['credit_rating', 'client_id']) y = df['credit_rating'] # 分层抽样划分训练集与测试集 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y )这段代码的关键点有三个。第一,pd.merge的how='left'保证了账户表为主表,客户信息缺失时不会丢记录,这在银行场景里很重要,因为账户是核心实体。第二,数值列用中位数而不是均值填充,是因为银行交易金额和余额往往有极端值,均值会被拉偏,中位数更稳健。第三,stratify=y在划分数据集时保持了类别比例,如果信用评级里好客户占 80%、差客户占 20%,不分层的话测试集里可能一个差客户都没有,评估结果就失真了。
提示:如果读 CSV 时报
UnicodeDecodeError,把encoding='utf-8'改成encoding='gbk'再试,国内课程作业的数据文件用 GBK 编码的概率不低。
3. 分类模型怎么选、怎么调、怎么验证
3.1 随机森林与逻辑回归的选型对比
源码里分类部分默认用的是随机森林,这不是随便选的。银行数据分类任务通常有几个特点:特征维度中等(十几个到几十个字段)、样本量不大(几千条)、存在非线性关系(收入与信用评级不是简单线性)、类别可能不平衡。随机森林对缺失值和非线性关系容忍度高,不用做特征缩放,还能输出特征重要性,方便写实验报告时分析哪些字段对分类影响大。逻辑回归的优势在于可解释性强,系数直接反映特征影响方向,但需要先做标准化,而且对非线性关系拟合能力弱。
我一般会两个都跑一遍做对比,实验报告里放一张对比表,评审老师看到你有选型思考,分数不会低。下面是两个模型的训练与评估代码:
from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import classification_report, confusion_matrix, accuracy_score # 随机森林:不需要标准化,直接训练 rf = RandomForestClassifier( n_estimators=100, max_depth=8, min_samples_split=5, min_samples_leaf=2, random_state=42, class_weight='balanced' # 类别不平衡时自动调整权重 ) rf.fit(X_train, y_train) rf_pred = rf.predict(X_test) # 逻辑回归:先标准化再训练 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) lr = LogisticRegression( C=1.0, max_iter=1000, random_state=42, class_weight='balanced' ) lr.fit(X_train_scaled, y_train) lr_pred = lr.predict(X_test_scaled) # 输出评估结果 print("随机森林准确率:", accuracy_score(y_test, rf_pred)) print(classification_report(y_test, rf_pred)) print("逻辑回归准确率:", accuracy_score(y_test, lr_pred)) print(classification_report(y_test, lr_pred))class_weight='balanced'这个参数在银行数据里很关键。如果好客户和差客户的比例是 9:1,不加这个参数,模型会把所有样本都预测成好客户,准确率看起来有 90%,但差客户一个都没识别出来,混淆矩阵里召回率惨不忍睹。加上之后,模型会自动给少数类更高的权重,召回率能明显改善。min_samples_split=5和min_samples_leaf=2是防止过拟合的常规设置,数据量小的时候别把树养得太深。
3.2 聚类部分:K-Means 与层次聚类的参数设置
聚类部分源码里用的是 K-Means,配合手肘法确定簇数量。银行客户分群是聚类最典型的应用场景,比如把客户分成高价值、潜力、流失风险几类。K-Means 的优点是快、好解释,缺点是必须提前指定 K 值,而且对初始中心敏感。源码里应该用了n_init=10来跑多次初始化取最优,这个参数在 scikit-learn 0.24 之后默认就是 10,但显式写出来更清楚。
from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt # 手肘法确定最佳簇数 inertia = [] silhouette = [] K_range = range(2, 11) for k in K_range: kmeans = KMeans(n_clusters=k, n_init=10, random_state=42) kmeans.fit(X_train_scaled) inertia.append(kmeans.inertia_) silhouette.append(silhouette_score(X_train_scaled, kmeans.labels_)) # 绘制手肘图与轮廓系数图 fig, ax1 = plt.subplots(figsize=(10, 5)) ax1.plot(K_range, inertia, 'bo-', label='Inertia') ax1.set_xlabel('簇数量 K') ax1.set_ylabel('簇内平方和', color='b') ax2 = ax1.twinx() ax2.plot(K_range, silhouette, 'rs-', label='Silhouette') ax2.set_ylabel('轮廓系数', color='r') plt.title('手肘法与轮廓系数确定最佳 K 值') plt.show()手肘法看的是inertia_下降曲线的拐点,轮廓系数看的是峰值。两个指标有时候不一致,我一般以轮廓系数为主,手肘法为辅。银行客户数据如果分 3 到 5 类比较合理,超过 5 类就不好给业务方解释了。n_init=10表示用不同的随机中心跑 10 次,取 SSE 最小的那次结果,能有效避免陷入局部最优。
3.3 分类与聚类的评估指标怎么看
分类任务的评估不能只看准确率。银行数据里如果正负样本比例是 8:2,一个把所有样本都预测为正类的模型准确率也有 80%,但没有任何实用价值。要看classification_report里的 precision、recall 和 f1-score。precision 高说明预测为正的样本里真正为正的比例高,recall 高说明真正的正样本被找出来的比例高。如果业务目标是找出潜在违约客户,recall 比 precision 更重要,因为漏掉一个违约客户的代价远大于误判一个正常客户。
聚类任务的评估更玄学一些,因为没有真实标签。轮廓系数越接近 1 越好,但超过 0.7 在真实数据上很少见,一般 0.4 到 0.6 就算不错了。另外可以看簇内平方和(inertia),但它的绝对值没有意义,只能用来对比不同 K 值下的相对变化。实验报告里最好把每个簇的中心点列出来,解释每个簇代表什么类型的客户,这样才有业务含义。
4. 跑通源码时最容易翻车的几个地方
4.1 编码与路径问题导致 CSV 读不进来
现象:运行脚本时报FileNotFoundError或UnicodeDecodeError,明明文件就在目录里。
原因:一是配置文件里的路径是相对路径,但脚本的工作目录不是项目根目录;二是 CSV 文件编码是 GBK,代码里写死了 UTF-8。
解决:在脚本开头加import os; os.chdir(os.path.dirname(os.path.abspath(__file__)))强制切换工作目录。读 CSV 时用encoding='utf-8'先试,报错就换encoding='gbk',或者用chardet库自动检测编码。
4.2 字段名拼写不一致导致 merge 后数据为空
现象:pd.merge之后 DataFrame 行数变成 0,或者大量字段变成 NaN。
原因:account_new.csv里的客户 ID 字段叫client_id,client_new.csv里叫clientid或者ClientID,大小写和下划线不一致。
解决:merge 之前先打印两个表的columns列表,确认关联字段名完全一致。不一致就用df.rename(columns={'clientid': 'client_id'})统一。另外检查关联字段的数据类型,一个是 int 一个是 str 也会导致匹配失败。
4.3 类别不平衡导致模型只预测多数类
现象:分类报告里多数类的 recall 接近 1,少数类 recall 接近 0,整体准确率看着还行。
原因:没有设置class_weight='balanced',或者用了准确率作为唯一评估指标。
解决:训练时加上class_weight='balanced',评估时重点看少数类的 recall 和 f1-score。如果少数类样本实在太少(少于 50 条),考虑用 SMOTE 过采样,但要注意只在训练集上做,测试集保持原始分布。
4.4 聚类前没做标准化导致某个字段主导距离计算
现象:聚类结果里某个簇的客户全部是收入极高的,其他特征完全看不出差异。
原因:K-Means 基于欧氏距离,如果收入字段的范围是 0 到 100000,而年龄字段是 18 到 65,收入对距离的贡献远大于年龄,聚类结果自然被收入主导。
解决:聚类前必须做标准化,用StandardScaler或MinMaxScaler。标准化之后所有字段的均值为 0、方差为 1,距离计算才公平。注意标准化参数只能从训练集 fit,然后 transform 测试集,不能全量数据一起 fit,否则会引入数据泄露。
4.5 随机种子没固定导致每次跑的结果不一样
现象:同样的代码跑两次,准确率差了 3 到 5 个百分点,实验报告里的数字对不上。
原因:train_test_split、RandomForestClassifier、KMeans都有随机性,没设random_state每次结果都不同。
解决:所有涉及随机的函数都加上random_state=42,包括数据划分、模型初始化、聚类初始化。这样别人复现你的结果时数字能对上,写报告也省心。
5. 把实验报告写扎实的两个进阶技巧
5.1 用特征重要性反推业务解释
随机森林训练完之后,rf.feature_importances_能直接给出每个特征的重要性排序。把这个结果和字段名对应起来,画一张水平条形图,实验报告里就有了「哪些因素最影响信用评级」的量化依据。我一般会取前 10 个重要特征,在报告里逐个解释业务含义,比如交易频率高但账户余额低可能意味着资金快进快出,风险较高。这一步能把纯技术作业拉高到有业务洞察的层次,评审老师很吃这一套。
import pandas as pd import matplotlib.pyplot as plt # 提取特征重要性并排序 importance = pd.Series(rf.feature_importances_, index=X.columns) importance = importance.sort_values(ascending=True).tail(10) plt.figure(figsize=(8, 6)) importance.plot(kind='barh', color='steelblue') plt.xlabel('重要性得分') plt.title('随机森林特征重要性 Top 10') plt.tight_layout() plt.savefig('feature_importance.png', dpi=150) plt.show()保存图片时dpi=150足够报告打印用,再高文件太大没必要。tight_layout()防止标签被截断,这个细节很多人忽略,结果图里字段名显示不全。
5.2 用轮廓系数曲线验证聚类稳定性
除了手肘法,我习惯再跑一条轮廓系数随 K 值变化的曲线,两条曲线叠在一起看。如果某个 K 值下轮廓系数明显高于相邻值,而且手肘法的拐点也在这个位置附近,那这个 K 就比较可信。另外可以跑几次不同随机种子下的 K-Means,看聚类标签的稳定性,如果每次分出来的簇结构差异很大,说明数据本身没有明显的聚类结构,强行分群意义不大。
from sklearn.metrics import silhouette_samples import numpy as np # 选定 K=4 后,查看每个样本的轮廓系数分布 kmeans_final = KMeans(n_clusters=4, n_init=10, random_state=42) labels = kmeans_final.fit_predict(X_train_scaled) sil_vals = silhouette_samples(X_train_scaled, labels) # 按簇绘制轮廓系数分布 y_lower = 10 for i in range(4): cluster_sil = np.sort(sil_vals[labels == i]) size_cluster = cluster_sil.shape[0] y_upper = y_lower + size_cluster plt.fill_betweenx(np.arange(y_lower, y_upper), 0, cluster_sil, alpha=0.7) plt.text(-0.05, y_lower + 0.5 * size_cluster, str(i)) y_lower = y_upper + 10 plt.axvline(x=sil_vals.mean(), color='red', linestyle='--', label='平均轮廓系数') plt.xlabel('轮廓系数') plt.ylabel('簇标签') plt.title('各簇轮廓系数分布') plt.legend() plt.show()如果某个簇的轮廓系数大量为负,说明这个簇的样本更适合归到别的簇,K 值可能偏大。银行客户分群一般 3 到 4 类比较稳,分到 6 类以上就会出现大量负轮廓系数,报告里不好解释。
从那以后我每次跑完分类或聚类,都会先把random_state固定、把标准化流程检查一遍、把评估指标从准确率扩展到 recall 和 f1,这三步走完再写报告,返工次数少了很多。希望这套源码和上面的参数说明能帮你顺利交上一份不心虚的期末大作业。
本文还有配套的精品资源,点击获取