ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Python分析东京上班族一天开销:从CSV到可视化

用Python分析东京上班族一天开销:从CSV到可视化 在东京上一天班到底要花多少钱这个问题的答案不同人可能给出完全不同的数字差别不在谁更真实而在统计口径不同。通勤是按单程票计算还是按月票分摊午餐吃便利店饭团还是公司附近定食下班后的同事聚餐算不算工作日开销这些口径一旦不同最终数字就会差出好几倍。如果把这个问题当成一道技术题来处理步骤反而清晰了先定义数据模型再把逐笔消费记录成结构化数据然后用 Python 做聚合统计最后用图表把开销结构展示出来。这篇文章就按这个流程完成一次东京上班族工作周开销的分析覆盖 CSV 数据准备、pandas 清洗汇总、matplotlib 可视化和常见问题排查。项目面向三类读者想学 Python 数据分析并需要一个真实场景的新手准备做个人记账或成本管理小工具的开发人员以及想了解东京打工人日常开销结构的好奇读者。示例代码并不复杂但足够跑通一个完整分析链路之后替换成自己的数据也能直接使用。1. 先拆解“在东京上一天班要花多少钱”这个问题的数据画像1.1 一天开销由哪些部分组成很多人在估算东京上班一天的开销时第一反应是“交通费加午饭”。但实际记录几天后会发现一天的真实支出往往不止这两项。加班后的便利店晚餐、下午的咖啡、临时买伞、同事聚餐都会进入账单。为了让数据模型不乱通常把一天开销拆成五个类别。类别典型项目金额参考示例值是否相对固定transport上下班轨道交通、巴士、打车单程 200-400 日元往返 400-800 日元相对固定meal午餐、加班晚餐、便利店食品便利店 500-800食堂 600-900外食 900-1500弹性较大drink咖啡、茶、瓶装饮料便利店 100-200咖啡店 300-500弹性较大social同事聚餐、请客、小型应酬一次 1500-3000明显波动other打印、文具、雨伞、口罩等临时支出100-1000偶发这张表不是官方统计数据只是用于说明数据建模时的分类思路。每个人完全可以根据自己的消费习惯增加类别比如把“医疗”或“学习”单独拆出去。重点是分类要固定否则后续统计容易混乱。1.2 为什么采用“记录—聚合—可视化”而不是只看平均数只记住“一个月花了五万日元”没有太大价值。一旦超支无法定位是哪一类出了问题也无法判断哪些支出是刚性的、哪些是可以通过行为调整压缩的。逐笔记录、聚合统计、可视化展示能够回答三个具体问题钱花在哪里按类别汇总立刻能看到 transport、meal、social 各占多少。哪些是固定成本每天都要发生的通勤费、工作日午餐属于相对刚性的部分。哪些是弹性成本同事聚餐、临时买伞、加班便利店消费属于可以被理性调节的部分。这也是个人财务管理的基础逻辑先量化再归因最后做预算。1.3 示例项目要达成的目标本文不会做一款完整的记账 App而是完成一个最小可复现闭环准备一份结构化的开销流水文件。用 Python 读取并清洗数据。输出每日总开销和分类汇总。生成两张图一张观察单日开销结构一张观察一周趋势。最后把统计逻辑封装成函数方便进一步扩展。读者在自己电脑上复现时只需要准备 Python 环境和一份 CSV 文件。2. 数据模型设计把零散消费记录变成结构化数据2.1 为什么选择 CSV 作为最小数据载体记录开销的方式有很多手机记账 App、Excel、数据库都可以。对于这个小项目来说CSV 是最容易上手、也最容易检查的格式。它没有数据库依赖用记事本或 Excel 就能编辑Python 读取也简单。当数据量增大到上万条以后再迁移到 SQLite 或 MySQL 也不困难。这里要注意一个原则先确定数据结构再录数据。如果边录边改结构后面做统计时很容易出现字段对不上、格式不统一的问题。2.2 字段设计与类别枚举建议每条开销记录至少包含以下字段字段说明示例date消费日期格式统一为 YYYY-MM-DD2026-08-07category类别使用小写英文枚举transportitem具体消费项目名称jr_kawasaki_to_shinagawaamount_jpy金额单位日元使用纯数字220payment_method支付方式suicanote备注上班通勤category 必须固定成一组枚举值避免“交通”和“transport”混用。示例中使用的枚举如下CATEGORIES {transport, meal, drink, social, other}如果后续需要记录更多类型可以提前把枚举值设计成配置项而不是散落在数据文件里。2.3 准备一份示例开销记录为了让文件名便于识别下面把一份工作周流水保存为daily_cost_260808.csv。文件名里的 260808 可以理解为这套示例记录的版本号方便以后迭代不同的数据集。date,category,item,amount_jpy,payment_method,note 2026-08-07,transport,jr_kawasaki_to_shinagawa,220,suica,上班通勤 2026-08-07,transport,jr_shinagawa_to_kawasaki,220,suica,下班通勤 2026-08-07,meal,set_meal_at_restaurant,950,cash,公司附近定食 2026-08-07,drink,iced_coffee,180,suica,便利店咖啡 2026-08-07,social,after_work_drinking,2500,paypay,同事聚餐 2026-08-10,transport,jr_kawasaki_to_shinagawa,220,suica,上班通勤 2026-08-10,transport,jr_shinagawa_to_kawasaki,220,suica,下班通勤 2026-08-10,meal,soba_noodles,720,cash,午餐 2026-08-10,drink,bottled_tea,150,suica,自动贩卖机 2026-08-11,transport,jr_kawasaki_to_shinagawa,220,suica,上班通勤 2026-08-11,transport,jr_shinagawa_to_kawasaki,220,suica,下班通勤 2026-08-11,meal,company_cafeteria,650,employee_card,公司食堂 2026-08-11,drink,canned_coffee,160,suica,便利店 2026-08-11,meal,convenience_store_dinner,480,suica,加班后便利店晚餐 2026-08-12,transport,jr_kawasaki_to_shinagawa,220,suica,上班通勤 2026-08-12,transport,jr_shinagawa_to_kawasaki,220,suica,下班通勤 2026-08-12,meal,bento_shop,780,paypay,午餐便当 2026-08-12,drink,latte,420,cash,咖啡店 2026-08-13,transport,bus_to_station,180,suica,雨天坐公交到车站 2026-08-13,transport,jr_kawasaki_to_shinagawa,220,suica,上班通勤 2026-08-13,transport,jr_shinagawa_to_kawasaki,220,suica,下班通勤 2026-08-13,meal,curry_rice,880,cash,午餐 2026-08-13,drink,orange_juice,200,suica,便利店 2026-08-13,other,umbrella,800,cash,下雨临时买伞这份示例数据里8月7日因为有同事聚餐总开销明显偏高8月11日出现了加班后的便利店晚餐8月13日因为下雨多了公交和雨伞支出。这些细节会在后面的聚合统计中体现出来。2.4 运行代码前的数据检查清单在写任何代码之前先人工检查一遍 CSV能省去后面很多排查时间日期是否都在同一年的同一个工作周内。category 是否全部来自已定义的枚举集合。amount_jpy 是否都是纯数字没有千位分隔符和货币符号。CSV 的列名是否与代码读取时的字段名完全一致。是否有多余的空行或空格。注意CSV 中的金额不要写成2,500这种带逗号的形式否则 pandas 会把它当成字符串后续统计会报类型错误或直接拼接字符串。3. 用 Python 完成开销统计从原始流水到每日汇总3.1 环境准备与依赖安装示例以 Python 3.10 或更高版本为基础。建议在项目目录下创建虚拟环境避免依赖冲突mkdir tokyo-workday-cost cd tokyo-workday-cost python -m venv venv source venv/bin/activateWindows 下的激活命令是venv\Scripts\activate然后安装依赖pip install pandas matplotlibpandas负责数据处理matplotlib负责绘图。安装完成后可以用下面这行命令确认版本python -c import pandas; print(pandas.__version__)注意不同操作系统的虚拟环境激活命令不同Windows 下不要执行source venv/bin/activate要使用venv\Scripts\activate。3.2 读取 CSV 并做类型清洗读取部分使用 pandas 的read_csvimport pandas as pd df pd.read_csv(daily_cost_260808.csv, encodingutf-8) df[date] pd.to_datetime(df[date]) df[amount_jpy] pd.to_numeric(df[amount_jpy], errorscoerce)to_datetime会把字符串日期转成时间类型方便后面按周、按月聚合。to_numeric配合errorscoerce可以在出现异常值时把对应值变成 NaN而不是让脚本直接崩溃。3.3 按日期汇总和按类别汇总聚合的核心操作是groupbydaily_summary df.groupby(date)[amount_jpy].sum().reset_index() category_summary df.groupby([date, category])[amount_jpy].sum().reset_index()第一个分组得到每一天的总开销第二个分组得到每一天不同类别的开销。reset_index()可以把分组后的索引转回普通列方便后续打印或保存。3.4 完整可运行脚本下面把读取、清洗、汇总、导图整合成一个脚本analyze_cost.py。模块化后以后替换数据文件即可复用import pandas as pd import matplotlib.pyplot as plt CSV_FILE daily_cost_260808.csv FONT_LIST [ Hiragino Sans GB, SimHei, Noto Sans CJK JP, Microsoft YaHei, ] plt.rcParams[font.sans-serif] FONT_LIST plt.rcParams[axes.unicode_minus] False def load_and_clean(path): df pd.read_csv(path, encodingutf-8) df[date] pd.to_datetime(df[date]) df[amount_jpy] pd.to_numeric(df[amount_jpy], errorscoerce) df df.dropna(subset[amount_jpy]) return df def daily_total(df): return df.groupby(date)[amount_jpy].sum().reset_index() def daily_by_category(df): return df.groupby([date, category])[amount_jpy].sum().reset_index() def save_daily_chart(df_daily, outputdaily_total.png): df_daily_sorted df_daily.sort_values(date) plt.figure(figsize(8, 4)) plt.bar( df_daily_sorted[date].dt.strftime(%m-%d), df_daily_sorted[amount_jpy], ) plt.title(Tokyo Workday Daily Cost) plt.xlabel(Date) plt.ylabel(JPY) plt.tight_layout() plt.savefig(output, dpi150, bbox_inchestight) print(fsaved: {output}) if __name__ __main__: df load_and_clean(CSV_FILE) total daily_total(df) by_category daily_by_category(df) print( daily total ) print(total.to_string(indexFalse)) print( daily by category ) print(by_category.to_string(indexFalse)) save_daily_chart(total)这个脚本把职责拆成三块load_and_clean负责数据入口daily_total和daily_by_category负责聚合save_daily_chart负责输出图表。后面接入新数据时不需要改动核心逻辑。3.5 预期输出与结果解读运行脚本后控制台会输出每日汇总 daily total date amount_jpy 2026-08-07 4070 2026-08-10 1310 2026-08-11 1730 2026-08-12 1420 2026-08-13 1500分类汇总大致如下 daily by category date category amount_jpy 2026-08-07 transport 440 2026-08-07 meal 950 2026-08-07 drink 180 2026-08-07 social 2500 2026-08-10 transport 440 2026-08-10 meal 720 2026-08-10 drink 150 ...从这些数字能看出8月7日之所以最高是因为多了 2500 日元的同事聚餐8月13日没有社交支出但下车后的雨伞和公交让总额达到 1500 日元。这就是聚合统计的价值不仅知道花了多少还能知道为什么这一天的数字比平时高。4. 数据可视化单日结构图与一周趋势图4.1 用饼图观察单日开销结构饼图适合观察占比。选择 8月7日这份记录最丰富的数据可以画出当天的开销结构import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(daily_cost_260808.csv, encodingutf-8) df[date] pd.to_datetime(df[date]) day df[df[date] 2026-08-07] day_category day.groupby(category)[amount_jpy].sum() plt.figure(figsize(6, 6)) plt.pie( day_category.values, labelsday_category.index, autopct%.1f%%, startangle90, ) plt.title(2026-08-07 Cost Structure) plt.tight_layout() plt.savefig(cost_20260807_pie.png, dpi150, bbox_inchestight)autopct%.1f%%会在扇区上显示百分比startangle90控制起始角度让第一块从正上方开始。执行后当前目录会生成cost_20260807_pie.png。4.2 用柱状图对比一周每日开销柱状图适合对比每天的总量。前面analyze_cost.py里的save_daily_chart已经实现了这张图。运行脚本后生成daily_total.png横轴是日期纵轴是日元金额。如果想看某个类别在一周内的变化可以把聚合维度改成日期加类别category_pivot ( df.groupby([date, category])[amount_jpy] .sum() .unstack(fill_value0) ) category_pivot.plot(kindbar
返回列表