用Python分析网约车司机月入2.5万的辛苦程度:从流水清洗到时薪计算
2026/9/1 11:53:00 网站建设 项目流程

这次我们不聊模型,不聊框架,而是聊一个更现实的数据分析问题:月入2.5万的网约车司机,到底有多辛苦?

与其靠感觉和情绪,不如直接拿数据说话。我整理了一套用 Python 分析网约车司机流水数据的思路,从订单流水清洗、成本扣减、有效工时统计,到净收入、时薪和疲劳指数计算,全流程用脚本跑通。这样可以客观拆解“月入2.5万”需要跑多少单、在线多少小时、承担多少成本,以及强度到底处在什么水平。

这篇文章适合三类读者:一是想入行网约车、需要理性评估收入的朋友;二是手头有司机流水数据、想做数据分析或写报告的技术人;三是想练手 pandas 数据清洗和可视化的人。整个项目不需要 GPU,普通电脑就能跑,环境简单,重点在数据思维和分析方法。

1. 核心能力速览

能力项说明
项目类型网约车司机收入与疲劳度数据分析脚本
输入数据订单流水 CSV,包含订单金额、平台抽成、里程、时长、起止时间、油电费等字段
核心功能计算净收入、有效工时、时薪、高峰期订单占比、长途订单占比、疲劳指数
运行环境Python 3.9+,Windows / macOS / Linux 均可
主要依赖pandas、matplotlib、flask(可选)
启动方式命令行执行脚本
是否支持 API可扩展为 Flask 接口,供其他工具调用
是否支持批量任务支持批量处理多个月份的流水文件
硬件要求CPU 即可,内存 4G 以上,无需 GPU
适合场景司机收入分析、出行平台数据研究、副业评估、数据分析教学

这里要强调一点:文中的示例数据是模拟生成的,目的是验证分析流程。真实场景中请使用司机自己的授权流水数据,并严格保护个人隐私。

2. 适用场景与使用边界

这个分析方案能解决一个核心问题:把“辛苦”变成可量化的指标。

比如,月流水 3 万,平台抽成 25%,油电成本 3000 元,违章和保养折损 1000 元,那么净收入大约是 18500 元。如果在线 400 小时,有效接单时间 220 小时,时薪只有 84 元左右。再叠加连续驾驶时间和深夜订单比例,就能看出这份工作真实的体力消耗程度。

但也要清楚这个方案的边界:

  • 它不适合分析平台规则、派单算法这类黑盒问题。我们不讨论平台抽成是否合理,只算司机到手收入。
  • 它不能精确计算所有成本,比如车辆折旧、保险、维修因为车型不同差异很大,只能根据输入数据估算。
  • 它需要相对干净的流水记录,如果司机只用语音接单、没有导出数据,需要手动整理。
  • 数据隐私需要格外注意。订单流水可能包含乘客上下车位置、手机号脱敏信息等,分析时要注意脱敏,分析完成后及时删除或加密保存。

如果涉及劳动关系、平台责任等法律问题,请咨询专业律师,本方案只提供技术分析手段。

3. 环境准备与前置条件

整个项目只需要 Python 环境和三个常用库。推荐使用 Python 3.9 以上版本,太老版本对 pandas 和 matplotlib 的兼容性不好。

3.1 安装 Python

如果电脑上还没有 Python,可以从官网下载安装,安装时记得勾选“Add Python to PATH”。装好后在命令行确认:

python --version

能看到版本号说明环境正常。

3.2 创建隔离环境(推荐)

不要直接往系统环境里装一堆包,建议为这个分析项目建一个独立虚拟环境:

mkdir driver-income-analysis cd driver-income-analysis python -m venv venv

Windows 激活:

venv\Scripts\activate

macOS / Linux 激活:

source venv/bin/activate

激活后终端提示符前面会出现(venv),说明已经进入虚拟环境。

3.3 安装依赖

pip install pandas matplotlib flask
  • pandas:用于表格数据清洗和聚合。
  • matplotlib:用于生成收入曲线、工时分布等图表。
  • flask:用于扩展 API 服务,如果暂时不需要,可以先不安装。

安装完成后,可以快速检查依赖版本:

import pandas as pd import matplotlib print(pd.__version__) print(matplotlib.__version__)

3.4 准备数据文件

在项目目录下创建一个data文件夹,用来存放流水 CSV。建议的字段格式如下:

字段名示例值说明
order_idD20250501120001订单编号
start_time2025-05-01 08:30:00接单开始时间
end_time2025-05-01 09:10:00订单结束时间
order_amount86.50乘客支付金额
platform_fee20.30平台抽成 / 信息服务费
distance_km25.8订单里程
wait_minutes12接驾等待+途中等待时间
fuel_cost18.00本单油费或电费(可按里程折算)
city上海所在城市

如果你的流水数据字段名不一样,可以在后面的脚本里做列名映射,不必强求一致。

4. 数据准备与模拟数据生成

在没有真实数据之前,我们先写一个脚本生成模拟数据,用来验证分析逻辑。模拟数据按一个月生成,假设司机每天出车 10 小时,中途随机休息,共产生约 600 条订单。

4.1 生成模拟流水

新建generate_sample_data.py

import csv import random from datetime import datetime, timedelta random.seed(42) output_path = "./data/sample_orders.csv" start_date = datetime(2025, 5, 1, 6, 0, 0) days = 30 hourly_density = 1.8 # 平均每小时订单数量 fields = [ "order_id", "start_time", "end_time", "order_amount", "platform_fee", "distance_km", "wait_minutes", "fuel_cost", "city" ] with open(output_path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=fields) writer.writeheader() order_seq = 0 for day in range(days): current = start_date + timedelta(days=day, hours=0) # 模拟每天早6点到晚12点开工 day_start = current.replace(hour=6, minute=0, second=0) day_end = current.replace(hour=23, minute=59, second=59) cursor = day_start while cursor < day_end: # 随机生成订单间隔 interval = random.randint(15, 60) cursor += timedelta(minutes=interval) if cursor > day_end: break order_seq += 1 order_id = f"D{day+1:02d}{order_seq:06d}" # 订单金额集中在 15~120 元 order_amount = round(random.uniform(15, 120), 2) # 平台抽成约 20%~30% platform_fee = round(order_amount * random.uniform(0.2, 0.3), 2) # 里程 2~40 公里 distance_km = round(random.uniform(2, 40), 1) # 等待时间 0~20 分钟 wait_minutes = random.randint(0, 20) # 油费按每公里0.5~0.8元估算 fuel_cost = round(distance_km * random.uniform(0.5, 0.8), 2) end_time = cursor + timedelta(minutes=random.randint(15, 60)) writer.writerow({ "order_id": order_id, "start_time": cursor.strftime("%Y-%m-%d %H:%M:%S"), "end_time": end_time.strftime("%Y-%m-%d %H:%M:%S"), "order_amount": order_amount, "platform_fee": platform_fee, "distance_km": distance_km, "wait_minutes": wait_minutes, "fuel_cost": fuel_cost, "city": "上海" }) cursor = end_time # 随机休息 0~30 分钟 cursor += timedelta(minutes=random.randint(0, 30)) print(f"生成完成: {output_path}")

这里的关键是生成一段看起来真实的数据,用来验证后续计算逻辑。运行方式:

python generate_sample_data.py

运行后会生成data/sample_orders.csv

4.2 认识数据格式

生成的模拟数据中,order_amount是乘客支付金额,platform_fee是平台抽成,实际到手流水应该是order_amount - platform_feefuel_cost是每单的能源成本,还没有算车辆折损和保险。

这个模拟数据刻意把订单集中在白天和晚上,没有生成凌晨 2 点到 5 点的订单,因为很多平台这时候单量少。如果你想分析熬夜强度,后续可以自己往数据里加凌晨订单。

5. 流水清洗与核心指标计算

拿到流水后,第一步是清洗。真实导出数据经常有缺失、重复、格式混乱问题,所以需要做这几件事:

  1. 解析时间列。
  2. 计算订单时长。
  3. 计算实际到手流水。
  4. 标记高峰时段。
  5. 按天聚合,统计在线时长、订单数、净收入等。

5.1 编写数据分析脚本

新建analyze_income.py

import pandas as pd import numpy as np DATA_PATH = "./data/sample_orders.csv" # 读取数据 df = pd.read_csv(DATA_PATH, encoding="utf-8-sig") print("原始数据量:", len(df)) print("\n前5行数据:") print(df.head()) # 清洗:去掉重复订单 df = df.drop_duplicates(subset=["order_id"]) # 清洗:删除关键字段为空的记录 df = df.dropna(subset=["order_amount", "platform_fee", "start_time", "end_time"]) # 时间列转换 df["start_time"] = pd.to_datetime(df["start_time"]) df["end_time"] = pd.to_datetime(df["end_time"]) # 计算订单时长(小时) df["order_hours"] = (df["end_time"] - df["start_time"]).dt.total_seconds() / 3600 # 计算实际到手流水 df["real_income"] = df["order_amount"] - df["platform_fee"] # 标记高峰时段:7-9点 和 17-20点 df["hour"] = df["start_time"].dt.hour df["is_peak"] = df["hour"].isin([7, 8, 9, 17, 18, 19, 20]) # 标记深夜时段:23点-凌晨4点 df["is_late_night"] = df["hour"].isin([23, 0, 1, 2, 3, 4]) # 计算成本:油费/电费 + 车辆折损估算(按每公里0.2元) df["depreciation_cost"] = df["distance_km"] * 0.2 df["total_cost"] = df["fuel_cost"] + df["depreciation_cost"] # 净收入 = 实际到手流水 - 本单直接成本 df["net_income"] = df["real_income"] - df["total_cost"] print("\n清洗后数据量:", len(df)) print("\n字段统计:") print(df[["order_amount", "real_income", "net_income", "distance_km"]].describe())

这个脚本会输出基础统计信息。这里用的是模拟数据,因此depreciation_cost只是一个估算值,真实使用时需要根据车型、购车价格和预期行驶里程调整。

5.2 按天聚合收入与工时

光看每单数据不够,需要按天汇总:

# 按天汇总 df["date"] = df["start_time"].dt.date day_stats = df.groupby("date").agg( total_order_amount=("order_amount", "sum"), total_platform_fee=("platform_fee", "sum"), total_real_income=("real_income", "sum"), total_net_income=("net_income", "sum"), total_distance=("distance_km", "sum"), order_count=("order_id", "count"), total_order_hours=("order_hours", "sum"), peak_order_count=("is_peak", "sum"), late_night_order_count=("is_late_night", "sum") ).reset_index() print("\n每日汇总:") print(day_stats.head(10))

5.3 计算月均指标

接着计算月均每日收入、时薪、高峰订单占比等:

monthly_summary = { "总自然天数": day_stats["date"].nunique(), "总订单数": day_stats["order_count"].sum(), "月总流水(不含抽成)": round(day_stats["total_order_amount"].sum(), 2), "月平台抽成": round(day_stats["total_platform_fee"].sum(), 2), "月实际到手流水": round(day_stats["total_real_income"].sum(), 2), "月净收入(扣除直接成本)": round(day_stats["total_net_income"].sum(), 2), "月行驶总里程": round(day_stats["total_distance"].sum(), 1), "月有效订单时长": round(day_stats["total_order_hours"].sum(), 2), "每日平均净收入": round(day_stats["total_net_income"].sum() / day_stats["date"].nunique(), 2), "单均净收入": round(day_stats["total_net_income"].sum() / day_stats["order_count"].sum(), 2), } print("\n===== 月度汇总 =====") for k, v in monthly_summary.items(): print(f"{k}: {v}")

如果模拟数据显示月净收入接近 25000 元,就可以继续看每日在线时长和疲劳指数,判断这个收入强度是否合理。

5.4 计算辛苦程度指标

辛苦程度不能只看收入,还要看时间投入和身体消耗。我们定义四个可量化指标:

指标计算方式含义
有效工时所有订单时长之和真正在开车服务乘客的时间
在线时长从第一单到最后一单的时间跨度司机“上班”的总时长,包含等单、休息
时薪净收入 / 有效工时每小时实际产出
高峰订单占比高峰订单数 / 总订单数高峰时段集中度,越高说明越要赶时间
深夜订单占比深夜订单数 / 总订单数熬夜程度,影响身体恢复
连续劳动隐患最长连续接单时间是否超过疲劳驾驶阈值

代码:

df_sorted = df.sort_values("start_time") # 按天计算在线时长:从第一天第一单到最后一单的时间跨度 df_sorted["date"] = df_sorted["start_time"].dt.date online_hours = {} for date, group in df_sorted.groupby("date"): duration_hours = (group["end_time"].max() - group["start_time"].min()).total_seconds() / 3600 online_hours[date] = duration_hours day_stats["online_hours"] = day_stats["date"].map(online_hours) # 有效接单率 day_stats["efficiency"] = day_stats["total_order_hours"] / day_stats["online_hours"] # 时薪 = 净收入 / 有效订单时长 day_stats["hourly_income"] = day_stats["total_net_income"] / day_stats["total_order_hours"] print("\n每日效率指标:") print(day_stats[["date", "total_net_income", "online_hours", "total_order_hours", "efficiency", "hourly_income"]].head(10)) # 综合疲劳指数:这里用 有效订单时长占比 + 高峰订单占比 + 深夜订单占比 做一个简单加权 peak_ratio = day_stats["peak_order_count"].sum() / day_stats["order_count"].sum() late_ratio = day_stats["late_night_order_count"].sum() / day_stats["order_count"].sum() efficiency_avg = day_stats["efficiency"].mean() fatigue_index = peak_ratio * 0.4 + late_ratio * 0.3 + (1 - efficiency_avg) * 0.3 print(f"\n高峰订单占比: {peak_ratio:.2%}") print(f"深夜订单占比: {late_ratio:.2%}") print(f"平均有效接单率: {efficiency_avg:.2%}") print(f"疲劳指数(0~1,越高越累): {fatigue_index:.3f}")

这里fatigue_index只是一个演示用的加权值,不是标准医学指标。你可以根据实际研究调整权重,把它当作观测趋势的参考。

6. 功能测试与效果验证

分析脚本写完后,要按下面的步骤验证效果,判断结果是否合理。

6.1 测试用例

测试项输入预期结果
数据读取模拟 CSV 文件读取成功,打印前 5 行
重复订单清洗手动加入重复 order_id重复行被删除
时间解析标准时间字符串能按天聚合
净收入计算已知订单金额、抽成、油费计算结果正确
在线时长计算某天首单/末单时间跨度大于有效订单时长
时薪计算净收入/有效工时输出合理数字

建议在跑完整分析前,先用一个只有 10 条订单的小文件测试。比如把sample_orders.csv复制成test_orders.csv,只保留前 10 行,修改analyze_income.py中的路径,跑通后再切回完整数据。

6.2 效果验证脚本

可以在分析脚本末尾加入结果校验逻辑:

# 简单校验 assert day_stats["total_net_income"].sum() > 0, "净收入异常" assert day_stats["online_hours"].between(4, 24).all(), "在线时长超出合理范围" print("\n数据校验通过,分析结果可用。")

如果断言失败,说明上游数据或计算逻辑有问题,需要回头检查。

6.3 从结果反推辛苦程度

假设运行后得到下面这样的结果(这里用模拟数据演示):

  • 月净收入:约 25000 元。
  • 月有效订单时长:约 300 小时。
  • 月在线时长:约 420 小时。
  • 时薪:约 83 元。
  • 高峰订单占比:约 35%。
  • 深夜订单占比:约 8%。

这意味着平均每天在线 14 小时,有效开车 10 小时,每天净收入 833 元。看起来月入确实可观,但每天 14 小时绑在车上,身体和精神消耗非常大。再结合通勤、吃饭、充电、上厕所的时间,实际出门时间可能接近 15 到 16 小时。这个强度就是很多人说“月入 2.5 万很辛苦”的原因。

如果希望更精确,可以把online_hours替换成司机实际打卡上下线时间,而不是用首末单时间近似。

7. 批量分析与接口 API 扩展

实际使用中,一个司机可能有多个月份的流水,或者你想把分析能力提供给其他工具使用,这时候可以做两件事:批量分析和 API 封装。

7.1 批量分析多个 CSV

将所有流水文件放在data/monthly/目录下,命名为2025-04.csv2025-05.csv,然后写一个循环:

import os import glob result_rows = [] for path in glob.glob("./data/monthly/*.csv"): month_df = pd.read_csv(path, encoding="utf-8-sig") # 复用前面的清洗和聚合逻辑 month_total_net = month_df["net_income"].sum() if "net_income" in month_df.columns else None result_rows.append({"file": os.path.basename(path), "net_income": month_total_net}) result_df = pd.DataFrame(result_rows) print(result_df)

更稳妥的方式是把前面清洗和计算逻辑封装成函数,而不是复制粘贴。

7.2 封装成 Flask API

把分析逻辑封装成 HTTP 接口后,前端页面、小程序或其他服务都能调用。下面是一个最小示例:

from flask import Flask, request, jsonify import pandas as pd import tempfile app = Flask(__name__) def analyze_file(file_path): df = pd.read_csv(file_path, encoding="utf-8-sig") df = df.drop_duplicates(subset=["order_id"]) df["start_time"] = pd.to_datetime(df["start_time"]) df["end_time"] = pd.to_datetime(df["end_time"]) df["real_income"] = df["order_amount"] - df["platform_fee"] df["net_income"] = df["real_income"] - df["fuel_cost"] total_net = round(df["net_income"].sum(), 2) total_orders = len(df) order_hours = round((df["end_time"] - df["start_time"]).dt.total_seconds().sum() / 3600, 2) return { "total_net_income": total_net, "total_orders": total_orders, "total_order_hours": order_hours } @app.route("/api/analyze", methods=["POST"]) def analyze(): file = request.files.get("file") if not file: return jsonify({"error": "no file uploaded"}), 400 tmp_path = tempfile.mktemp(suffix=".csv") file.save(tmp_path) result = analyze_file(tmp_path) os.remove(tmp_path) return jsonify(result) if __name__ == "__main__": app.run(host="127.0.0.1", port=8090)

启动服务:

python api_server.py

用 curl 测试:

curl -X POST -F "file=@./data/sample_orders.csv" http://127.0.0.1:8090/api/analyze

返回 JSON:

{ "total_orders": 612, "total_net_income": 29786.52, "total_order_hours": 312.58 }

需要注意:接口启动后只能在本机访问,如果要暴露到局域网,需要设置host="0.0.0.0",同时要考虑访问权限和文件上传大小限制。生产环境建议加 Token 校验和文件类型校验。

8. 常见问题与排查方法

问题现象可能原因排查方式解决方案
读取 CSV 报编码错误文件不是 UTF-8 编码用文本编辑器查看编码读取时指定encoding="gbk"encoding="utf-8-sig"
时间列无法解析时间格式不统一,比如有的带毫秒打印原始值pd.to_datetime(..., errors="coerce"),再检查空值
net_income出现负数成本字段大于到手流水检查油费单位、平台抽成是否重复校正字段含义,确认成本口径
在线时长超过 24 小时一天内首末单跨天或数据有误查看该天的首末单时间对跨天订单做日期归属修正
按天聚合时日期错误start_time被解析成带时间的对象打印dtypes使用df["start_time"].dt.date获取日期部分
运行脚本内存占用高数据量过大查看数据行数改用分块读取pd.read_csv(..., chunksize=10000)
API 上传文件失败服务未启动或端口被占用检查日志和端口杀掉占用进程或更换--port
分析结果和平台订单总金额不一致平台抽成字段是否包含优惠券、奖励对比平台账单以平台账单为准,调整字段计算口径

9. 资源占用、最佳实践与后续建议

9.1 资源占用观察

这个分析是纯 CPU 计算,典型的 30 天、600 条订单数据在普通笔记本上跑完不到 5 秒,内存占用约 200MB 左右。即使处理上万条订单,pandas 也足够应对。你可以在脚本里这样粗略观察耗时:

import time start = time.time() # 执行分析... print(f"耗时: {time.time() - start:.2f}s")

如果数据量达到几十万条,可以考虑用polarsspark,但对个人司机月度分析来说,pandas 完全够用。

9.2 最佳实践建议

  • 第一次运行时,先用模拟数据和极少量真实样本验证流程。
  • 数据文件统一放在data/目录,输出结果放在output/目录,保持目录结构清晰。
  • 写分析脚本时,把清洗、计算、导出封装成函数,方便复用。
  • 批量分析前先备份原始数据,不要直接在原文件上修改。
  • 涉及真实乘客位置和手机号等敏感信息,务必先脱敏再保存。
  • 如果要用这个分析结果做职业决策,建议结合多个平台的对账单、实际车辆损耗和相关行业数据一起看,不要只依赖单一指标。
  • API 服务如果没有公网需求,就只监听127.0.0.1,避免数据泄露。

9.3 后续可以扩展的方向

你可以把这里的数据分析方案继续升级成一个小型驾驶行为看板:接入司机每日打卡数据,自动计算连续驾驶时长;按周、按月生成收入走势图;结合天气和城市数据看单量波动;甚至用线性回归预测下一个月的收入区间。这样“月入2.5万有多辛苦”就不再是感觉,而是一套可以复用的数据模型。

如果你手里有真实的网约车流水数据,建议先按上面的流程跑一遍,把online_hoursefficiencyhourly_income这几个指标打印出来,然后对照每天的实际作息,你就能对收入与辛苦程度得到更清晰的判断。这个分析脚本也完全可以反过来用于其他自由职业者,比如外卖骑手、独立货运司机,只需要调整字段映射和成本模型。

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

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

立即咨询