Python数据分析实战:从课程作业到图书馆借阅行为分析
2026/9/23 2:25:07 网站建设 项目流程

1. 选题定调:这份“作业”到底要做什么

先说结论:把一份看起来普普通通的课程作业,做成一个能写进简历的完整数据分析小项目,这完全可行。我这次拿到的题目就是“shuyibin的作业”,一份信息科技课的期末大作业,表面要求是“使用Python对一组真实数据进行统计分析并撰写报告”,但老师没有限定数据来源、没有限定分析维度、也没有限定报告形式。这种自由度极高的作业,最容易做崩,也最容易做出彩。

很多同学遇到这种开放式作业,第一反应是去网上抄一份现成的“学生成绩分析”或者“电商销售数据分析”,交差了事。但我的经验是:这种通用模板恰恰是评分最低的。为什么?因为全班三四十号人,三分之一都交同一份“销量分析”,老师审美疲劳不说,答辩时一问数据从哪来的、为什么选这个字段、异常值怎么处理的,答不上来就直接露馅。所以我在拿到题目后,没有急着写代码,而是先花了两天时间想清楚三件事:作业的受众是谁、要证明什么能力、用什么数据最能体现这种能力。

这份作业的受众是信息课老师,评分维度大概率包括:数据处理的规范程度、统计方法的正确性、可视化的表达效果、报告的逻辑完整性。也就是说,光跑出一个结果远远不够,过程的可解释性才是高分关键。我最终选定的方向是“校园图书馆借阅行为分析”,用自己模拟生成的一个月借阅流水数据,从时间规律、类别偏好、借阅周期三个角度切入,完整走了一遍“数据采集—清洗—聚合—可视化—结论输出”的流程。

为什么选图书馆数据而不是电商数据?因为校园场景离我最近,字段语义我能讲清楚,比如“还书日期减去借书日期就是借阅天数”,这类业务解释在答辩时非常加分。而且图书馆数据的分析结论是有实际意义的——可以反推热门书目的备货策略和开放时间优化,这种“分析结果能落回业务场景”的感觉,是纯模板作业给不了的。

2. 数据准备与预处理:八成的功夫都花在这

2.1 造一份“真实感”十足的数据集

既然是作业,我手上并没有现成的图书馆数据库,直接抓公开数据又不一定贴合校园场景。所以我的方案是自己写脚本模拟生成一份高仿真借阅流水。这里有个关键认知需要纠正:模拟数据不代表随便造,造数据的严谨程度直接决定后续分析的可靠程度。

我的数据字段设计如下:

字段名含义示例模拟规则
borrow_id借阅流水号B202505001日期+当日序号
student_id学号S2021001五位编号
book_id图书编号B001共120本
category图书类别计算机/文学/历史九个大类
borrow_date借出日期2025-05-04集中在学期中段
return_date归还日期2025-05-18借期3~30天

模拟的时候我给自己定了三个原则:第一,时间范围固定为2025年4月1日到5月31日,共61天,这样就有一个完整的学期中段周期可以观察规律;第二,借阅量与日期强关联,比如每周一和考试周前一周借阅量明显上升,这就人为制造了“业务规律”,方便后面分析出有意义的结论;第三,数据里必须埋入脏数据,包括缺失值、重复记录、异常日期,否则清洗环节没东西可写。

生成数据我用的是Python的random模块加pandas批量拼接,总共生成600条左右的流水。造数据不是目标,造出能支撑多维度分析的“信号”才是目标。

2.2 缺失值、重复值和异常值的处理逻辑

数据生成之后,我特意在脚本里埋了这些脏数据:三条records缺失return_date,两条records完全重复,一条records的borrow_date晚于return_date,还有一条category写成了“计算机类”而不是统一的“计算机”。

这里分享一个处理脏数据的顺序方法论:先看结构再看值,先补缺失再剔异常。我是这样操作的:

import pandas as pd df = pd.read_csv('library_borrow.csv', parse_dates=['borrow_date', 'return_date']) print(df.info()) print(df.isnull().sum())

df.info()能快速暴露每列的非空数量和数据类型。缺失的return_date我采用“同类书籍平均借阅天数补齐”的策略,而不是直接删行。比如历史类图书的平均借期是12.6天,缺失的那条就用借用日期加上13天作为估计归还日。这样处理比直接drop更有技术含量,也更能体现对业务的理解。

遇到borrow_date晚于return_date的记录,我的判断是原始录入把日期填反了,处理方式是交换两个字段的值。重复行直接drop_duplicates()删掉就好。全部清洗完之后,我还会用df.describe()看一遍数值分布的合理性,比如借阅天数最小值为1、最大值为30,没有负数、没有超长周期,确认数据质量过关再进入下一步。

2.3 派生字段和聚合表构建

原始流水表只能回答“谁借了什么”,但分析需要的是“某天借了多少”“某类书被借了几次”这样的汇总视角。所以清洗完数据后,我紧接着做了三张聚合表:

  • 每日借阅量表:df.groupby('borrow_date').size(),用于观察时间趋势
  • 类别借阅排行表:df['category'].value_counts(),用于分析藏书偏好
  • 借阅周期分布表:计算(return_date - borrow_date).dt.days后生成新列,再分组统计

这里要强调:分析不是拿到数据就开始画图,而是先想清楚“要通过哪几张表回答哪些问题”,然后再用代码去实现。我把分析框架分成了三个问题:

  • 图书馆的借阅高峰出现在什么时候?
  • 哪类图书最受欢迎?男生女生的偏好差异大吗?
  • 平均借阅时长是多少?不同类别的借阅周期有没有显著区别?

框架一旦定下来,后面的代码和图表就都是为这三个问题服务的,报告自然也不会散。

3. 核心分析与可视化实现

3.1 借阅时间趋势:找到隐藏的“节奏感”

第一个分析目标是观察61天内的借阅量变化。直接画df.groupby('borrow_date').size().plot(kind='line')确实能出一条线,但这种最朴实的方式信息量很低。我做了两个优化维度:

第一个优化是按星期几分组。把所有借阅记录映射到“周一”到“周日”,然后求每天的平均借阅量,这样就能回答“一周里哪天最忙”的问题。我得到的结果是:周一和周五显著偏高,周六周日断崖式下降。道理其实很朴素——周一大家刚回学校,习惯性要去图书馆借一周的书;周五则是因为周末前有阅读计划;周末反而是借阅低谷。

第二个优化是周粒度趋势。把61天按自然周拆分,观察第1周到第9周的借阅量走势,可以看到明显的“两头高中间平稳”形态。第1周是开学初借阅高峰,大家新学期信心满满要读书;第8周骤然上升,因为期末考试临近。

这两个维度的代码逻辑都不复杂,但分析角度一分开,报告的层次立刻就不一样了。导师看到的不再是“一条线”,而是“两条不同尺度的规律线”。

3.2 热门类别与读者画像

类别偏好的分析我用的是柱状图和饼图结合的方式。堆叠柱状图能同时展示“类别的总借阅量”和“男女比例构成”,饼图则适合展示占比。运行出来的结果是:计算机类借阅占比28.6%排名第一,文学类24.2%紧随其后,历史类和经管类分列三四位。

这个结果其实能延伸出好几个有价值的结论。计算机类高居榜首,和我们学校理工科背景强、编程课程学分比重大有直接关系。文学类能排到第二,说明“课外轻阅读”的需求始终存在,图书馆的新书采购不能只盯着专业书。对比男女比例还能发现,借阅计算机类的以男同学为主,文学类的女同学比例更高,但两者的重叠用户也不少。

这里我没有做过于复杂的推荐算法,而是用了一个很直观的pd.crosstab交叉表。交叉表在处理“类别×性别”这种双维计数场景时,比groupby更清爽:

cross = pd.crosstab(df['category'], df['gender']) cross['合计'] = cross.sum(axis=1) cross = cross.sort_values('合计', ascending=False)

3.3 借阅周期分析:用箱线图替代均值

借阅周期就是我前面算出来的(return_date - borrow_date).dt.days。很多人在这一步会直接报一个“平均借阅天数14.2天”了事,但这样做有个很明显的坑:如果数据里有极端的31天长期借阅记录,均值会被拉高,掩盖掉大多数人的真实行为。

我的做法是画分组箱线图,每个类别一张图,同时观察中位数、四分位数和离群点。结果很有看点:计算机类的中位借阅周期是9天,明显短于文学类的16天。这说明工具书大家借回去是当手册查的,办完事就还;小说则被当枕头书,慢慢翻慢慢看。这种差异如果只报一个均值,是完全没有办法发现的。

再进一步,我还计算了逾期归还率,即借阅天数超过30天的记录占比。逾期率最高的是文学类,达到了6.8%,计算机类只有2.1%。这个数字对图书馆管理有直接价值:逾期率高的类别,应该适当延长借期或者引入临近到期提醒功能。

3.4 可视化细节:字体、配色与图表的“可读性”

可视化是这次作业里我踩坑最多的环节,集中说一下三个高频雷区。

第一个是Matplotlib中文乱码问题。默认字体不支持中文,图上一片方块。解决方式是在绘图前声明中文字体:

plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei'] plt.rcParams['axes.unicode_minus'] = False

第二个是图表配色问题。Matplotlib默认的颜色虽然不难看,但同质性很强,多图并列时容易视觉疲劳。我选了一个低饱和度的配色方案,主色用#4C72B0和#DD8452,辅助色用#55A868,整套图表放在报告里视觉风格统一,加分不少。

第三个是坐标轴信息密度。很多人画柱状图时横坐标不旋转、不加数值标签,导致柱子挤成一团。我的习惯是:类别超过五个的柱状图,横坐标标签必须旋转30度;关键数值必须直接标在柱顶上方,用plt.text()手动添加,而不是让读者自己拿尺子去对坐标轴读数。

4. 报告撰写与常见问题排查实录

4.1 作业报告的结构化表达框架

代码和分析做完了,作业只完成了一半,另一半是报告。我见过太多技术能力不差但因为报告混乱被扣分的案例。我自己用的报告框架是这样:

  • 项目背景与目标(1页):说明为什么选图书馆数据、想回答什么业务问题
  • 数据来源与处理说明(1.5页):贴上字段字典、清洗前后对比,强调数据质量
  • 分析过程与可视化(3页):三个分析模块各一页,每个模块按“图表+结论”的结构写
  • 结论与改进建议(0.5页):把分析结果反推回图书馆管理的具体动作

报告里有一条铁律我每次都会强调:每一张图表下面必须紧跟一段话,写清楚“这张图说明了什么、为什么会出现这种情况、下一步应该怎么做”。图表要是孤零零摆在那,没有解读,那和分析不做的效果差不多。

4.2 高频报错与排查思路

把我在写这份作业过程中实际遇到、以及帮同学排查过的高频问题整理成一个速查表,全部都是真金白银换来的经验:

报错/问题现象根因解决方案
KeyError: 'gender'列名拼写错误或字段被空格包裹先跑df.columns.tolist()核对列名
中文字体显示为方框Matplotlib缺少中文字体配置设置font.sans-serif后重启kernel
TypeError: unsupported operand type(s)日期列还是字符串类型pd.to_datetime()显式转换
数据透视表出现大量NaN分组键之间存在空组合pd.crosstab中加dropna=False
图表全挤在左下角x轴或y轴scale问题检查是否误用了plt.xlim(0, 10)硬限制

其中“日期列没转类型”这个问题在入门阶段特别常见。pd.read_csv读进来的日期默认是字符串,如果不转成datetime64类型,减法操作直接报错。排查思路也很简单:df.dtypes一查便知。如果这一列显示object,就说明该做类型转换了。

4.3 仿真答辩:把自己的作业当面试项目准备

既然这是一份能写进简历的作业,那么制作过程中就要养成分阶段记录的肌肉记忆。我每完成一个分析模块,就把代码、图表、结论三样东西归档到同一个文件夹,标注当天的修改时间。最后写报告的时候,直接调档拼接就行,不需要临时回想“我当时怎么处理的”。

更重要的一个习惯是:我做完之后会把自己当成面试官,对着报告逐段拷问一遍。数据哪里来的?模拟生成的,那模拟规则和真实场景的贴合依据是什么?缺失值为什么用均值补而不是删除?补完之后对后面的分析有没有引入偏差?这些问题的答案我都提前写在报告的脚注或附录里。老师答辩时一旦问到,我不至于慌。

我个人有个经验:老师提问的重点往往不在你的分析结论多惊艳,而在于你做每一步时有没有想过“为什么”。只要把“为什么”这个环节做扎实了,这份作业的分数下限就不会低。

5. 作业之外的延伸价值

做完这份“shuyibin的作业”之后,我最大的感受是:一份课程作业的价值,不取决于题目的新颖程度,而取决于你愿不愿意把每个环节往深处做一步。

改数据源就能变成一篇完整的数据分析项目;加一个协同过滤算法就能升级成图书推荐原型;把模拟数据替换成豆瓣的真实书目数据,结合爬虫技术,这份作业的含金量又会再上一个台阶。这些都是已经跑通的拓展方向,尤其是把模拟数据换成真实API数据的路径,非常建议感兴趣的同学接着试。

最后分享一个我在传输文件时踩过的小坑:交作业的时候,记得把数据和代码用zipfile压缩成一个压缩包再提交,千万别拿pd.to_csv直接存的裸表在微信里传来传去,格式全乱是小事,工程文件残缺被老师判定为抄袭才是大事。用压缩包保证“数据+代码+报告”三位一体,每份文件命名带上日期,这套习惯我从这份作业一直用到现在,省过不少麻烦。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询