Files
mcm-mfp/task1/README.md
2026-01-19 10:23:51 +08:00

588 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Task 1: 2021年MFP访问计划——需求驱动的频次分配与日历排程
## 摘要
本文提出一套**因果逻辑清晰、闭环可审计**的2021年MFP访问频次分配与日历排程方案。核心创新点
1. **截断回归修正**识别到9个站点历史服务量 $\mu_i > 250$最高396.6),说明高需求站点的观测数据被容量截断,采用截断正态模型恢复真实需求 $\tilde{\mu}_i$
2. **质量加权有效性**:考虑高服务量下每户分配食物减少的质量折扣
3. **满足率公平性**:以"年度服务量/真实需求"的均等程度衡量公平,而非简单的访问次数均等
方案遵循四阶段结构:需求估计 → 频次分配 → 效果评估 → 日历排程。
---
## 整体流程图
### Mermaid版本GitHub可渲染
```mermaid
flowchart TB
subgraph INPUT["数据输入"]
A[data.xlsx<br/>70站点数据]
end
subgraph TASK1["TASK 1: 基础排程"]
direction TB
subgraph CORE["核心流程 ✅ 已完成"]
B1[01_clean.py<br/>数据清洗]
B2[02_demand_correction.py<br/>截断回归修正]
B3[03_allocate.py<br/>Hamilton分配]
B4[04_evaluate.py<br/>指标计算]
B5[05_schedule.py<br/>日历排程]
B1 --> B2 --> B3 --> B4
B3 --> B5
end
subgraph VALIDATE["结果验证 ⏳ 待完成"]
V1[06_validate.py<br/>约束检验]
V2[07_backtest.py<br/>历史回测]
end
subgraph SENSITIVITY["敏感性分析 ⏳ 待完成"]
S1[08_sensitivity.py<br/>参数扫描]
end
subgraph VISUAL["可视化 ⏳ 待完成"]
P1[09_visualize.py<br/>图表生成]
end
CORE --> VALIDATE
CORE --> SENSITIVITY
VALIDATE --> VISUAL
SENSITIVITY --> VISUAL
end
subgraph TASK2["TASK 2: 天气响应 ⏳"]
C1[2a或2b方案]
end
subgraph TASK3["TASK 3: 双站点同车 ⏳"]
D1[共生站点优化]
end
subgraph TASK4["TASK 4: Executive Summary ⏳"]
E1[1页执行摘要]
end
A --> B1
VISUAL --> TASK2
VISUAL --> TASK3
TASK2 --> TASK4
TASK3 --> TASK4
style CORE fill:#90EE90
style VALIDATE fill:#FFE4B5
style SENSITIVITY fill:#FFE4B5
style VISUAL fill:#FFE4B5
```
### ASCII版本详细
```
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ TASK 1 完整流程 │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────────┐ │
│ │ 核心流程 [已完成 ✓] │ │
│ │ │ │
│ │ data.xlsx ──▶ 01_clean ──▶ 02_demand ──▶ 03_allocate ──▶ 04_evaluate │ │
│ │ correction │ │ │
│ │ ▼ │ │
│ │ 05_schedule │ │
│ └─────────────────────────────────────────────────────────────────────────────────┘ │
│ │ │ │
│ ┌─────────────┴────────────┬───────────┴───────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ 结果验证 [待完成 ⏳] │ │ 敏感性分析 [待完成 ⏳] │ │ 可视化 [待完成 ⏳] │ │
│ │ │ │ │ │ │ │
│ │ 06_validate.py │ │ 08_sensitivity.py │ │ 09_visualize.py │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │• 约束满足检验 │ │ │ │• C ∈ {350,400,450}│ │ │ │• 站点地图(需求) │ │ │
│ │ │ - Σk=730? │ │ │ │• p阈值扫描 │ │ │ │• k分布直方图 │ │ │
│ │ │ - k≥1? │ │ │ │• C̄ ∈ {200,250,300}│ │ │ │• E-F权衡曲线 │ │ │
│ │ │ - 每日2站点? │ │ │ │ │ │ │ │• 日历热力图 │ │ │
│ │ └──────────────────┘ │ │ └──────────────────┘ │ │ │• 间隔分布箱线图 │ │ │
│ │ │ │ │ │ └──────────────────┘ │ │
│ │ 07_backtest.py │ │ 输出: │ │ │ │
│ │ ┌──────────────────┐ │ │ • 参数-指标表 │ │ 输出: │ │
│ │ │• 2019数据回测 │ │ │ • Pareto前沿图 │ │ • figures/*.png │ │
│ │ │• 预测vs实际对比 │ │ │ │ │ • 论文插图 │ │
│ │ │• 残差分析 │ │ │ │ │ │ │
│ │ └──────────────────┘ │ │ │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ └──────────────────────┘ │
│ │ │
└────────────────────────────────────────┼───────────────────────────────────────────────┘
┌────────────────────┴────────────────────┐
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
│ TASK 2: 天气响应 [待完成] │ │ TASK 3: 双站点 [待完成] │
└──────────────────────────┘ └──────────────────────────┘
│ │
└────────────────┬────────────────────────┘
┌──────────────────────────┐
│ TASK 4: 执行摘要 [待完成] │
└──────────────────────────┘
```
### 当前进度与待完成事项
| 模块 | 状态 | 脚本 | 说明 |
|------|------|------|------|
| 数据清洗 | ✅ 完成 | `01_clean.py` | 字段标准化 |
| 需求修正 | ✅ 完成 | `02_demand_correction.py` | 截断回归 |
| 频次分配 | ✅ 完成 | `03_allocate.py` | Hamilton方法 |
| 指标计算 | ✅ 完成 | `04_evaluate.py` | E1,E2,F1,F2 |
| 日历排程 | ✅ 完成 | `05_schedule.py` | 贪心+局部优化 |
| **约束验证** | ⏳ 待完成 | `06_validate.py` | 硬约束检验 |
| **历史回测** | ⏳ 待完成 | `07_backtest.py` | 模型有效性 |
| **敏感性分析** | ⏳ 待完成 | `08_sensitivity.py` | 参数稳健性 |
| **可视化** | ⏳ 待完成 | `09_visualize.py` | 论文图表 |
---
## 运行结果摘要
| 指标 | 推荐方案 | 均匀分配 | 2019缩放 | 变化 |
|------|---------|---------|---------|------|
| E1 (总服务量) | **140,121** | 104,797 | 104,071 | +34.6% |
| E2 (质量加权) | **131,673** | 101,309 | 100,264 | +31.3% |
| F1 (Gini系数) | 0.314 | **0.026** | 0.092 | 公平性降低 |
| F2 (最低满足率) | 2.0 | **8.4** | 5.0 | - |
**核心发现**按需求比例分配可提升34.6%的总服务量,但存在有效性-公平性权衡。
---
## 1. 问题形式化
### 1.1 输入数据
| 字段 | 符号 | 说明 |
|------|------|------|
| 位置 | $(lat_i, lon_i)$ | 经纬度坐标 |
| 2019年访问次数 | $v_i$ | 历史频次总计722次 |
| 单次服务人数均值 | $\mu_i$ | 观测均值,范围[17.2, 396.6] |
| 单次服务人数标准差 | $\sigma_i$ | 观测波动,范围[2.2, 93.5] |
### 1.2 运营参数
| 参数 | 值 | 来源 |
|------|-----|------|
| 卡车物理运力 | 15,000 lbs | 题面明确 |
| 典型服务户数 | [200, 250] | 题面"typical" |
| 每日可派车次 | 2 | 题面 |
| 年度总访问次数 $N$ | 730 | $365 \times 2$ |
### 1.3 硬约束
$$\sum_{i=1}^{70} k_i = 730 \tag{C1: 资源约束}$$
$$k_i \geq 1 \tag{C2: 覆盖约束}$$
$$|\text{sites on day } t| = 2 \tag{C3: 每日容量}$$
---
## 2. 阶段一:真实需求估计(截断回归)
### 2.1 问题本质:为什么需要修正?
**底层逻辑**
题面说"typical visit serves 200-250 families",但数据中 $\mu_{max} = 396.6$。这说明:
1. **200-250不是硬上限**:高需求站点可以服务更多家庭
2. **存在隐性容量约束**卡车运力15000磅 ÷ 每户约38-75磅 ≈ 200-400户
3. **观测被截断**:当真实需求 > 容量时,只能观测到容量值
```
真实需求分布: 观测到的分布:
│ │
│ ╱╲ │ ╱╲
╲ │ ╲▓▓▓ ← 被截断部分
╲ │
╲ │
├───────────── ├───────┤
0 μ̃ C 0 μ C
```
**因此**:直接用 $\mu_i$ 会**系统性低估**高需求站点的真实需求。
### 2.2 截断概率的推导
**假设**:真实需求 $\tilde{D}_i \sim \mathcal{N}(\tilde{\mu}_i, \sigma_i^2)$
**推导**:截断概率 = 真实需求超过容量C的概率
$$p_i^{trunc} = P(\tilde{D}_i > C) = P\left(Z > \frac{C - \tilde{\mu}_i}{\sigma_i}\right) = 1 - \Phi\left(\frac{C - \tilde{\mu}_i}{\sigma_i}\right)$$
**近似**:由于我们不知道 $\tilde{\mu}_i$,用观测值 $\mu_i$ 近似:
$$p_i^{trunc} \approx 1 - \Phi\left(\frac{C - \mu_i}{\sigma_i}\right)$$
**物理意义**$p_i^{trunc}$ 越大,说明该站点越可能被截断,需要更大的修正。
### 2.3 修正公式的推导
**截断正态的条件期望**Mills ratio
对于 $X \sim \mathcal{N}(\mu, \sigma^2)$,有:
$$E[X | X > c] = \mu + \sigma \cdot \frac{\phi\left(\frac{c-\mu}{\sigma}\right)}{1 - \Phi\left(\frac{c-\mu}{\sigma}\right)}$$
**简化近似**
当截断概率 $p^{trunc}$ 较小时,修正量近似与 $p^{trunc}$ 线性相关:
$$\tilde{\mu}_i \approx \mu_i \cdot (1 + \alpha \cdot p_i^{trunc})$$
取 $\alpha = 0.4$(经验系数,可通过敏感性分析调整)。
**分段处理的原因**
- 当 $p^{trunc} < 0.02$:截断概率很低,修正量可忽略
- 当 $p^{trunc} \geq 0.02$:存在显著截断,需要修正
### 2.4 实际修正结果
| site_id | 站点名称 | $\mu$ | $\sigma$ | $p^{trunc}$ | $\tilde{\mu}$ | 修正幅度 |
|---------|---------|-------|----------|-------------|---------------|----------|
| 66 | MFP Waverly | 396.6 | 51.9 | 0.474 | 471.9 | +19.0% |
| 2 | MFP Avoca | 314.6 | 57.3 | 0.068 | 323.2 | +2.7% |
| 13 | MFP College TC3 | 261.5 | 92.0 | 0.066 | 268.4 | +2.6% |
| 17 | MFP Endwell | 285.2 | 60.8 | 0.030 | 288.6 | +1.2% |
| 30 | MFP Redeemer | 230.6 | 93.5 | 0.035 | 233.8 | +1.4% |
---
## 3. 阶段二:频次分配模型
### 3.1 分配原则的底层逻辑
**题面要求**"frequency informed by total demand in surrounding communities"
**问题**:什么是"周边社区需求"
**两种理解**
1. ❌ 用空间核平滑估计(循环论证:用供给估计需求)
2. ✅ 每个站点本身就服务其周边社区,$\tilde{\mu}_i$ 就是需求代理
**我们的选择**:直接用修正后的 $\tilde{\mu}_i$ 作为"周边社区需求"的代理。
**为什么按比例分配?**
假设目标是**最大化总服务量**且**保证公平**
- 最大化 $\sum k_i \mu_i$ subject to $\sum k_i = N$
- 拉格朗日条件:$\frac{\partial}{\partial k_i}(k_i \mu_i - \lambda k_i) = \mu_i - \lambda = 0$
这说明最优解是 $k_i \propto \mu_i$(或 $\tilde{\mu}_i$)。
### 3.2 为什么用Hamilton方法
**问题**:按比例分配得到的是实数,需要转换为整数。
**Hamilton方法最大余数法的优点**
1. **保证总和**$\sum k_i = N$ 严格成立
2. **公平性**余数大的站点优先获得额外1次
3. **可解释**:政治选举中广泛使用,逻辑透明
**替代方法对比**
| 方法 | 优点 | 缺点 |
|------|------|------|
| 四舍五入 | 简单 | 总和可能不等于N |
| 向下取整+贪心 | 简单 | 可能不公平 |
| Hamilton | 公平、总和准确 | 稍复杂 |
| 整数规划 | 可加约束 | 过于复杂 |
### 3.3 分配结果验证
- 总访问次数:$\sum k_i = 730$ ✓
- 访问次数范围:$[2, 32]$
- 覆盖约束:$\min k_i = 2 \geq 1$ ✓
---
## 4. 阶段三:效果评估指标
### 4.1 有效性指标的设计逻辑
**E1原始总服务量**
$$E_1 = \sum_{i=1}^{70} k_i \cdot \mu_i$$
**问题**E1假设服务量与访问次数线性增长但忽略了**质量下降**。
**E2质量加权服务量**
**底层逻辑**:当 $\mu_i > 250$ 时,每户获得的食物量下降。
$$\text{每户食物量} = \frac{15000 \text{ lbs}}{\mu_i}$$
当 $\mu_i = 250$ 时每户60磅典型值
当 $\mu_i = 400$ 时每户37.5磅(低于典型)。
**质量折扣因子**
$$q(\mu) = \min\left(1, \frac{250}{\mu}\right) = \begin{cases} 1 & \mu \leq 250 \\ \frac{250}{\mu} & \mu > 250 \end{cases}$$
**物理意义**$q(\mu)$ 表示"相对于典型服务质量的比例"。
### 4.2 公平性指标的设计逻辑
**为什么不用"访问次数相等"衡量公平?**
题面说"are some served much better than others"——关键词是**served**,不是**visited**。
需求100的站点访问10次 vs 需求200的站点访问10次 → 后者服务不足。
**满足率的定义**
$$r_i = \frac{\text{年度服务量}}{\text{年度需求}} = \frac{k_i \cdot \mu_i}{\tilde{\mu}_i}$$
**为什么用Gini系数**
- Gini系数是衡量分布不均等的标准指标
- 取值[0,1]0表示完全均等1表示完全不均等
- 易于解释:收入分配、资源分配广泛使用
### 4.3 评估结果
| 方案 | E1 | E2 | F1 (Gini) | F2 (min r) |
|------|-----|-----|-----------|------------|
| **推荐方案** | **140,121** | **131,673** | 0.314 | 2.00 |
| 均匀分配 | 104,797 | 101,309 | **0.026** | **8.41** |
| 2019缩放 | 104,071 | 100,264 | 0.092 | 5.00 |
**有效性-公平性权衡**
```
F1 (Gini, 越小越公平)
0.4 │ ● 推荐方案 (E1=140k)
0.3 │
0.2 │
0.1 │ ● 2019缩放 (E1=104k)
0.0 │● 均匀分配 (E1=105k)
└────────────────────────────▶ E1 (总服务量)
100k 120k 140k
```
---
## 5. 阶段四:日历排程
### 5.1 排程目标的底层逻辑
**为什么要均匀分布访问日期?**
题面说"schedule published months in advance to help clients plan"。
如果某站点访问间隔不均匀1月5次2月0次客户难以规划。
**理想间隔**
$$\Delta_i^* = \frac{365}{k_i}$$
例如:$k_i = 12$ 次/年 → 理想间隔 ≈ 30天/次
### 5.2 贪心算法的设计逻辑
**为什么不用整数规划?**
730个访问事件 × 365天 = 26万个0-1变量计算复杂度过高。
**贪心策略**
1. 为每个访问事件计算"理想日期"
2. 按理想日期排序
3. 依次分配到最近的可用槽位
**为什么有效?**
- 理想日期已经考虑了均匀分布
- 按顺序分配避免了冲突
- 局部优化可进一步改善
### 5.3 排程结果
- 365天全部满载 ✓
- 平均间隔55.4天
- 最大间隔179天低频站点
---
## 6. 待完成:结果验证
### 6.1 约束满足检验 (`06_validate.py`)
| 检验项 | 公式 | 预期结果 |
|--------|------|---------|
| 资源约束 | $\sum k_i = 730$ | 通过 |
| 覆盖约束 | $\min k_i \geq 1$ | 通过 |
| 日容量约束 | 每日恰好2站点 | 通过 |
| 无重复约束 | 同一天不重复访问同一站点 | 通过 |
### 6.2 模型有效性验证 (`07_backtest.py`)
**方法**留一交叉验证Leave-One-Out
1. 用2019年的69个站点数据训练模型
2. 预测第70个站点的 $\tilde{\mu}$ 和 $k$
3. 与实际值对比
4. 重复70次计算平均误差
**评估指标**
- MAPE平均绝对百分比误差
- $R^2$(决定系数)
---
## 7. 待完成:敏感性分析
### 7.1 参数扫描 (`08_sensitivity.py`)
| 参数 | 基准值 | 扫描范围 | 影响 |
|------|--------|---------|------|
| 有效容量 $C$ | 400 | [350, 450] | 截断修正强度 |
| 截断阈值 $p^{trunc}$ | 0.02 | [0.01, 0.10] | 修正站点数 |
| 质量阈值 $\bar{C}$ | 250 | [200, 300] | E2计算 |
### 7.2 敏感性报告格式
```
C = 350: 修正7站点, E1=141,234, F1=0.318
C = 400: 修正5站点, E1=140,121, F1=0.314 ← 基准
C = 450: 修正3站点, E1=139,456, F1=0.310
```
---
## 8. 待完成:可视化
### 8.1 图表清单 (`09_visualize.py`)
| 图编号 | 图名 | 类型 | 用途 |
|--------|------|------|------|
| Fig.1 | 站点地图 | 散点图+地图底图 | 展示站点分布和需求 |
| Fig.2 | 需求修正对比 | 柱状图 | 展示修正前后μ变化 |
| Fig.3 | 频次分配分布 | 直方图 | 展示k的分布 |
| Fig.4 | 有效性-公平性权衡 | 散点图 | Pareto前沿 |
| Fig.5 | 日历热力图 | 热力图 | 全年排程概览 |
| Fig.6 | 访问间隔箱线图 | 箱线图 | 间隔分布 |
| Fig.7 | 敏感性分析 | 折线图 | 参数影响 |
### 8.2 图表示例说明
**Fig.1 站点地图**
- X轴经度
- Y轴纬度
- 点大小:$\mu_i$(需求)
- 点颜色:$k_i$(访问频次)
**Fig.4 有效性-公平性权衡**
- X轴E2质量加权服务量
- Y轴F1Gini系数越小越公平
- 多个方案的散点
- Pareto前沿曲线
**Fig.5 日历热力图**
- X轴月份(1-12)
- Y轴日期(1-31)
- 颜色:该日访问的站点需求总和
---
## 9. 可复现流水线
### 9.1 完整脚本结构
```
task1/
├── 01_clean.py ✅ 数据清洗
├── 02_demand_correction.py ✅ 截断修正
├── 03_allocate.py ✅ 频次分配
├── 04_evaluate.py ✅ 指标计算
├── 05_schedule.py ✅ 日历排程
├── 06_validate.py ⏳ 约束验证
├── 07_backtest.py ⏳ 历史回测
├── 08_sensitivity.py ⏳ 敏感性分析
├── 09_visualize.py ⏳ 可视化
├── 01_clean.xlsx
├── 02_demand.xlsx
├── 03_allocate.xlsx
├── 04_metrics.xlsx
├── 05_schedule.xlsx
└── figures/ ⏳ 图表输出目录
```
### 9.2 运行命令
```bash
# 核心流程(已完成)
python task1/01_clean.py
python task1/02_demand_correction.py
python task1/03_allocate.py
python task1/04_evaluate.py
python task1/05_schedule.py
# 验证与分析(待完成)
python task1/06_validate.py
python task1/07_backtest.py
python task1/08_sensitivity.py
python task1/09_visualize.py
```
---
## 10. 假设与局限性
### 10.1 显式假设
| 编号 | 假设 | 依据 | 影响 |
|------|------|------|------|
| A1 | 真实需求服从正态分布 | 中心极限定理 | 截断修正公式 |
| A2 | 有效容量 $C=400$ | $\mu_{max}=396.6$ | 修正强度 |
| A3 | 2021年需求≈2019年 | 题面要求 | 整体方案有效性 |
| A4 | 全年365天运营 | 简化假设 | 总访问次数 |
| A5 | 每日2站点硬约束 | 题面 | 排程可行性 |
### 10.2 局限性
1. **截断修正简化**使用线性近似而非完整MLE
2. **需求外生性**:未建模"访问频次→需求"的反馈效应
3. **空间相关性**:未考虑相邻站点需求相关性
4. **季节性**:未考虑冬季需求下降
---
## 11. 结论与建议
### 11.1 方法论贡献
1. **截断偏误识别**:发现高需求站点 $\mu$ 被低估
2. **质量-数量权衡**:引入质量折扣因子
3. **满足率公平**:以服务充足度衡量公平
### 11.2 对FBST的运营建议
1. **MFP Waverly**:需求异常高($k=32$),建议增派资源
2. **数据收集**:记录"被拒服务人数"以更准确估计需求
3. **动态调整**:季度末复盘,调整下季度排程
### 11.3 有效性-公平性权衡
推荐方案牺牲公平性换取服务量最大化。若FBST优先考虑公平
- 可设置 $k_i$ 上下限约束
- 或使用混合目标函数 $\max (E_2 - \lambda \cdot F_1)$