Python学生校园消费行为分析:从数据清洗到聚类实战

发布时间:2026/10/1 22:49:46
Python学生校园消费行为分析:从数据清洗到聚类实战 简介基于Python的学生校园消费行为分析项目围绕校园一卡通消费记录展开通过数据清洗、特征工程与可视化等手段从食堂、时段、性别等维度刻画学生消费规律可服务于食堂运营优化及贫困生精准援助等场景。资源面向数据分析与Python学习者覆盖完整源码、实验数据与分析结果集。包内共22个文件包含7个Jupyter Notebook承担任务拆解与建模过程、3个Python脚本、8张结果展示图就餐峰值、食堂各时段占比、性别消费对比等另附分析报告与说明文档压缩包整体约19.08MB目录结构清晰。目前已有143人学习下载。对相关领域初学者或竞赛选手而言资源打通了从一卡通原始数据到分析结论的完整闭环Notebook分步呈现数据处理与建模思路图表与报告可直接复用便于快速掌握学生消费行为分析的常见方法也可在此基础上扩展个性化研究。1. 基于Python的学生校园消费行为分析源码、数据与结果集到底能做出什么每年毕业季和课程设计季都会有一大批人搜“学生校园消费行为分析Python源码”这个词的热度常年不减。但真拿到源码包、数据集和结果集之后最常见的翻车点其实是——代码能跑但不知道每一步在干什么也不知道结果怎么解释。学生校园消费行为分析这个方向的核心价值在于把一卡通流水这种高维、稀疏、带时间戳的原始数据转化成可解释的用户画像、消费规律和异常行为信号。它适合课程设计、毕业设计也适合做数据分析和机器学习方向的练手项目。这一篇我不会假装有某个现成源码包在手只从一线工程视角把这类项目最常见的实现路径、参数设置、结果集结构和踩坑点逐一拆开。你用自己学校的一卡通数据、爬虫抓的公开数据或模拟数据都能照做关键是理解口径和边界。先说明白一件事这类项目难的不是聚类或可视化而是“消费行为”这个词到底怎么定义成特征。2. 从流水到特征学生消费行为分析的数据口径与预处理方案2.1 一卡通消费原始数据长什么样字段、粒度与脏数据学生校园消费行为分析的第一步从来不是写聚类代码而是先把流水数据摸清楚。绝大多数高校的一卡通流水会包含这几类核心字段学号或卡号、交易时间、消费金额、商户名称、消费类型餐费、超市、洗浴、饮水、门禁扣费等、余额。字段命名各校不同但信息含量基本一致。我见过最差的数据集只有四个字段卡号、时间、金额、地点名称连学号都没有这种情况下做个人画像会非常吃力。拿到数据先做三件事看时间跨度、看记录条数、看字段缺失比例。时间跨度至少覆盖一个完整学期才有意义因为消费行为天然有周周期和月度周期。记录条数决定你能不能用个体级别的分析方法——如果只有几千条流水那只能做群体分析做不了个体聚类。缺失比例高的时候首先确认是采集问题还是导出问题不要直接填充。原始数据里最常见的脏数据有测试卡金额为0或负数、退款流水负金额但商户名带“退”字、跨校区流水同一张卡多条不同校区记录、凌晨异常消费、余额字段与消费金额不匹配。这些脏数据直接用阈值过滤经常会误伤更稳妥的做法是先按卡号和交易时间排序人工抽看100条流水把业务上的异常模式记下来再写过滤规则。2.2 用Python做消费流水的清洗与聚合最小代码实现下面是一段可以直接跑通的清洗代码适用于大多数“卡号、时间、金额、商户”四字段格式其他字段自行映射即可。import pandas as pd import numpy as np from datetime import datetime # 加载原始流水注意encoding按实际情况调整 df pd.read_csv(consume_flow.csv, encodingutf-8-sig) df.columns [card_no, trade_time, amount, merchant] # 时间字段转标准格式 df[trade_time] pd.to_datetime(df[trade_time]) # 过滤明显异常金额为0、负金额且非退款标记、单笔超过200的测试交易 # 阈值设置依据普通校园消费单笔极少超过100超过200基本是充值或测试 df df[df[amount] 0] df df[df[amount] 200] # 剔除夜间测试消费凌晨2点到5点基本无真实消费场景 df df[~df[trade_time].dt.hour.isin([2,3,4])] # 按卡号和商户名去除完全重复的流水同一秒同一商户同金额视为重复 df df.drop_duplicates(subset[card_no, trade_time, merchant, amount]) # 衍生基础字段日期、星期、小时、是否工作日 df[date] df[trade_time].dt.date df[weekday] df[trade_time].dt.weekday df[hour] df[trade_time].dt.hour df[is_weekend] df[weekday].isin([5,6]).astype(int) print(f清洗后剩余记录数: {len(df)}) print(df[[card_no, trade_time, amount, merchant]].head())这段代码的逻辑是先做格式统一再做业务规则过滤最后做衍生字段。注意几个参数的设置逻辑200元的单笔阈值不是拍脑袋而是先看金额分布的95分位数再定凌晨2点到5点的剔除规则也要先统计各小时交易量再确认——有些学校有24小时便利店这部分需要保留建议先用df.groupby(hour).size()看一眼分布再决定几点到几点可以安全剔除。清洗之后的数据集才是分析的基础。如果直接拿原始流水去做聚类或可视化结果集里会出现大量“余额充值”“食堂补卡”这类非消费记录导致聚类中心被拉偏。2.3 打标签的隐蔽细节消费行为≠消费金额这里有个关键认知消费行为分析的重心不是“花了多少钱”而是“怎么花钱”。两个学生月消费额完全相同但一个是每天规律三顿饭、周末宅宿舍另一个是周一到周五几乎不消费、周末集中大额购物——这两类人的消费行为画像完全不同。所以在做特征工程时要围绕消费频次、消费时段分布、消费地点多样性、消费金额稳定性这四个维度展开而不是只盯着月总消费额。我见过很多源码包只做“月均消费”“日均消费”“总消费”三个特征就上KMeans做出来的聚类结果基本就是按穷富分层完全体现不出“行为”两个字。正确做法是至少构造以下特征组消费频次日均笔数、每周活跃天数、时段偏好早餐时段占比、午餐时段占比、晚餐时段占比、夜宵时段占比、地点结构常去商户数、单一商户依赖度、食堂消费占比、金额画像金额均值、金额标准差、金额中位数、大额消费次数。这些特征构造完聚类结果才有解读空间。3. 消费画像怎么做RFM变体与KMeans聚类参数选择3.1 把RFM模型改造成校园消费版R、F、M的定义差异RFM模型是消费分析的老祖宗但电商领域的RFM不能直接用在校园场景。电商的R是最近一次购买时间距今天数校园场景里学期末和学期初差距很大直接用自然日计算最近消费间隔会被假期严重干扰。我做校园项目时通常这样改造R值取数据时间范围内最后消费日期距数据截止日期的天数但只统计在活跃周内的记录排除假期前后连续不消费的自然中断。F值不是总消费次数而是“周均活跃消费天数”。因为有些学生一天刷10次每顿饭刷一次有些学生一天刷3次用总次数会膨胀了吃饭次数而压缩行为差异。M值保留总消费额和月均消费额两个版本看业务需要。M值在校园场景下代表消费能力但也要结合家庭经济水平做归一化——否则聚类出来的本质是家庭收入分层。这套改造做下来你会发现同一个RFM三元组校园场景和电商场景的解释完全反向电商R值小、F值高是优质用户但校园里R值小、F值高的学生可能就是天天宅在宿舍周边消费消费地点多样性未必高。所以特征里一定要加一个“地点多样性”维度否则聚类结果很难写成报告。3.2 KMeans聚类归一化、K值选择与轮廓系数KMeans是这个项目里最常见的聚类算法选它不是因为效果最好而是因为结果好解释、写报告方便。但KMeans有个致命前提必须做标准化。消费金额的量纲是元消费频次的量纲是次/周消费时段的量纲是百分比如果不标准化KMeans的欧氏距离会被金额特征完全主导。from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score import pandas as pd # 假设df_features是特征表索引是card_no列是特征名 feature_cols [weekly_freq, avg_amount, std_amount, breakfast_ratio, lunch_ratio, dinner_ratio, merchant_diversity, weekend_ratio] X df_features[feature_cols].values # 标准化这一步不做聚类结果基本是按金额一维排序 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 用轮廓系数选K遍历2到8选轮廓系数下降拐点 # 注意轮廓系数不是越大越好要结合业务可解释性 sil_scores {} for k in range(2, 9): km KMeans(n_clustersk, n_init10, random_state42) labels km.fit_predict(X_scaled) sil_scores[k] silhouette_score(X_scaled, labels) print(不同K值的轮廓系数:, sil_scores) # 真实项目中我常用K4原因在后文说明 best_k 4 km KMeans(n_clustersbest_k, n_init10, random_state42) cluster_labels km.fit_predict(X_scaled) # 把聚类结果写回特征表 df_features[cluster] cluster_labels df_features.to_csv(consumer_clusters.csv, encodingutf-8-sig)KMeans里有三个参数值得单独说。n_init默认是10表示用不同随机中心跑10次取最优这个参数在样本量小时效果不明显但样本量上万后建议调大到20否则每次运行结果都可能不一样。random_state必须固定否则你跑出来的聚类结果和源码包里给的结果集对不上写报告时图表数字会变。max_iter遇到不收敛警告时再调默认300够用调大意义不大。选K值时轮廓系数只是个参考不要机械地选最大值。比如轮廓系数在K2时最高但K2把学生分成“爱花钱”和“不爱花钱”两类报告毫无深度。常见做法是跑完2到8后每个K值都打印每个簇的特征均值看哪一个K的簇间差异在业务上最好解释——我一般选4或5因为“规律就餐型”“夜猫子型”“高消费型”“极简型”这四类已经能覆盖大多数校园场景分太多类反而写不出差异。3.3 聚类结果怎么验证别只看轮廓系数轮廓系数只能说明簇的分离度不能说明簇的业务含义。验证聚类结果是否靠谱标准做法是回看每个簇的特征均值表逐项检查是否符合直觉。比如“高消费型”簇的avg_amount应该显著高于整体均值merchant_diversity也应该高“规律就餐型”簇的breakfast_ratio和lunch_ratio应该显著高于其他簇。如果某个簇的特征均值和另一个簇几乎相同那就是K选大了或者特征里有冗余项。另一个验证思路是用时间序列做外部校验。把聚类标签贴回原始流水按周统计每个簇的消费总额趋势看是否呈现有规律的周期波动。如果某个簇的周消费趋势是一条直线或者白噪音说明这个簇可能没分好。这个验证过程在写报告时非常好用因为它能给出“簇间差异显著”的量化证据。4. 消费时段与行为模式识别从时间特征到可解释标签4.1 早中晚时段划分一个影响全局的参数消费时段划分是整个项目里最容易被低估的参数。很多源码直接用hour 10当早餐、10 hour 15当午餐、15 hour 21当晚餐这种粗暴划分在真实校园数据里会产生严重偏差。比如某高校上午第一大节11:30下课食堂高峰在11:30到12:30早餐截止在9:30之后基本没人又比如部分高校有夜宵档口营业到22:30如果晚餐截止到21点夜宵消费会被算成明天的早餐。正确做法是先用时间段聚合出的“每小时消费笔数”曲线找到真实波峰。下面这段代码就是干这个的# 统计全天24小时的消费笔数分布用于确定时段切分点 hourly_counts df.groupby(hour).size() hourly_pct hourly_counts / hourly_counts.sum() * 100 print(hourly_pct) # 根据波峰位置手动设置时段划分 # 示例峰值在8-9点为早餐11-13点为午餐17-19点为晚餐 def assign_meal_period(hour): if 6 hour 9: return breakfast elif 11 hour 13: return lunch elif 17 hour 19: return dinner elif 20 hour 23: return night_snack else: return other df[meal_period] df[hour].apply(assign_meal_period) # 计算每个学生各时段消费笔数占比作为特征 meal_pivot df.groupby([card_no, meal_period]).size().unstack(fill_value0) meal_ratio meal_pivot.div(meal_pivot.sum(axis1), axis0) meal_ratio.columns [f{col}_ratio for col in meal_ratio.columns]这段代码的关键在于时段边界值是自己根据数据分布定的不是抄通用公式。不同学校的作息差异很大有的学校上午第一大节8点开始早餐高峰在7:30左右有的学校上午没课早餐高峰能拖到10点。做这一步时不要嫌麻烦把每个小时的消费笔数打印出来看一眼再定边界。这个参数直接影响后面所有时段相关特征和聚类结果。4.2 消费地点多样性被多数源码忽略但最能区分行为模式的特征消费地点多样性这个特征在很多课程设计源码里根本不存在但它恰恰是区分“规律食堂党”和“校园漫游型消费”的核心维度。构造方式很简单每个学生出现过的商户名称去重计数再和周均消费笔数做比值得到“平均每笔消费涉及多少不同商户”的指标。这个特征在聚类里能有效把“两点一线型”和“高流动型”学生分开但要注意一个坑商户名称在不同校区可能重名比如两个校区都有“一食堂”如果数据没带校区ID地名多样性会被低估。如果原始流水有校区字段最好先把商户名拼上校区前缀再算多样性。有的数据集里商户名还会出现“临时窗口1”“临时窗口2”这类不可复用的名称这种要做归一化映射把临时窗口全部映射成“临时档口”一个类别否则同一个档口换名字会被算成两个地点。4.3 周末消费占比与作息规律指数行为标签的扩展维度周末消费占比这个特征非常便宜但很有解释力。它的定义是周末消费笔数除以总消费笔数能反映学生是“周末宅校型”还是“周末离校型”。配合夜间消费占比晚22点后的消费笔数占比可以进一步识别“夜猫子型”消费行为。这两个特征加进聚类后簇的边界会更清晰。作息规律指数更简单——直接统计每个学生一周7天里“有消费记录的天数”但这个指标在假期周会失真。我的处理方式是只取学期内完整周的数据去掉放假前后两周再计算平均每周活跃天数。这样做出来的规律指数更接近真实生活作息。这些特征结合起来才算把“消费行为”做成了能解释的标签体系而不是一堆堆在表格里没人看的数字。5. 避坑指南学生消费行为分析的常见翻车点与排查方法5.1 流水数据里的“假消费”记录现象、原因与解决现象聚类结果里出现一个簇消费笔数极高但金额极低日均消费10次以上、单笔均额不到1元。原因这类数据多半是饮水机的流水。很多学校的饮水机每次刷卡0.1到0.5元一天接10次水很正常。饮水消费混在餐费里面做聚类会把“接水频繁”错当成“消费行为活跃”完全扭曲画像。解决找到饮水机对应的商户名关键词“饮水”“开水”“直饮水”单独把这类流水剔除或单独建特征。如果源码包里没做这一步你可以自己加过滤规则df df[~df[merchant].str.contains(饮水|开水|直饮)]。这个字段一定要问清楚否则聚类结果直接翻车。5.2 跨学期数据直接拼接现象、原因与解决现象按月份画的消费趋势图在9月和3月出现断崖式下跌。原因数据跨越了两个学期中间有寒假或暑假。假期学生基本不消费直接把整个自然年的流水拼在一起假期那几个月都是零消费或极少消费趋势图没法看聚类特征里的“周均活跃天数”也会被假期拉低。解决做两类处理——如果做个体画像只取学期内数据如果做趋势分析把假期月份单独标注出来不要把假期当成普通停工周。具体做法是先用df[month] df[trade_time].dt.month标记月份然后df df[~df[month].isin([1,2,7,8])]剔除寒假暑假月份。非要保留假期数据的话把“是否假期”作为单独特征塞进模型而不是混在时间序列里直接画图。5.3 KMeans聚类结果每次跑都不一样现象、原因与解决现象同一份数据、同一个K值连续跑两次聚类结果集里的簇编号对不上甚至簇中心都不同。原因KMeans用随机初始化中心点random_state没固定或者n_init太小聚类结果会出现波动。这在课程设计答辩时很尴尬——导师让你现场重跑一遍结果图跟报告里对不上。解决设置random_state42固定随机种子n_init调大到20。如果换了机器跑还是对不上检查sklearn版本——旧版本和新版本的KMeans在初始化策略上有微调结果会有细微差异。把跑出结果集的Python版本和sklearn版本写进实验环境说明里这招在写报告时很好用。5.4 特征里混入充值记录现象、原因与解决现象聚类结果里出现一个簇总额极高、笔数极少、金额方差极大。原因一卡通系统里“充值”也是交易流水金额通常是100、200、500这类整数。如果清洗代码里没有排除充值类型充值记录会被当成消费行为一个人充500块会被误判为“大额消费型”。解决在清洗阶段看商户名或交易类型字段把包含“充值”“圈存”“转账”的记录直接剔除。如果数据没有交易类型字段就用金额特征做辅助判断——正常校园消费几乎不会出现大量整百金额按金额尾数过滤也能去掉大部分充值记录。但这个方法不完美因为食堂套餐可能有整10元定价优先级最高的还是找到交易类型字段。5.5 商户名编码混乱导致地点特征失效现象、原因与解决现象算出来的merchant_diversity异常偏高平均每个学生去过几十个商户明显不符合直觉。原因同一家商户在不同时期有不同的系统编码比如“一食堂一楼”“一食堂-1F”“第一食堂一层”三个名字其实是同一个地方但没有归一化被当成三个不同地点。解决用字符串模糊匹配或人工映射表做商户名归一化。常见做法是提取关键词“一食堂”“清真”“超市”“便利店”做类别映射而不是直接用原始商户名。这个预处理步骤做得好不好直接决定地点相关特征是否可信。6. 结果集怎么用消费行为分析报告的输出与验证6.1 结果集的标准构成聚类表、特征统计、可视化图一个完整的结果集不只是一张聚类标签表通常包含四类产物带聚类标签的学生特征表CSV格式每一行是一个学生列是特征值和标签每个簇的特征均值对比表用于报告分析和簇解释可视化图特征分布图、聚类散点图、消费时段热力图核心分析结论文本每个簇的行为描述和占比。源码包里如果只有聚类代码没有结果说明报告写作时会很痛苦。以聚类表为例每行记录格式建议包含card_no、cluster、weekly_freq、avg_amount、merchant_diversity、weekend_ratio等。这张表是后续所有分析的主表不要只保留聚类标签而丢了原始特征。簇特征均值表则是用来解释簇含义的——比如报告里写“第0簇学生日均消费2次主要集中在食堂周末消费占比仅15%属于规律就餐型”这些数字全部来自均值对比表。6.2 用Excel报告模板输出参数与格式结果集交付时最常用的是Excel格式因为写报告和答辩展示都方便。用pandas.ExcelWriter可以把多个DataFrame写进同一个工作簿的不同sheet比输出多个CSV文件更整洁。with pd.ExcelWriter(消费行为分析结果.xlsx, engineopenpyxl) as writer: df_features.to_excel(writer, sheet_name学生消费特征表, indexFalse) cluster_summary.to_excel(writer, sheet_name簇特征均值对比, indexFalse) # 每个簇的样本量占比 cluster_size df_features[cluster].value_counts() cluster_pct cluster_size / cluster_size.sum() * 100 cluster_pct.to_frame(name占比(%)).to_excel(writer, sheet_name簇分布)这个写法的好处是结构清晰一个Excel文件包含个体数据、聚合数据和分布统计导师或甲方打开一个文件就能看到全部结果。注意openpyxl引擎要预先安装pip install openpyxl否则写入Excel会报错。6.3 行为标签落库与后续扩展从聚类结果到持续监控如果这个项目不止用于课程设计而是要做成可运行的系统聚类结果不应该只停留在CSV和Excel里。常见做法是建一张MySQL表字段包括学号、所属簇、聚类日期、特征快照每周跑一次清洗和聚类脚本对比簇成员的变化。有学生的簇归属发生变化说明他的消费行为模式发生了转移——比如从“规律就餐型”变成了“外卖型”这可能是作息变化或生活状态变化的信号。这个扩展方向是校园消费行为分析真正有应用价值的地方但需要先跑通基础版本的清洗、聚类和结果输出否则后面全是空中楼阁。6.4 一个验证习惯每次跑完聚类先抽10个学生回看原始流水这是我做这类项目最习惯的动作——聚类跑完不要急着生成报告随机抽每个簇5到10个学生回到原始流水一条一条看他们的消费记录。如果“高消费型”簇里抽出来的学生确实每周都在校外餐厅和超市大额消费说明聚类可信“规律就餐型”簇里如果抽出来的学生有一半根本不在食堂吃饭那特征构造或者清洗环节一定有问题回去查。这个习惯救过我很多次因为特征工程和聚类都是黑匣子只有回到最原始的流水才能确认中间环节没把数据搞坏。写报告时也可以把抽检结果截图放进去作为“结果可信度验证”的一部分比单贴一个轮廓系数有说服力得多。希望这篇基于Python的学生校园消费行为分析落地笔记能帮到你至少让你少走我当年走过的弯路。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询