新闻详情

新闻详情

首页 / 资讯中心 / 详情

Python校园一卡通消费分析:从数据清洗到可视化实战

发布时间:2026/9/16 16:46:25来源:尧图网络
Python校园一卡通消费分析:从数据清洗到可视化实战
简介面向毕业设计、期末大作业及课程设计场景Python学生校园消费行为分析项目包含数据与完整源码适合Python初学者至进阶学习者参考。项目代码注释详实整体结构清晰覆盖数据清洗、任务分解、可视化分析等环节并配套多张图表结果与分析报告可帮助理解校园消费数据的处理流程。压缩包内共有21个文件以7个ipynb任务脚本和7个jpg结果图为主另含3个py脚本、1份docx分析报告、1个md说明、1个txt备注及1个zip附件包体大小约18.93MB下载后简单部署即可运行。目前已有329人学习下载。资源来自个人手打高分项目包含食堂早中晚餐占比、就餐峰值、不同性别消费对比等典型分析维度可直接迁移用于相似选题的演示或二次开发。1. 从一卡通流水到消费行为画像Python分析项目的设计起点当你拿到几千条校园卡消费记录时最想做的往往不是算总额而是从时间戳和金额里读出学生的生活节奏几点吃早餐、常去哪个食堂、周末和平时消费差多少。这套基于Python的学生校园消费行为分析项目把这件事做成了一个完整可复现的流程原始数据、带注释的源码、跑完生成的结果图表都放在一起。对毕业设计、课程设计或者期末大作业来说它的价值在于给出了从数据导入、清洗、可视化到分组对比的完整路径。项目本身不依赖复杂的展示层分析链路才是重点换一套数据也能复用大部分逻辑。2. 数据清洗与特征工程把流水变成可分析的结构化数据2.1 一卡通流水的原始形态动手前要先确认数据长什么样。从项目的命名和结果图来看原始数据是典型的校园一卡通消费流水至少包含消费时间、交易金额、消费窗口、用户性别、专业班级等字段。部分维度可能不在同一张表里需要通过学号关联。task1_1.ipynb 和 task1_2.ipynb 负责的就是数据导入和初步清洗。通常拿到手的是 Excel 或 CSV用 pandas 读取后第一步是检查字段类型、缺失值和重复记录。一个常见陷阱是金额字段被读成字符串尤其当原始表里含有“¥”之类的符号时。下面的代码演示了标准处理流程这套项目的源代码里就采用了同样的思路。import pandas as pd # 读取原始流水先不做处理方便排查格式问题 df pd.read_csv(campus_consume.csv, encodingutf-8-sig) # 查看字段名和前几行确认哪些列有缺失 print(df.head(3)) print(df.info())这里的encodingutf-8-sig是为了兼容 Excel 另存的 CSV 文件开头产生的 BOM 头。df.info()能直接输出每列的非空数量、类型和内存占用是判断数据质量最快的入口。如果看到金额列是 object 类型不要急着 to_numeric先检查是不是有逗号分隔或者货币符号以免误删记录。2.2 时间字段解析与消费时段划分消费行为分析离不开时间。原始数据里的时间字段可能是字符串需要先统一转成datetime64然后从中提取小时、星期、日期等维度。这个操作是后面做就餐峰值和周末对比的基础。同时将时间划分成早餐、午餐、晚餐等业务时段才能回答“哪一餐消费最高”这类问题。# 假设原始时间列名为 consume_time df[consume_time] pd.to_datetime(df[consume_time], format%Y-%m-%d %H:%M:%S) # 提取时间特征 df[hour] df[consume_time].dt.hour df[weekday] df[consume_time].dt.dayofweek # 0周一6周日 df[date] df[consume_time].dt.normalize() # 去掉时分秒保留日期 # 按业务习惯划分就餐时段注意边界值要覆盖到整点 def meal_period(hour): if 6 hour 10: return 早餐 elif 10 hour 15: return 午餐 elif 15 hour 21: return 晚餐 else: return 夜宵/其他 df[period] df[hour].apply(meal_period)format参数显式指定时间格式比让 pandas 自动推断快且不容易出错。dayofweek返回 0 到 6后续做周内和周末对比时需要映射成中文。划分时段时用左闭右开区间避免同一分钟被算作两个时段。时段边界可以根据学校食堂实际开放时间调整比如有的学校早餐持续到 10:30那就要把10改成11才能和后续交叉统计保持一致。2.3 缺失值、重复值与极端金额处理流水中经常有退款记录或重复刷卡记录。退款金额是负数重复记录则是同一时间同一窗口同一金额连续出现两次。这里要区分业务规则不能直接删掉所有负数。比如校园卡充值消费场景里负数可能是退餐其绝对值才有统计意义。# 去重同一时间、同一学号、同一金额、同一窗口才算真正重复 df df.drop_duplicates(subset[student_id, consume_time, amount, window], keepfirst) # 处理退款单独标记不参与正餐消费统计 df[is_refund] df[amount] 0 df[abs_amount] df[amount].abs() # 筛选明显异常的大额消费比如超过单笔200元结合校园消费场景判断 outlier_mask df[abs_amount] 200 print(f异常大额记录数: {outlier_mask.sum()})subset指定去重依据避免把同一天正常的两笔相同金额消费误删。退款记录应该在“实付金额”维度上特殊处理如果项目整体只关心消费频次和消费强度可以单独统计不建议直接删除后影响均值。极端金额的阈值不要定死先看df[abs_amount].describe()的分位数再决定 200 还是 300。提示退款记录先打上is_refund标记再决定是否过滤直接删掉会让总消费金额偏低。2.4 构建分析用的特征表单笔流水粒度太细分组对比时需要按“人”聚合。这里构建一个以学生 ID 为索引的消费特征表包含总消费、总次数、日均消费、就餐时段偏好等字段。task3 系列会用到这层数据不能把所有分析都压在原始流水上否则透视表计算会随着记录数增长而变慢。# 按学生聚合 feature df.groupby(student_id).agg( total_amount(abs_amount, sum), # 总消费金额 total_count(abs_amount, size), # 消费次数 avg_amount(abs_amount, mean), # 单笔平均金额 max_amount(abs_amount, max), # 单笔最大金额 ).reset_index() # 合并性别、专业等静态维度从另外的信息表读入 stu_info pd.read_csv(student_info.csv, encodingutf-8-sig) feature feature.merge(stu_info, onstudent_id, howleft) # 补充时段偏好计算每个学生各时段的消费占比 period_cross pd.crosstab(df[student_id], df[period], normalizeindex) feature feature.merge(period_cross, onstudent_id, howleft) print(feature.head())groupby().agg()用一个字典一次算出多个统计量避免多次 groupby 带来的额外开销。crosstab(normalizeindex)把每个学生的时段消费次数归一化成比例保留“这个人更偏哪一餐”的信息。这段代码在源码中以类似形态出现在 task3_1.ipynb 和 task3_2.ipynb 里。howleft会让没有匹配到信息表的学生变成 NaN后续分组计算时要dropna或用fillna补成“未知”。建好的特征表大致是下面的结构也是后面做占比和显著性分析的基础字段名类型计算方式用途student_idobject原始流水学号关联主键total_amountfloat逐笔金额绝对值求和消费强度total_countint流水条数消费频次avg_amountfloat金额均值单次消费水平breakfast_ratiofloat早餐次数 / 总次数时段偏好genderobject学生信息表合并分组变量majorobject学生信息表合并分组变量表格里的breakfast_ratio在后续分析里可以和性别做交叉看看是不是某一类学生更少去食堂吃早餐。3. 食堂消费占比与就餐峰值用可视化把规律说出来3.1 整体占比与早中晚餐分布项目里已经生成了“食堂占比.jpg”和“食堂午餐占比.jpg”等图片说明分析过程中是把食堂按窗口或商户聚合看不同食堂和不同时段的占比差异。可视化之前先做数据透视再用 matplotlib 绘制。这里的逻辑是先算占比再用图突出重点。饼图适合展示占比结构但当食堂超过5家时建议改成横向柱状图因为饼图的标签会互相遮挡。import matplotlib.pyplot as plt # 按食堂名称聚合订单笔数 place_count df.groupby(place).size().sort_values(ascendingFalse) # 绘制占比饼图 plt.figure(figsize(8, 6)) plt.pie(place_count.values, labelsplace_count.index, autopct%.1f%%) plt.title(各食堂消费占比) plt.tight_layout() plt.savefig(食堂占比.jpg, dpi150) plt.show()autopct控制饼图上的百分比格式dpi150保证存下来的图片在论文里足够清晰。注意sort_values(ascendingFalse)之后排序靠前的食堂会从12点方向开始依次排列颜色区分也符合阅读直觉。如果某个食堂占比低于 2%在饼图上几乎看不见可以先合并成一个“其他”类别。3.2 早中晚餐占比对比如果把时段和食堂两个维度叠在一起就能看出“哪家食堂承包了早餐”。实现上可以用pivot_table或crosstab。crosstab 的normalize参数在这里是关键按列归一化和按行归一化会得到完全不同的结论。# 时段×食堂 的交叉表 meal_place pd.crosstab(df[place], df[period], normalizecolumns) # 只看早餐时段按占比排序 breakfast_df meal_place[早餐].sort_values(ascendingFalse).head(5) # 绘制横向柱状图 plt.figure(figsize(8, 4)) breakfast_df.plot(kindbarh) plt.xlabel(早餐时段消费占比) plt.title(早餐主要消费食堂 Top5) plt.tight_layout() plt.savefig(食堂早餐占比.jpg, dpi150)normalizecolumns让每列和为 1也就是同一个时段内各食堂的分配比例。这样保存的图片与项目中的“食堂早餐占比.jpg”对应含义是“早餐时大家更常去哪家食堂”。如果改成normalizeindex则变成每个食堂内部三个时段的分配适合回答“某食堂的生意集中在哪一餐”。两个维度并不冲突可以都画出来互相印证。3.3 就餐峰值识别就餐峰值比占比更直观按小时统计订单量即可。需要注意不同日期类型的峰值可能不同工作日和周末的食堂开放时间也不一样所以先拆开看不要混在一起画一条曲线。峰值识别的结果可以指导食堂窗口排班这也是这类项目在答辩时容易被追问的落地场景。# 添加星期类型 df[date_type] df[weekday].apply(lambda x: 周末 if x 5 else 工作日) # 按小时和星期类型统计订单量 hourly df.groupby([date_type, hour]).size().reset_index(nameorder_cnt) # 分别绘制两条曲线 for dtype in [工作日, 周末]: sub hourly[hourly[date_type] dtype] plt.plot(sub[hour], sub[order_cnt], labeldtype, markero) plt.xlabel(小时) plt.ylabel(订单量) plt.title(工作日与周末就餐峰值对比) plt.legend() plt.tight_layout() plt.savefig(就餐峰值.jpg, dpi150)markero让散点出现在每个整点上方便读取具体峰值时段。从这样的图能直接看出午餐峰值是否集中在 12 点、晚餐是否被拉长到 19-20 点。如果两条曲线都在某个小时出现尖峰说明那个时段是全周性刚需可以进一步按食堂拆分看是哪一个食堂贡献了主峰。从源码文件的组织看task2_1.ipynb、task2_2.ipynb 与上面的分析是逐段对应的推荐按“先占比、后峰值、再交叉”的顺序执行这样产出的图片自然构成了毕设中的图表章节。对应的输出结构见下表分析主题相关源码输入数据输出图表各食堂整体占比task1_2.ipynb清洗后的流水表食堂占比.jpg早/午/晚餐时段占比task2_1.ipynb含 period 字段的流水表食堂早餐/午餐/晚餐占比.jpg工作日与周末就餐峰值task2_2.ipynb含 hour、date_type 字段就餐峰值.jpg这张表在写报告时可以直接用作“图表索引”说明每个图是用哪一段代码、从哪一层数据里算出来的。4. 性别与专业维度的消费差异从分组统计到显著性验证4.1 分组聚合看消费强度差异单看总量没意义因为男女样本量可能不同。需要看人均和单笔平均。这里用 groupby 按性别分组计算多指标。注意这里用到的是第2章构建的feature表不是原始流水。gender_stat feature.groupby(gender).agg( student_count(student_id, nunique), avg_total(total_amount, mean), avg_per_order(avg_amount, mean), avg_count(total_count, mean) ).round(2) print(gender_stat)nunique统计的是去重后的学生人数而不是记录数避免一个人刷几十次卡把人数带偏。round(2)控制输出小数位。avg_per_order和avg_total的区别是前者是“每顿平均花多少”后者是“一个阶段总共花多少”。这两个指标往往有相反的趋势比如某群体单次消费不高但总消费多说明它消费频次更高这两者要一起解读才会更有深度。4.2 用箱线图检查分布情况均值会被极端值拉偏箱线图能展示中位数、四分位数和离群点。对毕业设计来说一张箱线图加上一个 p 值比单纯堆数字更有说服力。画箱线图之前先确认分组数据量如果某一组人数少于 30箱线图的四分位数就不够稳定最好在前面加print(feature[gender].value_counts())看一眼样本量。import matplotlib.pyplot as plt # 只画男生和女生的日均消费分布 data_by_gender [feature[feature[gender] g][avg_amount] for g in [男, 女]] plt.figure(figsize(6, 5)) plt.boxplot(data_by_gender, labels[男, 女], showfliersFalse) plt.ylabel(单笔平均消费金额) plt.title(不同性别单笔消费分布对比) plt.tight_layout() plt.savefig(性别消费对比.jpg, dpi150)showfliersFalse不显示离群点让箱体更紧凑。如果离群点过多可以先通过分位数裁剪数据否则箱线图会被压成一条线。在项目里性别字段可能是“男”“女”也可能是“M”“F”先用value_counts()确认取值再做feature[gender].replace({M: 男, F: 女}, inplaceTrue)这一步不能省。4.3 用scipy做独立样本t检验图形只能“看”统计检验才能回答“差异是否显著”。这里用scipy.stats.ttest_ind比较男女单笔消费均值。前提是两组样本量不要悬殊并且大致服从正态分布校园消费数据通常近似正态可以直接用这个函数。如果数据明显偏态改用mannwhitneyu做非参数检验更稳妥。from scipy.stats import ttest_ind male_vals feature[feature[gender] 男][avg_amount] female_vals feature[feature[gender] 女][avg_amount] t_stat, p_value ttest_ind(male_vals, female_vals, equal_varFalse) print(ft统计量: {t_stat:.4f}) print(fp值: {p_value:.4f}) if p_value 0.05: print(结论在0.05显著性水平下性别间消费差异显著) else: print(结论差异不显著)equal_varFalse表示采用 Welch t 检验不假设两样本方差相等适合样本量不一致的情况。p 值只能回答“有没有差异”不能说“男生就是比女生花得多”还需要结合上一节的均值方向。在撰写毕业设计报告时把这段输出直接转述成“某类人群的日均消费显著更高t...p0.05”就是一个标准的统计结论。注意t检验只能证明差异存在无法说明差异有多大报告里最好同时给出均值和标准差。4.4 专业与性别的交叉对比项目里有一张“18连锁经营专业不同性别消费对比图”说明分析维度还包含专业。这里演示如何做二维交叉分析。交叉表的价值在于同时揭示两个因素专业内部性别差异以及同性别在不同专业间的差异。但要注意当某个分组样本量过小时结果不具备可解释性。# 专业 × 性别 的消费均值透视表 major_gender feature.pivot_table( indexmajor, columnsgender, valuestotal_amount, aggfuncmean, observedTrue ).round(2) # 保存到csv方便写报告时引用 major_gender.to_csv(major_gender_consumption.csv, encodingutf-8-sig)observedTrue是在 pandas 2.x 里处理分类变量时避免告警的常用参数。透视表行列分别是专业和性别值是总消费均值。save 成 CSV 的时候注意encodingutf-8-sig否则用 Excel 打开中文会乱码。如果某个专业只有 1 个学生aggfuncmean得到的值不具备代表性需要同时输出人数并加过滤条件。这是拿到源码后直接跑最容易忽略的地方。可以在报告里用下面的表格模板记录结果把实际数据填入对应单元格即可专业男生人数女生人数男生均值女生均值显著性18连锁经营122815.2012.45p0.03219会计83513.1011.02p0.104表中数字是示意实际要跑完项目代码后替换。表格结构本身表达的是先报告样本量再报告均值和 p 值这样的排序能让读者一眼判断结论可信度。5. 从ipynb到脚本化复现项目与参数化改造5.1 把多个notebook串成单入口项目拆成了多个 ipynb 文件复现时还要手动按顺序执行。如果想作为毕设演示或交付建议整理成一个main.py用函数把清洗、特征工程、可视化和统计检验封装起来。这样导师或答辩评委拿到手后不需要打开 notebook 就能重跑。import pandas as pd def load_data(csv_path): df pd.read_csv(csv_path, encodingutf-8-sig) # 这里补上清洗逻辑 return df def build_features(df): # 这里补上特征构建逻辑 return feature if __name__ __main__: df load_data(data/campus_consume.csv) feature build_features(df) # 然后调用生成图表的函数把if __name__ __main__:作为程序入口避免被 import 时直接执行。函数拆分的粒度以“一个函数对应一个任务”为准比如plot_meal_share(df)对应占比分析plot_peak(df)对应峰值分析。如果你觉得保留 notebook 更符合课程要求也可以保留 ipynb但把公共函数提取到一个utils.py里notebook 只负责调用和展示。5.2 用参数控制输入输出路径源码里直接写好相对路径没有问题但换机器、换数据集时容易报错。更稳的写法是使用argparse接收路径参数。这样也能把这份源码改成你的毕设题目比如换成“某高校图书馆消费分析”只需要在命令行传入新的数据路径。import argparse import os parser argparse.ArgumentParser(description校园消费行为分析) parser.add_argument(--input, defaultdata/campus_consume.csv, help消费流水CSV路径) parser.add_argument(--output, default./result, help图表输出目录) args parser.parse_args() os.makedirs(args.output, exist_okTrue)--output参数配合os.makedirs(exist_okTrue)可以避免每次手动建目录。运行方式变成python main.py --input data/xxx.csv --output ./result方便反复调参。如果你在 Windows 上跑注意路径分隔符用/而不是\\否则在 macOS 或 Linux 上切换会报找不到文件。5.3 把结论汇总到一份Excel图片放在文件夹里不直观建议用pandas.ExcelWriter把统计表和图片说明汇总到一个 Excel 里方便在毕设文档中引用。导出时最常见的坑是索引没有 reset导致第一列变成空值。with pd.ExcelWriter(结论汇总.xlsx) as writer: gender_stat.to_excel(writer, sheet_name性别对比) major_gender.to_excel(writer, sheet_name专业性别交叉) hourly.to_excel(writer, sheet_name峰值统计)ExcelWriter配合with语句可以在一个文件里写多个 sheet避免多次打开文件出现权限错误。注意分组聚合后的结果如果带 MultiIndex需要先reset_index()再输出否则导出的 Excel 里索引会变成空列。另外to_excel默认会把 DataFrame 的 index 也写进去如果 index 是学号这种业务键保留是有意义的如果是 range 索引就在to_excel里加indexFalse。如果你拿到的数据有更细的商户类别字段可以把这里的“食堂占比”替换成“窗口菜品类型占比”透视表和饼图逻辑不用改动。唯一要注意的是窗口字段的编码一致性有的数据用中文名有的用编号先建立映射字典统一成一套标签再进groupby不然一个窗口会被拆成好几段占比图会失真。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

conda env config vars unset 详解:Conda 环境变量的移除与恢复机制 2026/9/16 17:16:31

conda env config vars unset 详解:Conda 环境变量的移除与恢复机制

conda env config vars unset 详解:Conda 环境变量的移除与恢复机制 【免费下载链接】conda A system-level, binary package and environment manager running on all major operating systems and platforms. 项目地址: https://gitcode.com/GitHub_Trending/co…

阅读更多 →
基于MATLAB的说话人识别系统:MFCC特征提取与VQ码本实现 2026/9/16 17:16:31

基于MATLAB的说话人识别系统:MFCC特征提取与VQ码本实现

简介:这套基于MATLAB的说话人识别系统毕业设计项目,主要面向计算机、电子信息类专业正在完成毕业设计或课程设计的学生,也适合需要语音识别实战经验的中高级学习者。项目经导师指导并调试通过,属于评分98分的优秀毕设案例&#xf…

阅读更多 →
Hydra 插件开发实战指南:深入理解插件注册机制与基于示例的完整开发流程 2026/9/16 17:16:31

Hydra 插件开发实战指南:深入理解插件注册机制与基于示例的完整开发流程

Hydra 插件开发实战指南:深入理解插件注册机制与基于示例的完整开发流程 【免费下载链接】hydra Hydra is a framework for elegantly configuring complex applications 项目地址: https://gitcode.com/GitHub_Trending/hyd/hydra 本指南以 Hydra 官方文档&…

阅读更多 →
MATLAB实现相干衍射成像:从衍射图反演物体复振幅 2026/9/16 17:16:31

MATLAB实现相干衍射成像:从衍射图反演物体复振幅

简介:本资源是一套面向光学成像与计算成像方向的MATLAB仿真代码包,专为电子信息、自动化、人工智能及物理光学等相关专业学生与初学者设计,用于理解并实践相干衍射成像(CDI)的核心原理与数值模拟流程。资源共3个文件&a…

阅读更多 →
GenieX SDK 端到端测试体系深度解析:pytest 套件、模型矩阵与云端真机 CI 2026/9/16 17:16:31

GenieX SDK 端到端测试体系深度解析:pytest 套件、模型矩阵与云端真机 CI

GenieX SDK 端到端测试体系深度解析:pytest 套件、模型矩阵与云端真机 CI 【免费下载链接】GenieX Run frontier LLMs and VLMs locally on Qualcomm devices across NPU, GPU, and CPU with a few lines of code 项目地址: https://gitcode.com/GitHub_Trending/…

阅读更多 →
es-toolkit compat trim 详解:Lodash 兼容版字符串裁剪的用法、参数语义与源码实现 2026/9/16 17:13:30

es-toolkit compat trim 详解:Lodash 兼容版字符串裁剪的用法、参数语义与源码实现

es-toolkit compat trim 详解:Lodash 兼容版字符串裁剪的用法、参数语义与源码实现 【免费下载链接】es-toolkit A modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash. 项目地址: https://gitcode.com…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞