
简介这是一套面向数据分析初学者与进阶开发者的美团外卖数据分析系统项目源码围绕爬虫采集、数据存储、机器学习预测与可视化展示构建完整链路适合课程设计、毕业设计或数据分析实战练手。压缩包共26个文件约348KB以Python脚本为主体辅以Markdown说明文档、数据库文件、启动脚本与前端页面模板涵盖爬虫、分析、配置、建库及一键运行等模块结构清晰便于按需查阅。系统采用Scrapy采集数据支持MySQL、MongoDB存储并引入线性回归、LSTM进行订单趋势预测还包含最短路线推荐与随机组卷等扩展算法可迁移至交通、物流、金融等场景。目前已有166人学习下载读者可据此掌握从数据采集到预测建模的完整实现思路并参考论文大纲与核心代码快速复现项目。1. 外卖订单数据堆成山这套分析系统到底能拆出什么打开后台订单表、商家表、骑手轨迹表、评价表、优惠券核销表一字排开几十个字段横在眼前很多人第一反应是“先跑个 GMV 折线图交差”。可真做过外卖业务分析的人都知道GMV 只是最表层的一层皮真正值钱的是藏在订单时间戳、配送时长、满减门槛、复购间隔里的经营信号。美团外卖数据分析系统这类项目本质是把“订单流水”翻译成“可执行决策”的一套工程化流水线从多源数据接入、清洗建模到指标口径统一、可视化看板再到异常预警和归因分析。它适合三类人想从零搭一套本地分析环境的数据新手、需要给业务方交付日报周报的数据开发、以及想拿真实业务场景练手 SQL 与 Python 的在校同学。下面我按自己搭过的一套模拟项目 X 的路径把选型、建表、指标计算、排错和进阶技巧一层层拆开讲。2. 先定数据模型再写代码外卖分析系统的四层结构外卖场景的数据不像电商那么规整它有强时空属性一个订单同时关联用户、商家、骑手、时段、地理围栏。如果一上来就写查询后面改口径会改到怀疑人生。我一般先把系统拆成四层接入层、明细层、汇总层、应用层。接入层负责把 CSV、数据库增量、日志文件统一成带分区的时间序列明细层保留订单粒度的事实表和维度表汇总层按天、按商家、按商圈做预聚合应用层才是看板和预警。这套分层不是为了好看是为了让“口径变更”只影响一层。2.1 事实表与维度表怎么切分订单事实表的主键用“订单 ID 日期分区”字段分三类度量订单金额、配送费、优惠金额、实际支付、时间戳下单、接单、到店、送达、外键用户 ID、商家 ID、骑手 ID、商圈 ID。维度表至少要有商家维、用户维、骑手维、时间维、地理维。这里有个血泪经验不要把商家名称、品类直接冗余进事实表否则商家改名后历史数据全乱。用商家 ID 关联名称变更走缓慢变化维。-- 订单明细事实表按天分区 CREATE TABLE dwd_order_detail ( order_id VARCHAR(32) COMMENT 订单唯一标识, user_id VARCHAR(32) COMMENT 用户ID, merchant_id VARCHAR(32) COMMENT 商家ID, rider_id VARCHAR(32) COMMENT 骑手ID, order_amount DECIMAL(10,2) COMMENT 订单原价, discount_amount DECIMAL(10,2) COMMENT 优惠总额, pay_amount DECIMAL(10,2) COMMENT 实付金额, delivery_fee DECIMAL(10,2) COMMENT 配送费, order_time TIMESTAMP COMMENT 下单时间, accept_time TIMESTAMP COMMENT 接单时间, arrive_time TIMESTAMP COMMENT 送达时间, dt DATE COMMENT 分区日期 ) PARTITIONED BY (dt) STORED AS ORC;这段建表语句的关键在分区字段dt和金额字段的精度。金额统一用DECIMAL(10,2)别用FLOAT否则对账时会出现 0.01 的玄学误差。时间戳保留完整链路后面算“接单时长”“配送时长”都靠它们。如果数据源是 CSV接入层先做类型转换和空值填充再写入这张表。2.2 指标口径先写进配置表很多团队的口径散落在 SQL 注释和聊天记录里换个人就重算。我的做法是建一张指标字典表把指标名、计算公式、过滤条件、负责人写进去汇总层 SQL 从字典生成或至少人工对齐。指标名计算公式过滤条件更新频率有效订单数COUNT(order_id)状态已完成日实收 GMVSUM(pay_amount)状态已完成日客单价实收 GMV / 有效订单数同上日平均配送时长AVG(arrive_time - accept_time)配送时长0 且180 分钟日复购率复购用户数 / 下单用户数近 30 天周这张表看起来朴素但它能挡住 80% 的“你这个数怎么和我算的不一样”的扯皮。注意配送时长要设上下界骑手忘点送达会产生 20 小时的脏数据不设界均值直接失真。3. 用 Python 跑通清洗与指标计算的最小闭环模型定好之后先别急着上调度平台。用 Python 在本地跑通“读数据→清洗→算指标→落库”的闭环确认逻辑无误再工程化。我一般用 pandas 处理百万级以内的数据超过就换 Spark 或 DuckDB。下面这段代码演示从 CSV 读订单、清洗异常、计算日粒度指标并写入 SQLite 的完整流程。3.1 清洗脚本与异常订单识别import pandas as pd import sqlite3 # 1. 读取原始订单 CSV指定时间字段解析 df pd.read_csv(orders_raw.csv, parse_dates[order_time, accept_time, arrive_time]) # 2. 过滤无效订单金额为负、时间倒挂、配送时长异常 df df[df[pay_amount] 0] df df[df[arrive_time] df[accept_time]] df[delivery_minutes] (df[arrive_time] - df[accept_time]).dt.total_seconds() / 60 df df[(df[delivery_minutes] 0) (df[delivery_minutes] 180)] # 3. 按天聚合核心指标 df[dt] df[order_time].dt.date daily df.groupby(dt).agg( valid_orders(order_id, count), gmv(pay_amount, sum), avg_delivery(delivery_minutes, mean) ).reset_index() daily[avg_order_value] daily[gmv] / daily[valid_orders] # 4. 写入本地 SQLite供看板或后续分析读取 conn sqlite3.connect(waimai_analysis.db) daily.to_sql(ads_daily_metrics, conn, if_existsreplace, indexFalse) conn.close() print(daily.tail())逻辑说明第一步解析时间字段避免后续反复转换第二步做三层过滤金额、时间倒挂、配送时长各挡一类脏数据第三步按天聚合avg_order_value是派生指标不要提前算进原始表第四步落库用replace方便反复调试。参数上配送时长阈值 180 分钟是经验值不同城市可以调但一定要有上界。如果数据量到千万级把groupby换成 DuckDB 的 SQL 执行速度会快一个量级。3.2 商家维度汇总与排名日粒度指标只能看大盘业务方更关心“哪些商家在掉”。按商家汇总时要注意商家可能当天没有订单直接 groupby 会漏掉零单商家做排名时要用维度表左连接补零。# 商家日汇总保留零单商家 merchant_daily df.groupby([dt, merchant_id]).agg( orders(order_id, count), gmv(pay_amount, sum) ).reset_index() # 读取商家维度表左连接补全 merchants pd.read_csv(dim_merchant.csv) full merchants.merge(merchant_daily, onmerchant_id, howleft).fillna(0) full[rank_in_city] full.groupby(dt)[gmv].rank(ascendingFalse, methodmin)这里howleft保证维度表里的商家都出现fillna(0)把无订单填零。排名用methodmin处理并列避免出现跳号。如果要做“环比下滑预警”再按商家做 shift 计算昨日 GMV跌幅超过阈值就标记。4. 避坑与排查外卖数据里最容易翻车的五件事这套系统跑起来不难难的是数据对不上时能快速定位。下面五条是我踩过的坑按“现象→原因→解决”写遇到类似情况可以直接对照。4.1 指标对不上时区与分区边界现象看板显示昨日 GMV 比财务少了几万。原因订单时间戳存的是 UTC而业务口径按本地时间切天跨零点订单被分到前一天。解决接入层统一转成本地时区再落分区分区字段用转换后的日期别用原始时间戳直接切。4.2 配送时长均值忽高忽低现象某天平均配送时长突然从 35 分钟跳到 90 分钟。原因骑手端异常大量订单只有接单时间没有送达时间或者送达时间被写成默认值。解决清洗时强制过滤delivery_minutes在 0 到 180 之间同时监控“缺失送达时间订单占比”超过 1% 就告警。4.3 商家排名每天剧烈波动现象同一商家昨天排第 3今天掉到 200 名外。原因商家 ID 在源系统里存在大小写或前后空格差异groupby 时被当成两个商家。解决接入层对 ID 字段做strip().lower()标准化并在维度表加唯一约束。4.4 复购率算出来超过 100%现象复购率指标大于 1明显不合理。原因用户 ID 存在一人多号或者统计口径把“下单次数”当成了“复购用户数”。解决复购率分子是“近 30 天下单≥2 次的用户数”分母是“近 30 天下单用户数”两个集合都按去重后的用户 ID 算别用订单数直接除。4.5 调度任务跑完但数据没更新现象任务显示成功看板还是旧数据。原因写入用了append模式同一天数据重复写入看板取到了重复行导致聚合翻倍或者写入目标表分区没覆盖。解决汇总层统一用“按分区覆盖写”跑之前先删目标分区再写入同时加行数校验写入前后行数差异超过阈值就阻断。5. 从能跑到好用三个让分析系统真正落地的技巧系统能跑通只是及格线真正让业务方愿意天天用的是细节。第一个技巧是给指标加“数据质量标记”每条汇总记录带一个quality_flag字段标记当天数据是否完整、是否有异常过滤看板上用颜色区分避免拿脏数据做决策。第二个技巧是做“同环比自动归因”当 GMV 环比跌幅超过 5%自动拆解是订单数跌了还是客单价跌了再下钻到商圈和品类把归因结果写进日报而不是只丢一个数字。第三个技巧是保留原始明细的只读快照汇总层可以重算但明细层一旦清洗入库就不再修改出问题时能回溯到原始订单。# 数据质量标记与环比归因示例 daily[gmv_wow] daily[gmv].pct_change() # 周环比 daily[flag] normal daily.loc[daily[gmv_wow] -0.05, flag] gmv_drop daily.loc[daily[valid_orders] 0, flag] no_order # 归因跌幅日拆解订单数与客单价贡献 drop_days daily[daily[flag] gmv_drop] for _, row in drop_days.iterrows(): prev daily[daily[dt] row[dt]].iloc[-1] order_contrib (row[valid_orders] - prev[valid_orders]) * prev[avg_order_value] price_contrib (row[avg_order_value] - prev[avg_order_value]) * row[valid_orders] print(f{row[dt]} GMV跌幅中订单数贡献 {order_contrib:.0f}客单价贡献 {price_contrib:.0f})这段代码先算周环比并打标再对跌幅日做贡献拆解。order_contrib是订单数变化乘以昨日客单价price_contrib是客单价变化乘以今日订单数两者相加约等于 GMV 变化。实际使用时把结果写进日报模板业务方一眼就能看出是“没人下单”还是“下单了但买得少”。我自己的习惯是每周抽一天把汇总层和明细层随机抽 100 单手工对账对不上就查清洗逻辑这个习惯帮我挡掉过好几次口径漂移。希望帮到你。本文还有配套的精品资源点击获取