Python数据分析实战:从记账数据看消费趋势与结构

发布时间:2026/10/11 13:03:08

Python数据分析实战:从记账数据看消费趋势与结构 简介这份Python源码示例面向希望用编程管理个人财务的初学者与数据分析爱好者通过读取日常记账数据完成消费分类汇总与可视化呈现帮助使用者看清自己的消费方向。压缩包共3个文件包含1个py主程序、1个xlsx记账数据表和1个txt使用说明整体约20KB体积轻巧下载后即可对照说明运行调试。项目以Pandas完成数据清洗、聚合与分类用matplotlib、seaborn绘制柱状图、饼图和趋势线直观展示食品、交通、娱乐等类别的占比与时间变化脚本还可自动读取表格、按规则归类并定期更新结果减少手工统计。标签中涉及的爬虫与游戏数据思路也可迁移到电商订单抓取或玩家付费行为分析等场景。目前已有191人学习适合作为数据分析入门练手项目在实操中熟悉数据处理流程与可视化技巧。1. 记账三年还在用 Excel 拉透视表这套 Python 源码把消费方向拆到骨头里每个月月底打开记账 App 导出 CSV然后丢进 Excel 拉透视表看这个月餐饮花了多少、交通花了多少——这事我干了三年。直到某天想回答一个更实际的问题过去一年我的消费结构到底在往哪个方向漂移是外卖越点越多还是订阅服务在悄悄吃掉预算Excel 透视表给不了这个答案因为它只会做静态汇总不会做趋势归因。这套「数据分析和图标-可视化分析日常记账数据总结个人消费方向-Python源码示例」要解决的正是这个问题把记账 App 导出的流水明细用 Python 做清洗、分类、聚合再通过可视化把消费方向的变化趋势画出来。它适合两类人一是已经有一年以上记账数据、想从「记了」升级到「看懂了」的普通用户二是正在学 Python 数据分析与可视化实践、需要一个真实数据集练手的入门者。核心链路不复杂——pandas 做数据清洗和分组聚合matplotlib 和 seaborn 出图最后用一份可复用的脚本把整个流程固化下来。下面按实际落地顺序拆开讲。2. 从记账 App 导出到 pandas DataFrame数据清洗的四个关键动作2.1 先搞清楚你的记账数据长什么样不同记账 App 导出的 CSV 字段名差异很大但核心信息就那么几列交易时间、交易类型支出/收入/转账、金额、分类、备注。常见做法是先把 CSV 读进来用df.head()和df.info()看一眼实际结构再决定怎么处理。我一般会先做一次字段映射把不同 App 的列名统一成标准字段这样后续脚本换数据源时不用大改。import pandas as pd # 读取记账 App 导出的 CSV注意编码问题 df pd.read_csv(bill_export.csv, encodingutf-8-sig) # 先看前 5 行和字段类型 print(df.head()) print(df.info()) # 字段映射把不同 App 的列名统一 column_map { 交易时间: trade_time, 交易类型: trade_type, 金额(元): amount, 分类: category, 备注: note } df df.rename(columnscolumn_map) # 只保留支出记录转账和收入单独处理 df_expense df[df[trade_type] 支出].copy() print(f支出记录数{len(df_expense)})这段代码的逻辑很直接先原样读入确认字段名和数据类型再做重命名。encodingutf-8-sig是为了处理 Excel 导出 CSV 时带的 BOM 头不加这个参数第一列列名会多一个不可见字符后面按列名取值直接报 KeyError。trade_type 支出这个过滤条件要根据你实际 App 的取值来改有的 App 写的是「付款」或「消费」。2.2 金额列和日期列的清洗不能省金额列最常见的坑是带货币符号或千分位逗号比如「¥1,234.50」。pandas 读进来会当成 object 类型不做处理后面没法求和。日期列同理有的导出格式是「2024/1/5 14:30」有的是「2024-01-05」统一转成 datetime 类型才能按月分组。# 清洗金额列去掉货币符号和千分位逗号 df_expense[amount] ( df_expense[amount] .astype(str) .str.replace(¥, , regexFalse) .str.replace(,, , regexFalse) .astype(float) ) # 清洗日期列统一转成 datetime df_expense[trade_time] pd.to_datetime( df_expense[trade_time], formatmixed ) # 派生月份列和星期列后面分组要用 df_expense[month] df_expense[trade_time].dt.to_period(M) df_expense[weekday] df_expense[trade_time].dt.dayofweek # 检查清洗结果 print(df_expense[[trade_time, amount, month]].describe()) print(df_expense[amount].isna().sum(), 条金额缺失)formatmixed是 pandas 2.0 之后才支持的参数能自动推断每条记录的日期格式。如果你用的 pandas 版本较老需要手动指定 format 或者用pd.to_datetime(df[trade_time])让它自己猜。dt.to_period(M)生成的是 Period 类型按月分组聚合时比直接用字符串截取更可靠不会出现「2024-1」和「2024-01」被当成两个不同月份的情况。2.3 分类字段的二次归并记账 App 自带的分类往往太细比如「餐饮」下面分了「早餐」「午餐」「晚餐」「夜宵」「零食」「饮料」做消费方向分析时反而看不清结构。我一般会建一个映射表把细分类归并成 6 到 8 个大类比如「餐饮」「交通」「居住」「购物」「娱乐」「医疗」「订阅服务」「其他」。这个映射表建议单独存一个 CSV方便随时调整。# 分类归并映射表 category_group { 早餐: 餐饮, 午餐: 餐饮, 晚餐: 餐饮, 夜宵: 餐饮, 零食: 餐饮, 饮料: 餐饮, 地铁: 交通, 公交: 交通, 打车: 交通, 房租: 居住, 水电: 居住, 物业: 居住, 衣服: 购物, 日用品: 购物, 数码: 购物, 电影: 娱乐, 游戏: 娱乐, 旅行: 娱乐, 会员: 订阅服务, 订阅: 订阅服务, 云盘: 订阅服务, } df_expense[category_group] ( df_expense[category] .map(category_group) .fillna(其他) ) # 看一下归并后的分布 print(df_expense.groupby(category_group)[amount].agg([sum, count])).map()对没有匹配到的分类会返回 NaN用.fillna(其他)兜底。这里有个细节如果你的记账数据里分类名称有前后空格.map()会匹配不上建议先做一次df[category] df[category].str.strip()。归并后的分布用groupby加agg一次看完金额合计和笔数能快速判断哪些类别值得深入分析。3. 消费方向的可视化用 matplotlib 和 seaborn 画出趋势和结构3.1 月度消费趋势图折线图加滚动均值最基础的一张图是按月汇总的总支出折线图。但直接画原始月度数据往往波动很大看不出趋势我一般会叠加一条 3 个月滚动均值线把短期波动压下去让方向性变化浮出来。import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams[font.sans-serif] [SimHei] matplotlib.rcParams[axes.unicode_minus] False # 按月汇总总支出 monthly df_expense.groupby(month)[amount].sum().reset_index() monthly[month_str] monthly[month].astype(str) monthly[rolling_3m] monthly[amount].rolling(3).mean() fig, ax plt.subplots(figsize(12, 5)) ax.plot(monthly[month_str], monthly[amount], markero, label月度支出, alpha0.6) ax.plot(monthly[month_str], monthly[rolling_3m], linewidth2.5, label3月滚动均值, colorred) ax.set_xlabel(月份) ax.set_ylabel(支出金额元) ax.set_title(月度消费趋势) ax.legend() plt.xticks(rotation45) plt.tight_layout() plt.savefig(monthly_trend.png, dpi150) plt.show()rolling(3).mean()是滚动窗口计算窗口大小设 3 是因为记账数据按月看3 个月刚好覆盖一个季度既能平滑波动又不至于太滞后。matplotlib.rcParams[font.sans-serif] [SimHei]是解决中文显示方块的标配Windows 上用 SimHeimacOS 上换成 Arial Unicode MSLinux 上如果没有中文字体需要额外安装。axes.unicode_minus False是防止负号显示成方块虽然消费数据一般没有负数但加上不亏。3.2 消费结构堆叠图看各大类占比怎么变光看总支出不够还得看结构。堆叠面积图或者堆叠柱状图能把各大类每月的金额叠在一起一眼看出哪个类别在膨胀、哪个在收缩。# 按月和分类汇总 pivot ( df_expense .groupby([month, category_group])[amount] .sum() .unstack(fill_value0) ) pivot.index pivot.index.astype(str) # 堆叠面积图 fig, ax plt.subplots(figsize(12, 6)) pivot.plot.area(axax, stackedTrue, alpha0.8, colormaptab20) ax.set_xlabel(月份) ax.set_ylabel(支出金额元) ax.set_title(消费结构月度变化) ax.legend(locupper left, bbox_to_anchor(1, 1), fontsize9) plt.tight_layout() plt.savefig(category_structure.png, dpi150) plt.show().unstack(fill_value0)把长格式转成宽格式行是月份、列是分类缺失的组合填 0。plot.area(stackedTrue)直接出堆叠面积图colormaptab20在分类数不超过 20 时颜色区分度够用。如果分类超过 10 个建议只保留金额前 8 的类别剩下的归入「其他」否则图例会挤成一团。3.3 分类占比环形图加同比标注单月或者单年的分类占比用环形图展示比较直观但纯环形图只能看静态结构。我一般会在环形图旁边加一个同比变化标注比如「餐饮占比 32%较上季度 4%」这样一眼能看出消费方向在往哪偏。# 取最近一个完整月份的数据 latest_month df_expense[month].max() current df_expense[df_expense[month] latest_month] cat_sum current.groupby(category_group)[amount].sum().sort_values(ascendingFalse) fig, ax plt.subplots(figsize(8, 8)) wedges, texts, autotexts ax.pie( cat_sum.values, labelscat_sum.index, autopct%1.1f%%, startangle90, pctdistance0.85, wedgepropsdict(width0.4, edgecolorw) ) ax.set_title(f{latest_month} 消费结构) plt.tight_layout() plt.savefig(category_pie.png, dpi150) plt.show()wedgepropsdict(width0.4)是画环形图的关键参数width 控制环的宽度设 0.4 左右视觉上比较舒服。pctdistance0.85控制百分比文字离圆心的距离太近会叠在环上。startangle90让第一个扇区从 12 点方向开始符合阅读习惯。4. 避坑与排查记账数据分析里最容易翻车的五个地方4.1 金额求和结果对不上差了几块钱现象Python 算出来的月度总支出和记账 App 里显示的对不上差额不大但就是不一致。原因最常见的是退款记录没处理。很多 App 把退款记成一笔负数的支出或者单独的收入记录如果只过滤trade_type 支出退款那笔可能被漏掉或者被当成正常支出加了进去。另一个原因是金额列有隐藏的空格或不可见字符astype(float)之前没清干净。解决先检查退款记录的trade_type取值如果是「退款」就单独拿出来从总支出里减掉。金额列清洗时加一步.str.strip()再用pd.to_numeric(errorscoerce)代替astype(float)把无法转换的值变成 NaN 而不是直接报错然后检查 NaN 的数量和来源。4.2 中文标签显示成方块现象图表里所有中文都变成一个个小方块英文和数字正常。原因matplotlib 默认字体不含中文字形需要手动指定一个系统中存在的中文字体。解决Windows 上用SimHei或Microsoft YaHeimacOS 上用Arial Unicode MS或PingFang SCLinux 上先fc-list :langzh看有哪些中文字体可用然后设成对应的字体名。如果设了还是方块清一下 matplotlib 的字体缓存rm -rf ~/.cache/matplotlib。4.3 月份排序乱了10 月排在 2 月前面现象折线图的 x 轴月份顺序不对「2024-10」出现在「2024-2」前面。原因月份列是字符串类型时pandas 按字典序排列「1」开头的排在「2」前面导致 10 月、11 月、12 月的位置错乱。解决用dt.to_period(M)生成 Period 类型再转字符串Period 类型本身有正确的时序。或者在绘图前把月份列转成pd.Categorical并指定有序类别。最稳妥的做法是排序时用原始的 datetime 列绘图时再用格式化后的字符串做标签。4.4 分类归并后「其他」占比超过 40%现象归并后的分类分布里「其他」这一类金额占比特别高说明大量记录没匹配上映射表。原因映射表的 key 和实际数据里的分类名称不完全一致可能是多了空格、大小写不同、或者 App 更新后改了分类名称。解决先df[df[category_group] 其他][category].value_counts()看哪些分类落入了「其他」然后把这些分类名补进映射表。建议映射表用 CSV 维护而不是硬编码在脚本里改起来不用动代码。4.5 数据量大了之后脚本跑得慢现象一年以上的记账数据几千条记录时脚本秒出结果几万条时明显变慢。原因主要慢在groupby和apply上尤其是用apply做逐行处理时pandas 的循环开销很大。解决能用向量化操作就不用apply比如金额清洗用.str.replace()链式调用而不是apply(lambda x: ...)。分组聚合时尽量用内置的sum、mean等聚合函数避免自定义函数。如果数据量真的很大考虑用pd.read_csv时指定dtype和usecols减少内存占用和读取时间。5. 把分析脚本变成可复用的月度报告工具5.1 用配置文件管理分类映射和图表参数脚本写死分类映射和图表尺寸下个月换个数据源就得改代码。我一般会把映射表和图表参数抽到一个config.yaml或者config.json里脚本启动时读进来。这样调整分类归并规则或者换配色方案时不用碰 Python 代码。import json with open(config.json, r, encodingutf-8) as f: config json.load(f) category_group config[category_group] chart_config config[chart] # 用配置里的参数控制图表 figsize tuple(chart_config[figsize]) dpi chart_config[dpi]配置文件里放三块内容分类映射表、图表尺寸和 DPI、输出目录路径。这样同一套脚本可以跑不同人的记账数据只需要换配置文件和 CSV 就行。5.2 自动生成月度报告把关键指标算出来除了出图我还会在脚本最后算几个关键指标输出到一个文本文件或者直接打印出来。这些指标比图更直接地回答「消费方向有没有变化」这个问题。指标计算方式看什么月度总支出当月 amount 求和绝对值是否异常环比变化率(本月-上月)/上月短期波动3月滚动均值rolling(3).mean()趋势方向最大分类占比分类求和后取最大结构集中度订阅服务占比订阅类金额/总支出固定支出压力餐饮外卖占比餐饮类金额/总支出弹性支出变化这张表里的指标不需要全算选三四个你真正关心的就行。我一般会重点看「3月滚动均值」和「订阅服务占比」前者告诉你消费在涨还是在降后者告诉你固定支出有没有在悄悄膨胀。5.3 一个我踩过的坑别在脚本里直接覆盖原始数据早期我图省事清洗完直接df.to_csv(bill_export.csv)覆盖了原始文件结果某次分类映射改错了想回滚都回不去。后来改成清洗后的数据写到cleaned_bill.csv原始文件永远不动。这个习惯看起来多余但翻车一次就知道后悔药没处买。另外脚本里所有涉及金额的计算建议在最后统一做一次round(2)避免浮点数精度问题导致输出一堆123.45000000000002这种数字。虽然不影响分析结论但报告里看着难受。5.4 验证分析结果是否可信的两个方法第一个方法是交叉验证用 pandas 算出来的月度总支出和记账 App 里自带的月度统计对一下差额在几块钱以内说明清洗逻辑没问题。第二个方法是抽样检查随机抽 10 条记录手动核对金额、分类、日期是否和原始数据一致。这两个方法花不了几分钟但能避免在错误数据上做出一堆好看但没用的图。这套脚本我从最早的一百多行堆到一个文件里到现在拆成清洗、分析、可视化三个模块加一个配置文件前后改了七八版。最大的体会是记账数据分析的价值不在于图多好看而在于你能不能坚持每个月跑一次然后根据结果调整下个月的消费决策。工具只是工具用起来才有意义。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/11 13:03:08

AI UI设计工具有哪些?开发交付前先分清UI图、设计稿和前端代码

很多人聊“AI生成UI”,说的其实不是同一种东西。有人指的是一张UI图,方便讨论方向;有人要的是能继续改的设计稿;还有人想直接拿到能跑的前端代码。这三样交付出去,结果可差得远着呢。设计师看着UI图很满意,…

2026/10/11 13:03:08

参考文献中DOI如何正确书写?要不要带https://doi.org前缀?

先给急着出结果的读者一个结论:不存在一个放之四海而皆准的固定答案,但绝大多数正式场景下,带上https://doi.org/前缀更稳妥。这是我在审稿、投稿和给团队做培训时被问得最多的问题之一。同一条文献,A 作者在参考文献里只写10.101…

2026/10/11 16:38:25

Imatest SFRplus教程:从拍摄规范到MTF50指标解读与常见问题排查

简介:这份Imatest教程是一份面向相机评测人员、影像工程师及摄影爱好者的图像质量分析入门文档,重点解决如何看懂Imatest色彩、噪声与解像力测试图表。资源为单个doc文档,压缩包仅128KB,内容紧凑,适合快速查阅。文档依…

2026/10/11 16:38:25

微服务多级缓存架构设计

1 需求背景系统读多写少场景,大量热点字典、基础业务信息,请求全部打到 Redis,Redis CPU / 带宽压力高。 引入本地内存缓存,缩短访问链路;同时解决多实例本地缓存脏数据问题。非目标不用于强一致性业务(库存…

2026/10/11 16:38:25

安全日志分析实战:从撞库、Webshell到横向移动的攻击链还原方法

做安全运营这些年,我翻过的日志如果打印出来,大概能堆满一整面墙。网络攻击日志分析这件事,听起来很高大上,实际干起来往往是从一堆看似无关的字符里,把攻击者的行动轨迹一点点抠出来。你盯着几十万行访问记录&#xf…

2026/10/11 16:38:24

从SEO到GEO:AI时代企业为什么需要建立品牌知识资产?

随着生成式AI快速进入企业营销体系,传统的搜索流量逻辑正在出现新的变化。 世界广告主联合会(WFA)最新调研显示,96%的受访大型品牌已经在使用生成式AI或智能体AI。 对于企业数字化团队而言,一个值得关注的问题是&#…

2026/10/11 16:33:24

分步傅里叶法解非线性薛定谔方程:光纤脉冲传播仿真源码详解

简介:本资源是一份面向光学工程、非线性光纤通信及计算物理方向学习者与研究者的MATLAB源代码解析文档,聚焦分步傅里叶法求解非线性薛定谔方程(NLS)这一核心数值方法。文档完整呈现了从理论建模、参数设置、脉冲初始化&#xff08…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑