ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

银行数据分类与聚类实践:Python课程作业源码详解

银行数据分类与聚类实践:Python课程作业源码详解 简介这套课程作业资料来自数据仓库与数据挖掘课程围绕银行数据的分类与聚类任务提供了完整的Python实现方案。项目源于大三期末大作业经导师指导并获得高分评价代码完整、可直接运行既适合计算机专业学生作为期末大作业或课程设计的参考也适合想要动手练习数据挖掘项目的学习者仿照实践。压缩包内共122个文件体积约11.36MB核心由Python源码、银行数据集CSV文件和实验报告组成同时收有Java代码以及SSAS数据仓库工程文件覆盖从数据预处理、模型训练与评估到结果分析、多维建模的完整流程。Python脚本负责分类与聚类算法CSV文件存储实验数据工程文件展示数据仓库设计文档则对实验步骤和结论作了详细梳理。已有280人学习下载。通过这份资料使用者可以获得一整套可直接复用与扩展的项目资料包括算法代码、原始数据、图文报告和工程配置便于快速迁移至自己的作业或练习中。1. 银行数据分类与聚类一份能直接跑通的课程作业源码数据仓库与数据挖掘课程最让人头疼的往往不是算法推导而是把课堂上的决策树、KMeans真正落到一份像样的数据上还要写出能解释每一步的实验报告。这份 Python 实现银行数据分类和数据聚类的源码包是我大三期末大作业的完整版本经导师指导修改后定稿评审成绩 99 分。它自带清洗后的银行客户、账户、交易、订单四类 CSV 数据以及一个 Access 格式的数据仓库文件 AccoutAnalytic.asdatabase分类和聚类脚本都是完整可运行的。适合正在做数据仓库与数据挖掘课程设计、期末大作业的同学或者想拿真实业务数据练手的数据分析新人。你不需要自己造数据也不用为了跑通算法去爬接口解压后按实验报告里的顺序执行就能看到结果。这篇文章我会把源码包的目录结构、分类和聚类的具体实现思路、参数设置以及我踩过的坑全部拆开讲。2. 把银行数据先理清楚从原始 CSV 到可用特征集2.1 源码包里到底有哪些文件它们各自负责什么拿到这个资源包第一步不是急着跑模型而是先看懂文件之间的依赖关系。压缩包里的核心文件分三类数据仓库相关、数据文件、代码和报告。数据仓库相关的是 AccoutAnalytic.asdatabase 和 AccoutAnalytic.configsettings前者是一个 Access 数据库快照后者是数据仓库的配置设置文件用于展示星型模型和维度建模的部分。数据文件则分了两套一套是原始数据 account.csv、client.csv、order.csv、trans.csv另一套是清洗后的 account_new.csv、client_new.csv、order_new.csv、trans_new.csv另外还有一个 Frogs_MFCCs.csv这个是在聚类部分用来做跨数据集验证的附加数据。你可以这样理解这两套数据的区别原始 CSV 是脏的里面有空值、重复记录、格式不一致的字段而带 _new 后缀的文件已经是清洗完毕的版本可以直接喂给分类和聚类算法。实际作业中我建议你在实验报告里把清洗前后的对比写清楚比如记录数变化、缺失值填充方式、异常值处理策略这一块是评分老师最看重的数据处理能力。2.2 我一般怎么处理银行账户和交易数据字段含义与预处理逻辑以 account.csv 和 trans.csv 为例account 表主要记录账户的 ID、客户 ID、开户日期、账户类型等trans 表记录每笔交易的金额、类型、时间戳和对手方信息。在跑分类和聚类之前需要做三件标准化操作去重、补缺失值、构造衍生特征。import pandas as pd from sklearn.preprocessing import LabelEncoder # 读取原始数据 account pd.read_csv(account.csv) trans pd.read_csv(trans.csv) # 1. 去除完全重复的记录 account account.drop_duplicates() trans trans.drop_duplicates() # 2. 缺失值处理数值列用中位数填充类别列用众数填充 for col in trans.select_dtypes(include[float64, int64]).columns: trans[col] trans[col].fillna(trans[col].median()) for col in trans.select_dtypes(include[object]).columns: trans[col] trans[col].fillna(trans[col].mode()[0]) # 3. 构造客户维度聚合特征每个账户的交易频次、金额均值、最大单笔金额 trans_grouped trans.groupby(account_id).agg( trans_count(amount, count), trans_mean(amount, mean), trans_max(amount, max) ).reset_index() # 4. 把账户基本信息与聚合特征合并 account_feature account.merge(trans_grouped, onaccount_id, howleft) print(account_feature.head())这段代码的处理逻辑是把交易流水按账户进行聚合生成三个新特征交易笔数、平均金额、最大单笔金额。这几个特征对判断一个账户是否活跃、是否存在大额异常交易非常有效。参数上需要注意 fillna 时选择 median 而不是 mean因为银行交易金额的分布通常右偏平均值容易被超大额交易拉高中位数更能代表一般水平。2.3 为什么同时保留原始数据和清洗后数据交付作业的聪明做法很多同学的毛病是只给一份清洗后的数据老师想验证你的清洗过程却找不到原始数据。这份作业里既保留 account.csv 又保留 account_new.csv就是让评审能对照着看。在实验报告里我建议列一个清洗前后对比表写清楚每一张表删掉了多少行、补了多少缺失值、有哪些字段做了类型转换。比如 order.csv 里的订单金额字段可能是字符串读进来后必须转成 float否则聚类的时候直接报错。另外要提醒一点AccountAnalytic.asdatabase 那个 Access 文件在 Linux 环境下可能打不开但 Python 可以通过 pyodbc 或读取 CSV 的方式绕过它。课程作业里老师检查的重点往往是代码能否在标准 Python 环境跑通而不是 Access 文件本身。所以我的做法是在实验报告里用文字说明数据仓库的星型模型设计然后把实际数据处理全部用 CSV 完成这样可复现性最高。3. 银行数据分类用决策树和逻辑回归识别高风险账户3.1 分类任务的目标设定与标签构造分类部分要解决的核心问题是给定一个账户的历史交易和基本信息判断它是否有坏账风险。数据里没有现成的标签列需要自己构造。常见做法是通过 trans 表里的交易状态字段比如是否存在逾期、退款、纠纷等记录把这些记录映射为 0 和 1。如果交易表里没有状态字段也可以用订单表 order.csv 里是否存在未完成订单来打标签。源码包里已经有一个标签构造的模块在代码注释里标明了label的处理方式。我在这里给你一个通用的构造脚本import numpy as np from sklearn.model_selection import train_test_split # 假设 order 表里有一个 status 字段completed / pending / failed order pd.read_csv(order_new.csv) order[bad_flag] np.where(order[status].isin([failed, pending]), 1, 0) # 按账户聚合统计坏订单数量 bad_order order.groupby(account_id)[bad_flag].sum().reset_index() bad_order.rename(columns{bad_flag: bad_count}, inplaceTrue) # 只要存在坏订单就标记为高风险账户 bad_order[label] np.where(bad_order[bad_count] 0, 1, 0) # 与账户特征合并 feature account_feature.merge(bad_order[[account_id, label]], onaccount_id, howleft) feature[label] feature[label].fillna(0) # 划分训练集和测试集注意分层采样保证标签比例一致 X feature.drop([account_id, label], axis1) y feature[label] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) print(训练集样本数:, X_train.shape[0], 测试集样本数:, X_test.shape[0])这里的关键点是 train_test_split 里带了 stratifyy 参数如果不加这个参数当正样本比例很小时随机划分可能会让测试集里一个高风险账户都没有模型评估就会失真。random_state42 是为了让结果可复现交作业时老师重新运行得到的准确率和你的报告一致这样才能自证代码真实有效。3.2 决策树参数怎么调max_depth 和 min_samples_leaf 的决定性作用分类模型我选了两套方案逻辑回归作为基线决策树作为主模型。逻辑回归的好处是可解释性强决策树则能直观输出规则路径。源码里的决策树部分用了 grid search 微调参数核心参数是 max_depth 和 min_samples_leaf。from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import GridSearchCV params { max_depth: [3, 5, 7, 9], min_samples_leaf: [5, 10, 20], criterion: [gini, entropy] } dt_model DecisionTreeClassifier(random_state42) grid_search GridSearchCV( dt_model, params, cv5, scoringf1, n_jobs-1 ) grid_search.fit(X_train, y_train) print(最优参数:, grid_search.best_params_) print(交叉验证最优 F1:, grid_search.best_score_) best_dt grid_search.best_estimator_为什么用 f1 而不是 accuracy因为高风险账户在现实中是少数类如果准确率很高但把正样本全判成负样本模型没有实际意义。F1 能同时惩罚假阳性和假阴性。决策树的 max_depth 控制在 3 到 7 之间比较合理太深容易过拟合尤其是样本量只有几千条时。min_samples_leaf 设成 10 或 20 能保证每个叶子节点至少有足够的样本支撑避免学到极端个例。跑完 GridSearchCV 后我建议你把最优决策树用 export_text 打印出来贴在实验报告里。老师看到你展示的树规则比如交易金额均值 5000 且交易笔数 10 预测为低风险会觉得你的分析扎实。3.3 分类结果评估混淆矩阵和 ROC 曲线缺一不可实验报告里光写准确率是不行的至少要有混淆矩阵、Precision、Recall、F1以及 ROC 曲线下的面积 AUC。源码里已经封装了一个 evaluate_model 函数from sklearn.metrics import confusion_matrix, classification_report, roc_auc_score def evaluate_model(model, X_test, y_test): y_pred model.predict(X_test) y_proba model.predict_proba(X_test)[:, 1] print(混淆矩阵:\n, confusion_matrix(y_test, y_pred)) print(\n分类报告:\n, classification_report(y_test, y_pred)) print(AUC:, roc_auc_score(y_test, y_proba)) evaluate_model(best_dt, X_test, y_test)注意这里计算 AUC 用的是 predict_proba 返回的第二列而不是 predict 的硬分类结果。AUC 的意义在于它不受分类阈值影响能从概率角度衡量模型的好坏。如果只打印 predict 结果AUC 会变成 0 到 1 之间的奇怪值而且没办法画 ROC 曲线。我在作业里还会把 ROC 曲线保存成图片放在实验报告的附录中。4. 银行数据聚类KMeans 与轮廓系数的实战组合4.1 聚类要解决什么问题客户分群比分类更考验特征设计分类有标签聚类没有标签。聚类部分的目标是把银行客户根据消费习惯和资产规模分成若干群体每个群体有不同的行为特征。这份作业里聚类用的数据是 account_new.csv 和 trans_new.csv 合并后的特征和分类不同的是聚类前通常做标准化否则金额特征比如万级会完全压过交易笔数特征比如个位数。源码里用的是 StandardScaler。4.2 KMeans 聚类肘部法则确定 K 值KMeans 需要预先指定聚类数 K。确定 K 的惯用方法是肘部法则计算不同 K 值下的 SSE误差平方和或惯性值SSE 下降趋势变缓的位置就是合适的 K。from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler # 读取清洗后的账户数据并选取特征列 cluster_data pd.read_csv(account_new.csv) feature_cols [balance, trans_count, trans_mean, trans_max] X_cluster cluster_data[feature_cols].dropna() # 标准化 scaler StandardScaler() X_scaled scaler.fit_transform(X_cluster) # 计算不同 K 值的 SSE sse [] K_range range(2, 11) for k in K_range: kmeans KMeans(n_clustersk, random_state42, n_init10) kmeans.fit(X_scaled) sse.append(kmeans.inertia_) # 打印 SSE 变化 for k, val in zip(K_range, sse): print(fK{k}, SSE{val:.2f})这段代码里有一个容易忽略的参数 n_init。sklearn 较新版本的 KMeans 默认 n_init10但在旧版本里可能是 1。n_init 表示用不同初始质心运行算法的次数最后取最优结果如果不设置可能陷入局部最优解。还有一个参数是 max_iter默认 300如果数据量大可以调到 500但在这个作业场景下 300 足够。肘部法则选出的 K 值往往不是一个精确单点而是一个范围。以这份作业的数据量来看K 取 3 到 5 之间比较合理。K 太小分不出群体差异太大每个群体的人太少没有业务含义。源码运行后取的是 K4对应的客户群大致可以解释为高余额低频交易、低余额高频交易、中等余额理财活跃、大额交易频繁。4.3 用轮廓系数验证聚类效果并输出画像肘部法则选 K 有些主观轮廓系数可以量化评价聚类质量取值范围在 -1 到 1 之间越接近 1 说明样本离自己簇中心越近、离其他簇越远。from sklearn.metrics import silhouette_score # 在 K 范围内计算轮廓系数辅助确定最优 K silhouette_vals [] for k in K_range: kmeans KMeans(n_clustersk, random_state42, n_init10) labels kmeans.fit_predict(X_scaled) score silhouette_score(X_scaled, labels) silhouette_vals.append(score) print(fK{k}, silhouette_score{score:.4f}) best_k K_range[silhouette_vals.index(max(silhouette_vals))] print(轮廓系数最大的 K 值:, best_k)运行后你会发现轮廓系数最大的 K 和肘部法则看出的 K 可能不一样这在真实数据里很正常。我的处理方式是两者都展示在报告里并解释最终选 K 的业务理由。聚类部分最怕的是直接调用 KMeans 不解释 K 怎么来的评阅老师一定会问你为什么是 4 而不是 3 或 5。聚类完成后还需要做群体画像也就是统计每个簇的特征均值。比如计算每个簇的平均余额、平均交易笔数、平均每笔金额然后横向对比。这一步不是算法但决定实验报告的档次。源码里有一个 cluster_profile 函数输出的结果大概是这样cluster_data[cluster] best_kmeans.labels_ profile cluster_data.groupby(cluster)[feature_cols].mean() print(profile)如果第一个簇的 balance 是 80000trans_count 是 5trans_mean 是 1200第二个簇的 balance 是 15000trans_count 是 40你就可以给每个簇起业务标签比如高净值低频交易型和普通活跃消费型。聚类本身没有业务含义业务含义是靠你解读出来的这一步占分值很高。5. 避坑指南这份作业里最常见的五个翻车点5.1 现象KMeans 报错 could not convert string to float原因很简单聚类特征列里有字符串。银行交易数据里经常会混入逗号分隔的金额字符串比如 1,234.56pandas 读进来是 object 类型KMeans 无法处理。解决方法是先把数据清洗干净用 astype(str).str.replace(,, ) 转成浮点。我写了一个通用转换函数def safe_float_convert(series): series series.astype(str).str.replace(,, ).str.replace($, ) return pd.to_numeric(series, errorscoerce)转换后一定再检查一下是否有 NaN因为格式乱七八糟的字符串转成 NaN 后会让 KMeans 直接崩。用 fillna 处理好再进模型。5.2 现象决策树准确率 99%但 AUC 只有 0.5这说明模型把大样本的多数类全部预测对了少数类一个没预测出来。在正负样本比例失衡的情况下准确率没有参考价值。我踩过这个坑后来强制用 stratify 分层采样并在评估时以 F1 和 AUC 为准。另外检查一下是不是把 has_outlier 之类的泄漏特征放进去了。比如 trans_max 如果直接和标签相关模型会学成交易金额大于某值就是坏人这属于特征泄漏。处理方式是在实验报告里专门有一节讨论特征独立性挑出与标签直接相关的字段剔除。5.3 现象轮廓系数很高但聚类结果完全不符合业务直觉轮廓系数只是数学度量不代表业务上有意义。我第一次跑聚类时选了 K6轮廓系数最高但每个簇之间差异不明显业务上根本说不通。后来我改用 K3 到 4虽然轮廓系数略低但每个簇的画像特征突出。记住聚类的产出要能被业务解释不要为了指标好看牺牲可读性。遇到高轮廓系数但业务解释不通的情况把 K 值降下来重新看画像。5.4 现象Access 数据库文件打不开代码报 pyodbc 错误AccountAnalytic.asdatabase 这个文件在普通环境下没有 Access 驱动时确实打不开。常见的解决方案是两种一是用 pandas 直接读 CSV 替代数据库这是源码默认的运行方式二是在实验报告里放数据库截图说明你已经完成了数据仓库建模但运行代码时走 CSV 通道。不要在环境配置上死磕课程作业更看重你能不能跑出模型结果。如果老师要求看数据库设计我建议你把表结构、星型模型 ER 图画在报告里比在代码里硬连 Access 更省事。5.5 现象代码在你自己电脑上跑通换一台电脑就报环境错误这几乎是所有 Python 课程作业的通病。我在源码包里放了一个 requirements.txt 的依赖清单但即便这样还是容易因为版本差异出问题。我强烈建议你在实验报告开头写清楚 Python 版本我用的 3.8和关键库版本pandas 1.3.5、scikit-learn 1.0.2这样老师复现时遇到小差异也能自己排查。还有一个血泪经验不要在代码里用相对路径依赖你自己的文件目录要把 CSV 读取路径写成和脚本同级这样解压后不用改路径直接能跑。6. 实验报告的成文技巧让评阅老师一眼看到你的工作量实验报告是这个作业的另一半。源码已经帮你完成了代码部分报告则需要把思路串起来。我的报告结构是固定的六段式问题定义、数据预处理、分类模型、聚类模型、结果对比、总结。其中结果对比这个部分最出彩因为大多数同学只会把模型跑完贴个准确率而我把分类模型和聚类模型联动起来展示。具体做法是先跑分类模型把账户分成高风险和低风险再把所有账户做聚类得到 4 个客户群然后画一个交叉表看高风险账户在每个客户群里的占比。如果高净值低频交易群里高风险比例是 2%而低余额高频交易群里高风险比例是 15%这个发现比单独报告有说服力得多。代码如下# 将分类预测结果和聚类标签合并 result_df X_test[[account_id]].copy() result_df[pred_label] y_test result_df[cluster_label] best_kmeans.labels_ # 注意对齐 cross pd.crosstab(result_df[cluster_label], result_df[pred_label]) print(cross)验证这一步也很关键。把训练好的聚类模型接到 Frogs_MFCCs.csv 上如果这个完全不同的数据也能被分到有意义的簇里说明你的聚类算法没有过拟合到银行数据的特殊分布上。当年的评阅老师就因为这个跨数据集验证多给了加分他认为我没有死背算法而是在思考模型的泛化能力。从那以后我每次交数据挖掘类作业都会强制在最后加一段跨数据集验证或者业务交叉分析哪怕只是简单的统计对比。这个方法你也可以直接套用希望帮到你。本文还有配套的精品资源点击获取
返回列表