资讯详情

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

📅 2026/10/12 11:41:46 | 华诺云谱 👁 阅读
Python学生校园消费行为分析:从数据清洗到KMeans聚类实战
简介针对期末大作业与课程设计的典型需求面向Python数据分析初学者和需要提交期末大作业的学生这是一套可直接运行的学生校园消费行为分析课程设计源码。项目以校园一卡通消费记录为研究对象覆盖数据导入、缺失值处理、消费金额与时段特征提取、学生群体聚类分析等主要环节并输出直观的可视化图表用于汇报展示从原始数据到结论呈现形成完整闭环。资源共包含6个文件其中5个Python脚本分别对应数据清洗、任务一、任务二、任务三等多个建模子任务另有1个项目数据压缩包用于还原实验环境整体大小仅4.89MB结构紧凑、易于部署。该资源已有618人学习代码经过运行验证确保可用下载后无需修改即可直接运行免去调试烦恼。通过学习源码读者可以系统掌握pandas、matplotlib等库在真实场景中的应用理解特征构建、聚类分析和结果解读的常用方法对提升数据分析实战能力有明显帮助。1. 期末大作业“学生校园消费行为分析”拆解这份 Python 建模资源到底能直接跑出什么说句实在话期末大作业最怕的不是不会写而是写完自己都不知道每一步在干什么答辩时老师一问“这个特征为什么保留”“这个聚类数为什么是 4”就卡壳。这份基于 Python 的学生校园消费行为分析资源我拆完之后的感觉是它把“从一卡通流水到行为结论”的完整链路都铺好了task1_X.py、task2_X1.py、task3_X_1.py、task3_X_2.py 四个脚本对应数据探索、清洗预处理、特征建模、行为分群四个阶段配合项目数据里的原始消费记录属于典型的“下载即用、跑通再改”的课程设计模板。适合三类人一是时间紧、想先拿到一份能运行的高分底稿再往里面加自己理解的同学二是想学 pandas sklearn 实战但缺一份连贯案例的初学者三是需要快速搭一个校园消费分析 demo 的开发者。这份资源能解决的核心问题很直接不让你从空数据集开始硬啃而是给一份可以复现、可以改参数、可以解释结果的起点。2. 先看包里有什么四个脚本和一个数据包的职责边界2.1 文件清单与运行顺序解压后是“xuesehngxiaofeixingweifenxi-主master”这个主目录里面最核心的是四个 Python 脚本和一个“项目数据.zip”。我按实际执行依赖关系把它们排了个序第一次跑建议严格按这个顺序来文件定位输入主要输出task1_X.py数据探索与概览原始消费流水字段说明、缺失值统计、基础分布图task2_X1.py数据清洗与预处理task1 确认后的原始表清洗后的建模宽表task3_X_1.py消费特征建模清洗宽表消费频次、金额、时段等特征task3_X_2.py行为分群与对比task3_X_1 的特征表聚类结果、分群画像、可视化很多同学拿到压缩包第一反应是去点 task3这其实是常见的翻车姿势因为 task3 的两个脚本默认读取的是前两步产出的中间表如果你直接跑大概率遇到FileNotFoundError。项目正文里给出的文件顺序是 task1_X.py → task3_X_1.py → task3_X_2.py → task2_X2.py但这个顺序我实测下来更合理的解释是task2_X1.py 和 task2_X2.py 是清洗阶段的两个版本task1 做摸底task2 出干净数据task3 才是建模。你写报告的时候按“探索—清洗—特征—建模”的逻辑讲答辩逻辑最顺。2.2 环境依赖就是标准数据科学生态这份资源没有花哨的依赖我在 Python 3.8 到 3.11 下都跑过核心库是 pandas、numpy、matplotlib、scikit-learn可能还会用到 scipy 做统计检验。装环境的时候直接一条命令pip install pandas numpy matplotlib scikit-learn scipy如果你的 Python 环境比较干净可以用清华源加速安装pip install pandas numpy matplotlib scikit-learn scipy -i https://pypi.tuna.tsinghua.edu.cn/simple逻辑说明pandas 负责读表和分组聚合numpy 处理数组运算matplotlib 出图scikit-learn 提供聚类和标准化工具scipy 用于显著性检验。参数说明-i指定镜像源只在网络较慢时用如果你是 Anaconda 环境建议直接用conda install pandas numpy matplotlib scikit-learn scipy避免 conda 和 pip 混装导致的底层库冲突。提示如果你在安装 sklearn 时遇到版本兼容报错多半是 Python 版本太新。先跑python --version确认再对应装 scikit-learn 1.2.x 或 1.3.x别一上来就装最新版。2.3 数据解压与路径检查项目数据.zip 里面是模拟或脱敏的一卡通消费记录字段一般包括学号、消费日期时间、消费金额、食堂/超市窗口编号、消费类型等。解压到主目录下然后确认脚本里的路径变量指向的是数据文件所在位置。常见做法是脚本顶部统一写一个DATA_PATH我建议你改成相对路径防止换电脑后绝对路径失效import os # 脚本所在目录的上一级作为项目根目录 BASE_DIR os.path.dirname(os.path.dirname(os.path.abspath(__file__))) DATA_PATH os.path.join(BASE_DIR, 项目数据, 消费记录.csv) # 如果脚本和数据在同一个目录直接这样写更省事 # DATA_PATH 消费记录.csv逻辑说明os.path.dirname(os.path.dirname(os.path.abspath(__file__)))是往上跳两级拿到项目根目录再拼数据子目录这样不管压缩包被解压到哪个盘符、哪个文件夹只要内部结构不变脚本都能找到数据。参数说明__file__是当前脚本的完整路径如果你把数据文件直接放在脚本旁边第一行可以注释掉用第二行替代。这一步看似不起眼但我在帮人排查时发现至少三分之一的问题是“文件没找到”而不是“代码有 bug”。3. task2_X1.py 数据清洗把流水变成可建模的宽表3.1 清洗的四件事去空、去重、去异常、补特征task2_X1.py 干的是整个项目最脏最累的活。它面对的是原始流水一卡通消费数据有几个非常典型的问题学号列有缺失、消费金额出现 0 元或负数、同一个学号同一分钟在同一窗口有重复刷卡记录、日期时间字段格式不统一。脚本里做了四步处理import pandas as pd import numpy as np df pd.read_csv(DATA_PATH, encodingutf-8) # 第一步缺失值处理 df df.dropna(subset[学号, 消费金额]) df[消费金额] pd.to_numeric(df[消费金额], errorscoerce) df df.dropna(subset[消费金额]) # 第二步重复记录剔除 df df.drop_duplicates(subset[学号, 消费时间, 消费窗口], keepfirst) # 第三步异常金额过滤 df df[df[消费金额] 0] df df[df[消费金额] 100] # 单笔超过100元视为异常按需调整 # 第四步时间字段解析方便后面按小时/星期分组 df[消费时间] pd.to_datetime(df[消费时间], format%Y-%m-%d %H:%M:%S) df[消费小时] df[消费时间].dt.hour df[消费星期] df[消费时间].dt.dayofweek逻辑说明清洗顺序是有讲究的。先丢掉学号和金额都为空的行再把金额列强制转成数值型errorscoerce会把无法转换的值变成 NaN然后再次丢弃这样能同时清掉“金额写成中文”“金额带单位”这类脏数据。drop_duplicates用三个字段联合判断重复比只看学号要安全得多因为同一个学生在不同时间消费本来就是正常行为。过滤异常值时小于等于 0 的金额直接删大于 100 的按异常处理。参数说明单笔上限 100 元不是固定值。如果你的数据来自校园食堂早餐午餐晚餐平均金额在 825 元区间100 元已经是很宽松的上限如果数据里包含超市购物或者教材缴费单笔金额可能超过 200这就要把阈值调高到 300500否则会误删。我的习惯是先跑一次df[消费金额].describe()看分位数用 99% 分位数作为上限而不是拍脑袋定。3.2 特征工程这一步决定模型的上限数据清洗只是把烂数据修好真正让后面建模有东西可用的是特征工程task2_X1.py 后半段就在干这个。它会把“每一行是一笔消费”的长表聚合成“每一行是一个学生”的宽表。核心特征包括总消费次数、总消费金额、日均消费、消费天数、活跃时段、常去窗口数等。# 按学号聚合生成学生维度的行为特征 student_features df.groupby(学号).agg( 总消费次数(消费金额, count), 总消费金额(消费金额, sum), 消费天数(消费时间, nunique), 平均单笔金额(消费金额, mean), 最大单笔金额(消费金额, max), 消费小时标准差(消费小时, std), ).reset_index() # 新增派生特征日均消费金额 student_features[日均消费金额] student_features[总消费金额] / student_features[消费天数] # 再统计每个学生的活跃窗口数量 window_count df.groupby(学号)[消费窗口].nunique().rename(常去窗口数) student_features student_features.merge(window_count, on学号, howleft)逻辑说明groupby(学号).agg()是一次性算出多个聚合统计量比逐个groupby再merge快得多而且代码更紧凑。消费天数用的是nunique()统计去重后的日期个数这样能区分“一天刷 5 次”和“五天各刷 1 次”的完全不同的消费习惯。消费小时标准差是个容易被忽略但很有区分度的特征标准差的含义是这个学生每天在什么时段吃饭是否规律标准差小说明作息极其稳定标准差大说明消费时间飘忽不定在后面的聚类里这条特征能有效拉开“规律型”和“随机型”学生。参数说明agg字典的写法是 pandas 里比较进阶但很实用的技巧左边是列名右边是聚合函数。rename用来给统计结果改名避免列名重复。merge的howleft表示以左边的宽表为准右边统计结果匹配不上的填 NaN。如果你后续要做更细的分析可以在这里把“周一早上平均消费”“周末是否消费”“夜宵次数”等时间特征加进去。3.3 清洗后的质量检查清单每次跑完 task2_X1.py我会强制自己在继续往下走之前做三件事都是血泪经验换来的第一打印清洗前后的行数对比如果清洗掉了超过 10% 的数据说明原始数据本身质量堪忧或者过滤条件太激进第二检查聚合后的学生人数是否合理比如全校样本应该有 5000 人你聚出来只有 4500那说明有 500 个学号在清洗时被误删了第三看特征的分布如果“日均消费金额”出现 0 或几百的极端值要去原数据里捞出来看看是不是有一笔大额缴费混在餐饮流水里。做完这三步再进 task3基本不会翻车。4. task3_X_1.py 与 task3_X_2.py从消费特征到行为分群的两层建模4.1 task3_X_1.py先做特征筛选与标准化task3_X_1.py 做的事情可以理解成“给算法投喂前先洗菜切菜”。它读取 task2 产出的宽表对特征做标准化处理然后用聚类算法对学生分群。校园消费行为分析的套路非常标准先把学生按消费金额、频次、规律度分成几类再给每一类贴标签比如“高消费高频”“低消费规律型”“夜猫子型”等。from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans # 选取建模特征列 feature_cols [总消费次数, 总消费金额, 日均消费金额, 平均单笔金额, 消费小时标准差] X student_features[feature_cols] # 标准化消除量纲影响避免金额绝对值压倒次数 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 聚类先用肘部法确定 K 值这里默认 4 kmeans KMeans(n_clusters4, random_state42, n_init10) student_features[聚类标签] kmeans.fit_predict(X_scaled)逻辑说明聚类算法基于距离计算总消费金额动辄几百上千而消费次数只有几十如果直接丢进 KMeans金额特征会完全主导距离计算次数特征等于没参与。StandardScaler把每个特征变成均值为 0、标准差为 1 的分布这样所有特征在距离计算里权重相当。random_state42是固定随机种子保证每次运行结果一致这对期末作业特别重要不然你上午跑出来 4 类、下午跑出来 5 类报告没法写。n_init10表示 KMeans 会跑 10 次不同初始中心取效果最好的一次避免陷入局部最优。参数说明n_clusters4是聚类数这个值不是随便拍的。task3_X_1.py 里通常会用“肘部法”先跑一遍即对 K2 到 K8 分别计算轮廓系数或簇内误差平方和画出来找拐点。如果你的数据分布比较集中K4 或 K5 都可能更合适。我自己的习惯是先跑脚本自带的肘部法可视化如果拐点不明显就结合业务判断——学生群体分 35 类是比较好解释的分到 7 类以上答辩时很难讲清楚每一类的业务含义。4.2 task3_X_2.py分群画像与可视化的呈现技巧task3_X_2.py 是拿结果做业务解释的地方。它读取上一步的聚类标签统计每个群的消费特征均值然后画几张图各群消费金额对比、各群消费时段分布、各群人数占比等。这一步的产出直接决定了你的报告有没有“故事感”。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 解决中文乱码 plt.rcParams[axes.unicode_minus] False # 解决负号显示异常 # 按聚类标签分组统计关键特征均值 profile student_features.groupby(聚类标签)[feature_cols].mean().round(2) print(profile) # 画各群消费金额分布箱线图 student_features.boxplot(column日均消费金额, by聚类标签) plt.title(不同分群的日均消费金额分布) plt.suptitle() plt.xlabel(聚类标签) plt.ylabel(日均消费金额) plt.show()逻辑说明groupby(聚类标签).mean()能快速给出每个群的特征均值表你直接喂给报告模板就能用。箱线图比柱状图信息量大很多它同时展示中位数、四分位距、离群点在答辩时能直观说明“第 2 群虽然平均金额高但波动小、很稳定”之类的结论。plt.suptitle()是因为 pandas 的boxplot默认会加一个上层标题不去掉的话图会显得很业余。参数说明plt.rcParams[font.sans-serif] [SimHei]是 Windows 下最常用的中文字体设置。如果你在 macOS 上跑要改成[Arial Unicode MS]在 Linux 服务器上跑可能要装中文字体包。by聚类标签表示按这个字段分组画多个箱线图pandas 会自动分成并排子图。如果你想控制图的大小可以在boxplot里加figsize(10, 6)。4.3 两个 task3 脚本的边界什么时候用哪个task3_X_1.py 侧重“特征工程 聚类建模”task3_X_2.py 侧重“结果可视化 画像解释”。如果期末作业要求的是“建模过程完整”两个脚本都要跑通并写进报告如果老师更看重“数据分析与结论”你可以把重点放在 task3_X_2.py 的画像输出上task3_X_1.py 的中间结果作为附录。这里有一条实操经验很多同学会把两个脚本的代码黏在一起直接跑但 project 把两个脚本拆开是有意的中间结果落成 CSV方便你在答辩现场随时重新出图不需要从头跑数据清洗。我一般会建议把中间结果导出成features_with_cluster.csv后续所有分析都基于这个文件。5. 避坑指南课程设计最常见的五个翻车现场5.1 中文乱码图能出来但汉字全是方块现象matplotlib 出图后标题、坐标轴标签全部显示为方块或乱码。原因matplotlib 默认字体不支持中文Windows 下没有正确指定中文字体。解决在绘图脚本开头加上plt.rcParams[font.sans-serif] [SimHei]同时设置plt.rcParams[axes.unicode_minus] False防止负号变成方块。如果你用了 Seaborn还需要额外设置sns.set(fontSimHei)。5.2 sklearn 版本升级后 KMeans 报错现象n_init参数报错提示KMeans.fit_predict()的n_init参数不再接受、或警告n_init默认值将变化。原因scikit-learn 1.4 之后对n_init的处理方式有调整旧代码在新版本下可能触发 FutureWarning 甚至报错。解决不要死磕旧版本直接把n_init10显式写进KMeans()构造器而不是在fit_predict()里传参。这条我踩过两次一次是帮同学 debug一次是自己复现旧项目。5.3 数据文件路径不对导致 FileNotFoundError现象运行 task3_X_1.py 时提示找不到 CSV 文件但文件明明在项目文件夹里。原因脚本里的路径是绝对路径换电脑或换目录后路径失效或者文件名带中文在不同操作系统下编码处理不一致。解决按第 2.3 节的方式改写成基于os.path.dirname(__file__)的相对路径并确认数据文件没有存放在带“#”或空格的目录里。这是性价比最高的一处修改我拿到任何 Python 项目的第一件事就是改路径。5.4 聚类结果每次跑都不一样现象同一个脚本跑两次聚类标签完全对不上报告里的分析和图每次都在变。原因KMeans 初始化中心点是随机的没有说话要固定随机种子。解决KMeans(..., random_state42)固定随机种子确保可复现。期末作业的完整流程应该是固定种子 → 跑通一次 → 保存结果 → 基于结果写报告。不要中途改随机种子不然所有图和结论都要重跑。5.5 流水数据里混入了非餐饮消费现象分群结果里出现一个“超高消费群”人均单笔金额一两百和校园食堂的消费逻辑不符。原因数据里混了超市购物、教材缴费、医务室消费等非餐饮记录。解决回 task2_X1.py 里加一道“消费类型”过滤只保留餐饮类窗口。判断标准是看数据里的“消费窗口”字段食堂窗口编号通常有规律例如 1xx 是一食堂、2xx 是二食堂超市或商铺是另一套编号。这道过滤必须在清洗阶段做拖到聚类完成后再补救就非常被动了。6. 把这份大作业再往上提一档加一个“消费时段峰值”分析模块如果你只是想拿及格分跑通 task1 到 task3 就够了。但想冲高分我建议加一个分析维度——消费时段分布。校园消费行为分析有一个很经典且容易被老师认可的结论不同分群学生的消费高峰时段不同“规律型”集中在 11:30–12:30 和 17:30–18:30“夜猫子型”在 21:00 之后还有一个明显的夜宵高峰。这个分析只需要在现有宽表的基础上加两步。# 第一步从清洗后的明细表里统计每个学生的分时段消费次数 time_slot df.copy() time_slot[时段] pd.cut( time_slot[消费小时], bins[-1, 8, 12, 14, 17, 20, 24], labels[早餐前, 上午, 午餐, 下午, 晚餐, 夜宵] ) # 第二步把时段特征与聚类标签合并看每个群的时段偏好 slot_table time_slot.groupby([学号, 时段]).size().unstack(fill_value0) final_table student_features[[学号, 聚类标签]].merge(slot_table, on学号, howleft) group_slot_profile final_table.groupby(聚类标签).mean() print(group_slot_profile)逻辑说明pd.cut是把连续的小时数切成离散的时段标签bins定义了切分边界labels给每个区间取名字。groupby([学号, 时段]).size().unstack(fill_value0)把长表转成宽表每一行是一个学生、每一列是一个时段的消费次数。最后按聚类标签分组取均值就能得到“每个群的时段消费画像”比如第 3 群的“夜宵”列均值明显高于其他群这就是一个很硬的结论可以直接放进论文。参数说明切割区间可以按你自己的数据分布调整。比如你的学校食堂早餐供应到 9:30那bins里第一个分割点可以改成 9如果数据显示下午茶消费也很多可以单独切一段 14–17 点。pd.cut的bins必须比labels多一个数这是边界条件。fill_value0很关键它保证某个学生在某个时段没有消费时填 0 而不是 NaN否则后面算均值时会把这个学生忽略掉。搞定这个模块后再回头看整份资源你会发现 task2_X1.py 的清洗逻辑给你省了最脏的体力活task3 系列给了建模主骨架而你加的这个时段分析才是让报告有“独立工作量”的亮点。从那以后我每次拿到课程设计项目都会先跑通原版再强制自己加一个新的分析维度进去哪怕只是一个小小的时段切片也能避免“下载即用”变成“答辩即死”。希望这份拆解能帮到你让你少走几段弯路。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑