尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Python与pandas的网约车订单流水分析实战

基于Python与pandas的网约车订单流水分析实战 一位网约车司机晒出自己一天跑的快车订单说跑满10个小时流水也很难超过200。评论区有人建议换专车有人怀疑计价规则也有人问是不是当天运气不好。这类讨论通常会停留在“感觉”层面但如果把订单记录拆开看这其实是一个很适合用数据分析解决的问题。快车订单客单价偏低接驾距离短出车时段的影响会被明显放大。只看“今天跑了多少钱”很难判断是订单太少、单均太低、空驶太多还是低峰时段占比太高。要回答“为什么10小时流水难上200”正确做法是先建立一张结构化的订单流水表再按小时、按订单类型、按空驶里程做聚合计算。下面会从字段设计开始逐步实现一个用 Python 读取 CSV、聚合流水、生成图表的本地分析工具。整个过程只需要 Python 3、pandas 和 matplotlib不涉及平台后台也不修改任何运营规则只分析司机自己手里的订单记录。1. 先把“流水难上200”翻译成数据指标1.1 流水不等于收入也不等于每公里产出这里说的“流水”通常是司机端显示的订单总额它不是净收入。净收入还要扣除平台服务费、油费或电费、车辆折旧和日常保养成本。即使10小时流水刚好200元扣掉成本后可能只剩120元左右如果这10小时里还有大量空驶里程实际收益会更低。所以在分析订单之前需要明确三个口径司机到手流水平台账单里显示的司机收入通常已扣除了信息服务费或平台抽成。平台服务费如果账单单独列出就单独记录。油电成本只能由司机根据本地能源价格估算不适合直接放进平台账单口径。建议前两个字段进入结构化数据第三个在计算阶段按里程单独估算。这样才能区分“流水低”和“净收入低”是两码事。1.2 关键指标在线时长、完单里程、空驶里程、每小时流水分析之前先统一一批指标。表格里这些指标会贯穿后续代码和报表指标计算方式作用在线时长最后一次收车时间减去第一次出车时间剔除主动休息反映司机实际占用时间完单时长各订单 end_time 减去 start_time 之和真正产生计费的时长空驶或等待时长订单间隔时间减去主动休息时间在接单但没有计费的消耗每小时流水统计时段流水除以对应在线时长判断时段产出高低单均流水流水除以订单数与客单价、订单里程有关每公里流水流水除以计费里程判断长单是否划算空驶率空驶里程除以计费里程加空驶里程空驶越多成本越高其中“每小时流水”是最直观的指标。一天流水200元、在线10小时平均每小时只有20元。但20元/小时到底是订单太少还是单均太低必须进一步拆开。1.3 用倒推顺序定位低流水原因不要直接下结论说“平台计价太低”先按下面的顺序检查在线时长是否真的有10小时。很多人把“早上出门到中午回家”当在线时长中间休息和吃饭占了两个小时真实出车时长并没有10小时。每小时单量是否足够。如果10小时只完成了8单平均每小时0.8单这种单量下流水很难高。单均流水是否偏低。快车订单单均低是常见特征但同一天不同时段差异很大。空驶里程是否偏高。高峰期容易连续派单空驶少平峰时段接一单要跑好几公里去接空驶会明显吃掉收入。休息和等单时间是否被计入。如果长时间等单任何收入指标都会被稀释。这个排查顺序决定了后续脚本要输出什么。第一步先聚合每小时流水再看订单数和空驶里程最后估算成本。2. 先设计订单流水表再写分析脚本2.1 数据来源与字段选择平台账单通常包含订单起始时间、结束时间、订单金额、里程等字段。如果账单不支持导出也可以从App的“我的行程”逐条复制或者用手机备忘录记录关键字段。对于手动记录字段要尽量少且稳定否则坚持不了几天。推荐至少包含以下字段字段类型示例作用order_idtext202501060712001订单唯一标识便于去重start_timedatetime2025-01-06 07:12:00计算订单时段和完单时长end_timedatetime2025-01-06 07:38:00与 start_time 配合使用typetext快车订单类型income_rmbnumber28.60司机到手流水platform_fee_rmbnumber3.10平台服务费没有可填0paid_distance_kmnumber12.5计费里程empty_distance_kmnumber3.2接驾或空驶里程估算值需标注wait_minutesnumber6订单内等待或接驾等待时间注意字段口径要统一金额用“元”里程用“公里”时间用“YYYY-MM-DD HH:MM:SS”。不要在同一个文件里混用“上午7:12”和“2025/01/06”这类格式。2.2 建立 CSV 示例数据下面是用于脚本演示的示例数据不是真实案例。它包含早高峰、平峰和晚高峰多个时段方便后续按小时聚合时看到不同产出。order_id,start_time,end_time,type,income_rmb,platform_fee_rmb,paid_distance_km,empty_distance_km,wait_minutes D001,2025-01-06 07:12:00,2025-01-06 07:38:00,快车,28.60,3.10,12.5,3.2,6 D002,2025-01-06 07:45:00,2025-01-06 08:02:00,快车,24.10,2.60,9.8,1.5,4 D003,2025-01-06 08:10:00,2025-01-06 08:40:00,快车,32.40,3.50,14.2,2.8,8 D004,2025-01-06 09:05:00,2025-01-06 09:25:00,快车,18.80,2.10,7.6,6.4,12 D005,2025-01-06 10:10:00,2025-01-06 10:31:00,快车,15.20,1.70,6.8,4.7,15 D006,2025-01-06 17:22:00,2025-01-06 17:50:00,快车,31.50,3.40,13.1,1.2,5 D007,2025-01-06 18:05:00,2025-01-06 18:35:00,快车,36.80,4.00,15.6,2.0,7 D008,2025-01-06 19:12:00,2025-01-06 19:30:00,快车,17.90,2.00,7.4,3.8,10把这8条记录保存为order_records.csv。示例数据只有8单主要用来演示处理流程真实分析要至少积累一周以上的完整订单记录。2.3 数据清洗的固定动作手动整理时最常出现的问题是订单类型不统一、时间列带空格、金额单位混乱。建议每次收车后做三个固定动作去掉单元格首尾空格统一订单类型为“快车 / 专车 / 顺风车”这类枚举值。时间统一为“YYYY-MM-DD HH:MM:SS”不要混用斜杠日期和中文日期。金额统一为元并保留两位小数不要有的填“28.6”有的填“28.60”。空驶里程缺失时填0但要单独标记“0表示未记录或估算”不要在字段里写文字说明。注意数据质量决定分析结论。如果日期格式不统一后面按小时聚合时会直接报错或漏掉大量订单。3. 用 Python 完成读取、清洗和按小时聚合3.1 准备运行环境在终端创建虚拟环境并安装依赖python -m venv .venv source .venv/bin/activate # Windows 下使用 .venv\Scripts\activate python -m pip install --upgrade pip pip install pandas matplotlib openpyxlopenpyxl 是为了后期把结果写回 Excel。如果只处理 CSV可以不安装。运行环境建议使用 Python 3.9 或更高版本。3.2 读取 CSV 并检查数据类型先写最基础的读取逻辑确认时间字段被解析为 datetime而不是字符串。import pandas as pd file_path order_records.csv df pd.read_csv(file_path, parse_dates[start_time, end_time]) print(df.dtypes) print(df.head())parse_dates会让 pandas 自动把字符串解析为datetime64类型。如果不做这一步后面的df[start_time].dt.hour会报错或者返回错误结果。3.3 主流程按出发小时聚合订单和流水下面这段脚本是整篇文章的核心。它读取 CSV按订单的出发小时分组计算每个小时内的订单数、流水、计费里程、空驶里程和等待时间并额外生成单均流水、每公里流水、空驶率三个派生指标。import pandas as pd import matplotlib.pyplot as plt from matplotlib import rcParams rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] rcParams[axes.unicode_minus] False def load_data(file_path: str) - pd.DataFrame: df pd.read_csv(file_path, parse_dates[start_time, end_time]) df[order_hour] df[start_time].dt.hour numeric_cols [ income_rmb, platform_fee_rmb, paid_distance_km, empty_distance_km, wait_minutes, ] for col in numeric_cols: df[col] pd.to_numeric(df[col], errorscoerce).fillna(0) return df def aggregate_hourly(df: pd.DataFrame) - pd.DataFrame: hourly ( df.groupby(order_hour) .agg( order_count(order_id, count), income_sum(income_rmb, sum), platform_fee_sum(platform_fee_rmb, sum), paid_distance_sum(paid_distance_km, sum), empty_distance_sum(empty_distance_km, sum), wait_minutes_sum(wait_minutes, sum), ) .reset_index() ) hourly[income_per_order] hourly[income_sum] / hourly[order_count] hourly[income_per_km] hourly[income_sum] / hourly[paid_distance_sum].replace(0, float(nan)) hourly[empty_ratio] hourly[empty_distance_sum] / ( hourly[paid_distance_sum] hourly[empty_distance_sum] 1e-9 ) return hourly.sort_values(order_hour) if __name__ __main__: df load_data(order_records.csv) hourly aggregate_hourly(df) pd.set_option(display.float_format, {:.2f}.format) print(hourly)关键点说明errorscoerce会把非法数字转成 NaN再用fillna(0)兜底防止手动录入的脏数据让聚合失败。income_per_km使用replace(0, float(nan))避免计费里程为0时出现除零错误。empty_ratio在分母加1e-9是一种容错写法防止某个小时计费里程和空驶里程都是0。按start_time的小时分组默认假设订单流水发生在“出发时段”。如果更关注完单时间可以把分组字段改成end_time.dt.hour。3.4 增加“时段类型”维度每小时聚合是基础但司机更常说的是“早高峰”“晚高峰”“平峰”。可以在订单数据上增加一个时段
返回列表