
别再只盯着导航了手把手教你用Python和公开数据自己动手预测外卖送达时间每次点外卖时盯着手机屏幕上的倒计时你是否好奇过这个数字背后的计算逻辑作为数据科学入门者我们完全可以用Python搭建一个简易版的ETA预测系统。本文将带你从零开始用不到100行代码实现这个有趣的项目。1. 环境准备与数据获取首先需要配置Python环境。推荐使用Anaconda创建独立环境conda create -n eta_pred python3.8 conda activate eta_pred pip install pandas scikit-learn xgboost folium我们将使用模拟的公开数据集包含以下关键字段import pandas as pd data { restaurant_lat: [31.2304, 31.2356, 31.2289], # 餐厅纬度 restaurant_lng: [121.4737, 121.4782, 121.4715], # 餐厅经度 customer_lat: [31.2367, 31.2321, 31.2398], # 顾客纬度 customer_lng: [121.4812, 121.4756, 121.4834], # 顾客经度 weekday: [1, 5, 6], # 星期几(1-7) hour: [11, 18, 19], # 小时(0-23) weather: [sunny, rainy, cloudy], # 天气状况 actual_delivery_time: [28, 42, 35] # 实际配送时间(分钟) } df pd.DataFrame(data)提示实际项目中可接入真实API获取餐厅坐标本文为教学目的使用模拟数据2. 特征工程从原始数据到模型输入好的特征工程能显著提升模型效果。我们需要将原始数据转换为机器学习模型可理解的数值特征2.1 计算地理距离使用Haversine公式计算餐厅与顾客之间的直线距离from math import radians, sin, cos, sqrt, asin def haversine(lat1, lon1, lat2, lon2): # 将十进制度数转化为弧度 lon1, lat1, lon2, lat2 map(radians, [lon1, lat1, lon2, lat2]) # haversine公式 dlon lon2 - lon1 dlat lat2 - lat1 a sin(dlat/2)**2 cos(lat1) * cos(lat2) * sin(dlon/2)**2 c 2 * asin(sqrt(a)) r 6371 # 地球平均半径单位为公里 return c * r * 1000 # 转换为米2.2 时间特征处理将时间相关字段转换为更有意义的特征# 是否为用餐高峰时段 df[is_peak] df[hour].apply(lambda x: 1 if x in [11,12,17,18,19] else 0) # 是否为周末 df[is_weekend] df[weekday].apply(lambda x: 1 if x 6 else 0)2.3 天气特征编码使用独热编码处理分类变量weather_dummies pd.get_dummies(df[weather], prefixweather) df pd.concat([df, weather_dummies], axis1)最终特征矩阵应包含以下字段特征类型具体字段距离特征直线距离(米)时间特征小时、是否高峰、是否周末天气特征晴天、雨天、阴天3. 模型构建与训练我们将使用XGBoost回归模型它在结构化数据预测任务中表现优异3.1 数据准备from sklearn.model_selection import train_test_split # 计算距离特征 df[distance] df.apply(lambda x: haversine( x[restaurant_lat], x[restaurant_lng], x[customer_lat], x[customer_lng]), axis1) # 选择特征列 features [distance, hour, is_peak, is_weekend, weather_sunny, weather_rainy, weather_cloudy] X df[features] y df[actual_delivery_time] # 划分训练测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42)3.2 模型训练from xgboost import XGBRegressor from sklearn.metrics import mean_absolute_error # 初始化模型 model XGBRegressor( n_estimators100, max_depth5, learning_rate0.1, random_state42 ) # 训练模型 model.fit(X_train, y_train) # 评估模型 predictions model.predict(X_test) mae mean_absolute_error(y_test, predictions) print(f平均绝对误差: {mae:.2f}分钟)注意实际应用中MAE应控制在5分钟以内本文示例数据量较小仅供参考4. 模型优化与部署基础模型建立后我们可以通过以下方法进一步提升效果4.1 特征重要性分析import matplotlib.pyplot as plt from xgboost import plot_importance plt.figure(figsize(10, 6)) plot_importance(model) plt.show()常见的特征改进方向包括添加路网距离替代直线距离考虑餐厅出餐速度历史数据加入实时交通拥堵指数骑手接单量等压力因素4.2 简单部署示例使用Flask创建预测APIfrom flask import Flask, request, jsonify import pickle app Flask(__name__) # 加载保存的模型 with open(eta_model.pkl, rb) as f: model pickle.load(f) app.route(/predict, methods[POST]) def predict(): data request.json distance haversine(data[rest_lat], data[rest_lng], data[cust_lat], data[cust_lng]) features [ distance, data[hour], 1 if data[hour] in [11,12,17,18,19] else 0, 1 if data[weekday] 6 else 0, 1 if data[weather] sunny else 0, 1 if data[weather] rainy else 0, 1 if data[weather] cloudy else 0 ] eta model.predict([features])[0] return jsonify({eta_minutes: round(eta)}) if __name__ __main__: app.run(debugTrue)调用示例curl -X POST http://localhost:5000/predict \ -H Content-Type: application/json \ -d {rest_lat:31.23, rest_lng:121.47, cust_lat:31.24, cust_lng:121.48, hour:18, weekday:5, weather:rainy}5. 实际应用中的挑战在真实业务场景中ETA预测会遇到许多教学示例中未涉及的复杂情况动态路况处理使用实时交通API获取道路拥堵数据骑手行为建模不同骑手的行驶速度存在差异异常天气影响暴雨、大雪等极端天气的影响系数餐厅出餐延迟需要接入餐厅备餐状态数据一个实用的解决方案是采用混合模型架构原始特征 │ ▼ [基础预测模型] → 初始ETA │ ▼ [修正模块] ← 实时数据流 │ ▼ 最终ETA输出这种架构既保持了基础模型的稳定性又能通过修正模块适应实时变化。