diff --git a/task3/01_distance.xlsx b/task3/01_distance.xlsx index 64afdc6..d1b6a81 100644 Binary files a/task3/01_distance.xlsx and b/task3/01_distance.xlsx differ diff --git a/task3/02_pairing.py b/task3/02_pairing.py index f5100e2..ae66bee 100644 --- a/task3/02_pairing.py +++ b/task3/02_pairing.py @@ -26,10 +26,10 @@ INPUT_FILE = '01_distance.xlsx' OUTPUT_FILE = '02_pairing.xlsx' # 约束参数 -L_MAX = 50 # 距离阈值 (英里) -MU_SUM_MAX = 450 # 需求和上限 -CV_MAX = 0.5 # 变异系数上限 -Q = 400 # 卡车容量 +L_MAX = 30 # 距离阈值 (英里) +MU_SUM_MAX = 350 # 需求和上限 +CV_MAX = 0.3 # 变异系数上限 +Q = 350 # 卡车容量 # 价值函数权重 ALPHA = 1.0 # 容量利用率权重 diff --git a/task3/02_pairing.xlsx b/task3/02_pairing.xlsx index e8bd422..bea037a 100644 Binary files a/task3/02_pairing.xlsx and b/task3/02_pairing.xlsx differ diff --git a/task3/03_allocation.xlsx b/task3/03_allocation.xlsx index 216b5ea..acf68cc 100644 Binary files a/task3/03_allocation.xlsx and b/task3/03_allocation.xlsx differ diff --git a/task3/04_reschedule.xlsx b/task3/04_reschedule.xlsx index 4f15d64..c1708d7 100644 Binary files a/task3/04_reschedule.xlsx and b/task3/04_reschedule.xlsx differ diff --git a/task3/05_calendar.xlsx b/task3/05_calendar.xlsx index ca57067..23fa505 100644 Binary files a/task3/05_calendar.xlsx and b/task3/05_calendar.xlsx differ diff --git a/task3/06_evaluate.xlsx b/task3/06_evaluate.xlsx index 9528082..1a9057b 100644 Binary files a/task3/06_evaluate.xlsx and b/task3/06_evaluate.xlsx differ diff --git a/task3/07_sensitivity.py b/task3/07_sensitivity.py index b96f00e..cb524db 100644 --- a/task3/07_sensitivity.py +++ b/task3/07_sensitivity.py @@ -16,17 +16,66 @@ Task 3 - Step 7: 敏感性分析 (Refined) import pandas as pd import numpy as np from scipy import stats -import matplotlib.pyplot as plt import warnings import os +import tempfile from pathlib import Path warnings.filterwarnings('ignore') -# 设置绘图风格 -plt.style.use('seaborn-v0_8-whitegrid') -plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans'] # 适配中文 -plt.rcParams['axes.unicode_minus'] = False +# Matplotlib cache dirs(避免 home 目录不可写导致的警告) +_mpl_config_dir = Path(tempfile.gettempdir()) / "mm_task3_mplconfig" +_xdg_cache_dir = Path(tempfile.gettempdir()) / "mm_task3_xdg_cache" +_mpl_config_dir.mkdir(parents=True, exist_ok=True) +_xdg_cache_dir.mkdir(parents=True, exist_ok=True) +os.environ.setdefault("MPLCONFIGDIR", str(_mpl_config_dir)) +os.environ.setdefault("XDG_CACHE_HOME", str(_xdg_cache_dir)) + +import matplotlib.pyplot as plt +from cycler import cycler + +# 设置绘图风格(低饱和度 Morandi 色系) +MORANDI = { + "bg": "#F5F2ED", + "grid": "#D7D1C7", + "text": "#3F3F3F", + "muted_blue": "#8FA3A7", + "muted_blue_dark": "#6E858A", + "sage": "#AEB8A6", + "terracotta": "#B07A6A", + "warm_gray": "#8C857A", +} + +plt.rcParams.update( + { + "figure.facecolor": MORANDI["bg"], + "axes.facecolor": MORANDI["bg"], + "savefig.facecolor": MORANDI["bg"], + "axes.edgecolor": MORANDI["grid"], + "axes.labelcolor": MORANDI["text"], + "xtick.color": MORANDI["text"], + "ytick.color": MORANDI["text"], + "text.color": MORANDI["text"], + "grid.color": MORANDI["grid"], + "grid.alpha": 0.55, + "axes.grid": True, + "axes.prop_cycle": cycler( + "color", + [ + MORANDI["muted_blue"], + MORANDI["sage"], + MORANDI["terracotta"], + MORANDI["warm_gray"], + ], + ), + "legend.frameon": True, + "legend.framealpha": 0.9, + "legend.facecolor": MORANDI["bg"], + "legend.edgecolor": MORANDI["grid"], + "font.sans-serif": ["Arial Unicode MS", "SimHei", "DejaVu Sans"], + "axes.unicode_minus": False, + } +) # ============================================ # 基础参数和函数 (保持不变) @@ -333,16 +382,38 @@ for i, param in enumerate(params_list): data = df_results[df_results['param'] == param].sort_values('value') # 绘制 E1/E2 - 左轴 - line1, = ax.plot(data['value'], data['E1'], 'b-', linewidth=1.6, label='Expected Service (E1)') - line2, = ax.plot(data['value'], data['E2'], 'g-', linewidth=1.6, alpha=0.9, label='Quality-weighted (E2)') + line1, = ax.plot( + data['value'], + data['E1'], + linestyle='-', + color=MORANDI["muted_blue_dark"], + linewidth=1.8, + label='Expected Service (E1)', + ) + line2, = ax.plot( + data['value'], + data['E2'], + linestyle='-', + color=MORANDI["sage"], + linewidth=1.8, + alpha=0.95, + label='Quality-weighted (E2)', + ) ax.set_xlabel(param_labels[param][0]) ax.set_ylabel('Service (E1/E2)') # 绘制 R1 (风险) - 右轴 ax2 = ax.twinx() - line3, = ax2.plot(data['value'], data['R1'], 'r--', linewidth=1.6, label='Shortfall Risk (R1)') - ax2.set_ylabel('Risk Probability (R1)', color='r') - ax2.tick_params(axis='y', labelcolor='r') + line3, = ax2.plot( + data['value'], + data['R1'], + linestyle='--', + color=MORANDI["terracotta"], + linewidth=1.8, + label='Shortfall Risk (R1)', + ) + ax2.set_ylabel('Risk Probability (R1)', color=MORANDI["terracotta"]) + ax2.tick_params(axis='y', labelcolor=MORANDI["terracotta"]) # 添加基准线 if param == 'merge_ratio': @@ -354,7 +425,7 @@ for i, param in enumerate(params_list): elif param == 'cv_max': base_val = BASE_CV_MAX - ax.axvline(x=base_val, color='gray', linestyle=':', alpha=0.5) + ax.axvline(x=base_val, color=MORANDI["warm_gray"], linestyle=':', alpha=0.65) # 图例 lines = [line1, line2, line3] @@ -362,7 +433,7 @@ for i, param in enumerate(params_list): ax.legend(lines, labels, loc='best', frameon=True) ax.set_title(f'Effect of {param_labels[param][0]}') - ax.grid(True, alpha=0.3) + ax.grid(True, alpha=0.55) plt.tight_layout(rect=[0, 0.03, 1, 0.95]) plt.savefig(OUTPUT_FIG, dpi=300) diff --git a/task3/07_sensitivity.xlsx b/task3/07_sensitivity.xlsx index ad10fdd..463faba 100644 Binary files a/task3/07_sensitivity.xlsx and b/task3/07_sensitivity.xlsx differ diff --git a/task3/08_visualize.py b/task3/08_visualize.py index f113482..6a44d58 100644 --- a/task3/08_visualize.py +++ b/task3/08_visualize.py @@ -19,6 +19,7 @@ Task 3 - Step 8: 可视化(Fig.1/2/4/5) from __future__ import annotations from dataclasses import dataclass +import json from pathlib import Path import os @@ -38,11 +39,62 @@ import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt +from matplotlib import colors as mcolors +from cycler import cycler BASE_DIR = Path(__file__).resolve().parent +MORANDI = { + "bg": "#F5F2ED", + "grid": "#D7D1C7", + "text": "#3F3F3F", + "muted_blue": "#8FA3A7", + "muted_blue_dark": "#6E858A", + "sage": "#AEB8A6", + "clay": "#C6AA9B", + "rose": "#D2B8B3", + "warm_gray": "#8C857A", + "terracotta": "#B07A6A", +} + + +def _apply_morandi_style() -> None: + plt.rcParams.update( + { + "figure.facecolor": MORANDI["bg"], + "axes.facecolor": MORANDI["bg"], + "savefig.facecolor": MORANDI["bg"], + "axes.edgecolor": MORANDI["grid"], + "axes.labelcolor": MORANDI["text"], + "xtick.color": MORANDI["text"], + "ytick.color": MORANDI["text"], + "text.color": MORANDI["text"], + "grid.color": MORANDI["grid"], + "grid.alpha": 0.55, + "axes.grid": True, + "axes.titleweight": "semibold", + "axes.titlesize": 11, + "axes.labelsize": 10, + "legend.frameon": True, + "legend.framealpha": 0.9, + "legend.facecolor": MORANDI["bg"], + "legend.edgecolor": MORANDI["grid"], + "axes.prop_cycle": cycler( + "color", + [ + MORANDI["muted_blue"], + MORANDI["sage"], + MORANDI["clay"], + MORANDI["rose"], + MORANDI["warm_gray"], + ], + ), + } + ) + + @dataclass(frozen=True) class Paths: distance_xlsx: Path @@ -76,6 +128,15 @@ def _pair_key(a: int, b: int) -> tuple[int, int]: def fig1_pairing_map(paths: Paths) -> Path: + _apply_morandi_style() + + # Warmer + higher contrast for Fig.1 specifically (clearer on paper/screens) + fig1_colors = { + "paired": "#e76f51", + "unpaired": "#6c757d", + "link": "#bc6c25", + } + sites = pd.read_excel(paths.distance_xlsx, sheet_name="sites") pairs = pd.read_excel(paths.pairing_xlsx, sheet_name="selected_pairs") @@ -97,9 +158,9 @@ def fig1_pairing_map(paths: Paths) -> Path: ax.plot( [si["lon"], sj["lon"]], [si["lat"], sj["lat"]], - color="#2c7fb8", - alpha=0.35, - linewidth=1.0, + color=fig1_colors["link"], + alpha=0.5, + linewidth=1.2, zorder=1, ) @@ -110,8 +171,8 @@ def fig1_pairing_map(paths: Paths) -> Path: paired["lon"], paired["lat"], s=18, - color="#d95f0e", - alpha=0.85, + color=fig1_colors["paired"], + alpha=0.9, label=f"Paired sites (n={len(paired)})", zorder=2, ) @@ -120,7 +181,7 @@ def fig1_pairing_map(paths: Paths) -> Path: unpaired["lon"], unpaired["lat"], s=22, - color="#636363", + color=fig1_colors["unpaired"], alpha=0.9, marker="x", label=f"Unpaired sites (n={len(unpaired)})", @@ -130,7 +191,7 @@ def fig1_pairing_map(paths: Paths) -> Path: ax.set_title("Fig.1 Pairing map (sites + selected 34 links)") ax.set_xlabel("Longitude") ax.set_ylabel("Latitude") - ax.grid(True, alpha=0.2) + ax.grid(True, alpha=0.55) ax.legend(loc="best", frameon=True) out = paths.figures_dir / "fig1_pairing_map.png" @@ -140,7 +201,58 @@ def fig1_pairing_map(paths: Paths) -> Path: return out +def export_fig1_points_js(paths: Paths) -> Path: + sites = pd.read_excel(paths.distance_xlsx, sheet_name="sites").copy() + pairs = pd.read_excel(paths.pairing_xlsx, sheet_name="selected_pairs").copy() + + sites["site_id"] = sites["site_id"].astype(int) + sites["k"] = sites["k"].astype(int) + + paired_ids = set(pairs["site_i_id"].astype(int)).union( + set(pairs["site_j_id"].astype(int)) + ) + + points = [] + for _, r in sites.iterrows(): + points.append( + { + "site_id": int(r["site_id"]), + "site_name": str(r["site_name"]), + "lat": float(r["lat"]), + "lng": float(r["lon"]), + "mu": float(r["mu"]), + "sigma": float(r["sigma"]), + "k": int(r["k"]), + "is_paired": int(r["site_id"]) in paired_ids, + } + ) + + links = [] + for _, r in pairs.iterrows(): + links.append( + { + "site_i_id": int(r["site_i_id"]), + "site_j_id": int(r["site_j_id"]), + "site_i_name": str(r["site_i_name"]), + "site_j_name": str(r["site_j_name"]), + "distance": float(r["distance"]), + } + ) + + out = BASE_DIR / "fig1_points.js" + payload = ( + "// Auto-generated from `task3/01_distance.xlsx` (sites) + `task3/02_pairing.xlsx` (selected_pairs)\n" + "// Usage: open `task3/fig1_carto.html` in a browser (same directory must contain this file).\n" + f"window.FIG1_POINTS = {json.dumps(points, ensure_ascii=False, separators=(',', ':'))};\n" + f"window.FIG1_LINKS = {json.dumps(links, ensure_ascii=False, separators=(',', ':'))};\n" + ) + out.write_text(payload, encoding="utf-8") + return out + + def fig2_allocation_scatter(paths: Paths) -> Path: + _apply_morandi_style() + df = pd.read_excel(paths.allocation_xlsx, sheet_name="allocation") q_ratio = df["q_ratio"].to_numpy(dtype=float) @@ -157,15 +269,22 @@ def fig2_allocation_scatter(paths: Paths) -> Path: ] for ax, (title, x) in zip(axes, panels): - ax.scatter(x, q_ratio, s=22, alpha=0.75, color="#3182bd", edgecolor="none") + ax.scatter( + x, + q_ratio, + s=22, + alpha=0.8, + color=MORANDI["muted_blue"], + edgecolor="none", + ) if np.isfinite(x).all() and np.isfinite(q_ratio).all() and len(x) >= 2: coef = np.polyfit(x, q_ratio, 1) xx = np.linspace(float(np.min(x)), float(np.max(x)), 100) yy = coef[0] * xx + coef[1] - ax.plot(xx, yy, color="#de2d26", linewidth=2.0, alpha=0.9) + ax.plot(xx, yy, color=MORANDI["terracotta"], linewidth=2.0, alpha=0.95) ax.set_title(title) ax.set_xlabel("x") - ax.grid(True, alpha=0.2) + ax.grid(True, alpha=0.55) axes[0].set_ylabel("$q^*/Q$") fig.suptitle("Fig.2 Allocation strategy scatter (34 pairs)", y=1.02) @@ -178,6 +297,8 @@ def fig2_allocation_scatter(paths: Paths) -> Path: def fig4_calendar_heatmap(paths: Paths) -> Path: + _apply_morandi_style() + sites = pd.read_excel(paths.distance_xlsx, sheet_name="sites")[["site_id", "mu"]] sites["site_id"] = sites["site_id"].astype(int) mu_by_id = dict(zip(sites["site_id"], sites["mu"])) @@ -212,7 +333,17 @@ def fig4_calendar_heatmap(paths: Paths) -> Path: expected[day - 1, slot - 1] = pair_E.get(_pair_key(i_id, j_id), np.nan) fig, ax = plt.subplots(figsize=(12, 2.6), dpi=200) - im = ax.imshow(expected.T, aspect="auto", cmap="viridis") + cmap = mcolors.LinearSegmentedColormap.from_list( + "morandi", + [ + MORANDI["bg"], + "#E7E1D7", + MORANDI["sage"], + MORANDI["muted_blue"], + "#5F7478", + ], + ) + im = ax.imshow(expected.T, aspect="auto", cmap=cmap) ax.set_yticks([0, 1], labels=["Slot 1", "Slot 2"]) ax.set_xlabel("Day of year") ax.set_title("Fig.4 Calendar heatmap (expected service per slot)") @@ -233,6 +364,8 @@ def fig4_calendar_heatmap(paths: Paths) -> Path: def fig5_risk_distribution(paths: Paths) -> Path: + _apply_morandi_style() + risk = pd.read_excel(paths.evaluate_xlsx, sheet_name="pair_risk").copy() p = risk["shortfall_prob_either"].to_numpy(dtype=float) p = p[np.isfinite(p)] @@ -240,21 +373,36 @@ def fig5_risk_distribution(paths: Paths) -> Path: fig, axes = plt.subplots(1, 2, figsize=(11, 3.8), dpi=200) ax = axes[0] - ax.hist(p, bins=10, color="#3182bd", alpha=0.85, edgecolor="white") - ax.axvline(float(np.mean(p)), color="#de2d26", linewidth=2.0, label=f"mean={np.mean(p):.3f}") + ax.hist(p, bins=10, color=MORANDI["sage"], alpha=0.9, edgecolor=MORANDI["bg"]) + ax.axvline( + float(np.mean(p)), + color=MORANDI["terracotta"], + linewidth=2.2, + label=f"mean={np.mean(p):.3f}", + ) ax.set_title("Histogram") ax.set_xlabel("Shortfall probability (either site)") ax.set_ylabel("Count") - ax.grid(True, alpha=0.2) + ax.grid(True, alpha=0.55) ax.legend(loc="best", frameon=True) ax = axes[1] p_sorted = np.sort(p)[::-1] - ax.plot(np.arange(1, len(p_sorted) + 1), p_sorted, marker="o", markersize=3.5, linewidth=1.5) + ax.plot( + np.arange(1, len(p_sorted) + 1), + p_sorted, + marker="o", + markersize=3.2, + linewidth=1.6, + color=MORANDI["muted_blue_dark"], + markerfacecolor=MORANDI["muted_blue"], + markeredgecolor=MORANDI["bg"], + markeredgewidth=0.6, + ) ax.set_title("Sorted by risk (descending)") ax.set_xlabel("Pair rank") ax.set_ylabel("Shortfall probability") - ax.grid(True, alpha=0.2) + ax.grid(True, alpha=0.55) fig.suptitle("Fig.5 Risk distribution across 34 pairs", y=1.02) fig.tight_layout() @@ -282,6 +430,8 @@ def main() -> None: out1 = fig1_pairing_map(paths) print(f"Saved: {out1.relative_to(BASE_DIR)}") + out1_js = export_fig1_points_js(paths) + print(f"Saved: {out1_js.relative_to(BASE_DIR)}") out2 = fig2_allocation_scatter(paths) print(f"Saved: {out2.relative_to(BASE_DIR)}") out4 = fig4_calendar_heatmap(paths) diff --git a/task3/README.md b/task3/README.md index a2f1ada..7072d58 100644 --- a/task3/README.md +++ b/task3/README.md @@ -643,6 +643,8 @@ $$R_1 = P(S_i / D_i < 0.8 \text{ 或 } S_j / D_j < 0.8)$$ ``` task3/ +├── fig1_carto.html # 交互地图(CartoDB 底图) +├── fig1_points.js # ✅ 自动生成(08_visualize.py) ├── 08_visualize.py # 可视化脚本 └── figures/ ├── fig1_pairing_map.png @@ -656,6 +658,8 @@ task3/ ![Fig.1 站点配对地图](figures/fig1_pairing_map.png) +交互版:用浏览器打开 `task3/fig1_carto.html`(同目录需有 `task3/fig1_points.js`;需联网加载 CartoDB 底图)。 + ![Fig.2 分配策略散点图](figures/fig2_allocation_scatter.png) ![Fig.4 日历热力图](figures/fig4_calendar_heatmap.png) diff --git a/task3/__pycache__/07_sensitivity.cpython-313.pyc b/task3/__pycache__/07_sensitivity.cpython-313.pyc index 17fa2fa..d7ab62d 100644 Binary files a/task3/__pycache__/07_sensitivity.cpython-313.pyc and b/task3/__pycache__/07_sensitivity.cpython-313.pyc differ diff --git a/task3/__pycache__/08_visualize.cpython-313.pyc b/task3/__pycache__/08_visualize.cpython-313.pyc index d623973..e1e59f0 100644 Binary files a/task3/__pycache__/08_visualize.cpython-313.pyc and b/task3/__pycache__/08_visualize.cpython-313.pyc differ diff --git a/task3/fig1_carto.html b/task3/fig1_carto.html new file mode 100644 index 0000000..e462d4a --- /dev/null +++ b/task3/fig1_carto.html @@ -0,0 +1,175 @@ + + + + + + Task 3 Fig.1 (CartoDB + Leaflet) + + + + +
+ + + + + + diff --git a/task3/fig1_points.js b/task3/fig1_points.js new file mode 100644 index 0000000..175f19c --- /dev/null +++ b/task3/fig1_points.js @@ -0,0 +1,4 @@ +// Auto-generated from `task3/01_distance.xlsx` (sites) + `task3/02_pairing.xlsx` (selected_pairs) +// Usage: open `task3/fig1_carto.html` in a browser (same directory must contain this file). +window.FIG1_POINTS = [{"site_id":1,"site_name":"MFP American Legion - Binghamton","lat":42.108036,"lng":-75.887779,"mu":200.1818181818182,"sigma":46.0669473306365,"k":14,"is_paired":true},{"site_id":2,"site_name":"MFP Avoca","lat":42.4174468,"lng":-77.4358433,"mu":314.5833333333333,"sigma":57.3497377870416,"k":24,"is_paired":true},{"site_id":3,"site_name":"MFP Bath","lat":42.3362172,"lng":-77.3175047,"mu":279.4545454545454,"sigma":45.2534277958336,"k":19,"is_paired":true},{"site_id":4,"site_name":"MFP Beaver Dams","lat":42.2606415,"lng":-76.9559856,"mu":170.7,"sigma":28.7906234736241,"k":12,"is_paired":true},{"site_id":5,"site_name":"MFP Birnie Transportation Services","lat":42.172309,"lng":-77.1101217,"mu":213.375,"sigma":53.4039525021991,"k":15,"is_paired":true},{"site_id":6,"site_name":"MFP Boys and Girls Club","lat":42.1053841,"lng":-75.9213736,"mu":210.8,"sigma":52.3551971313896,"k":15,"is_paired":true},{"site_id":7,"site_name":"MFP Bradford","lat":42.3691309,"lng":-77.1081064,"mu":122.2727272727273,"sigma":29.7425315300864,"k":9,"is_paired":true},{"site_id":8,"site_name":"MFP Campbell","lat":42.2314129,"lng":-77.1948265,"mu":168.5,"sigma":40.968946111104,"k":12,"is_paired":true},{"site_id":9,"site_name":"MFP Canisteo","lat":42.272779,"lng":-77.606849,"mu":177.0,"sigma":51.3051870888531,"k":13,"is_paired":true},{"site_id":10,"site_name":"MFP Colesville","lat":42.1841905,"lng":-75.6329193,"mu":197.3333333333333,"sigma":66.2589044902074,"k":14,"is_paired":false},{"site_id":11,"site_name":"MFP College Corning Community College","lat":42.1172594,"lng":-77.0735445,"mu":251.0,"sigma":33.2505639049926,"k":17,"is_paired":true},{"site_id":12,"site_name":"MFP College Ithaca College","lat":42.4199351,"lng":-76.4969643,"mu":138.3333333333333,"sigma":66.1418929272515,"k":10,"is_paired":false},{"site_id":13,"site_name":"MFP College TC3 -College","lat":42.502126,"lng":-76.287671,"mu":261.5,"sigma":91.9929345112982,"k":19,"is_paired":false},{"site_id":14,"site_name":"MFP Conklin- Maines Community Center","lat":42.0870901,"lng":-75.8309647,"mu":153.1666666666667,"sigma":20.1125620325993,"k":11,"is_paired":true},{"site_id":15,"site_name":"MFP Danby","lat":42.3522565,"lng":-76.4800051,"mu":160.2222222222222,"sigma":34.5788149658782,"k":11,"is_paired":true},{"site_id":16,"site_name":"MFP Deposit","lat":42.0625771,"lng":-75.422248,"mu":156.5833333333333,"sigma":56.545168322378,"k":11,"is_paired":false},{"site_id":17,"site_name":"MFP Endwell United Methodist Church","lat":42.111296,"lng":-76.02203,"mu":285.25,"sigma":60.7814639806937,"k":21,"is_paired":true},{"site_id":18,"site_name":"MFP Erin","lat":42.1788978,"lng":-76.6922607,"mu":173.9090909090909,"sigma":27.3219858189501,"k":12,"is_paired":true},{"site_id":19,"site_name":"MFP First Assembly Of God Church","lat":42.1022814,"lng":-75.9129518,"mu":146.0,"sigma":21.6459108042479,"k":10,"is_paired":true},{"site_id":20,"site_name":"MFP Lamphear Court","lat":42.1598551,"lng":-77.0711389,"mu":126.0,"sigma":40.6803255750109,"k":9,"is_paired":false},{"site_id":21,"site_name":"MFP Lansing","lat":42.5183206,"lng":-76.5035538,"mu":181.0,"sigma":23.2937759927411,"k":13,"is_paired":true},{"site_id":22,"site_name":"MFP Lindley","lat":42.028142,"lng":-77.1392597,"mu":232.9,"sigma":49.6217918078562,"k":16,"is_paired":true},{"site_id":23,"site_name":"MFP Millport","lat":42.267172,"lng":-76.837356,"mu":166.0,"sigma":36.4996194805127,"k":12,"is_paired":true},{"site_id":24,"site_name":"MFP Montour Falls-Schuyler County Human Services Complex","lat":42.3409968,"lng":-76.8412703,"mu":149.3636363636364,"sigma":60.0387753493902,"k":11,"is_paired":false},{"site_id":25,"site_name":"MFP Nichols-The Creamery","lat":42.02302,"lng":-76.371793,"mu":122.4,"sigma":26.4253666010521,"k":9,"is_paired":true},{"site_id":26,"site_name":"MFP Owego VFW","lat":42.1043411,"lng":-76.2615965,"mu":176.25,"sigma":34.285234033433,"k":12,"is_paired":true},{"site_id":27,"site_name":"MFP Prattsburgh","lat":42.5272099,"lng":-77.2866,"mu":144.5,"sigma":50.8428952755446,"k":10,"is_paired":false},{"site_id":28,"site_name":"MFP Rathbone","lat":42.1306252,"lng":-77.3197915,"mu":269.0909090909091,"sigma":55.5687943821972,"k":19,"is_paired":true},{"site_id":29,"site_name":"MFP Reach for Christ Church Freeville","lat":42.4919905,"lng":-76.3443272,"mu":220.0,"sigma":22.6175939382498,"k":15,"is_paired":true},{"site_id":30,"site_name":"MFP Redeemer Lutheran Church","lat":42.1000047,"lng":-75.9224594,"mu":230.6,"sigma":93.4786963252412,"k":17,"is_paired":false},{"site_id":31,"site_name":"MFP Rehoboth Deliverance Ministry","lat":42.318273,"lng":-77.6493618,"mu":235.9,"sigma":56.9530996132393,"k":16,"is_paired":true},{"site_id":32,"site_name":"MFP Richford","lat":42.3551522,"lng":-76.2008963,"mu":265.9090909090909,"sigma":28.7974114998363,"k":18,"is_paired":true},{"site_id":33,"site_name":"MFP Saint Mary Recreation Center","lat":42.0988789,"lng":-75.9041391,"mu":148.3,"sigma":59.8313370140505,"k":11,"is_paired":false},{"site_id":34,"site_name":"MFP Salvation Army Ithaca","lat":42.4398066,"lng":-76.5019869,"mu":181.1818181818182,"sigma":39.5848915163808,"k":13,"is_paired":true},{"site_id":35,"site_name":"MFP Schuyler Outreach","lat":42.3770866,"lng":-76.8698425,"mu":138.8571428571429,"sigma":21.5362374571215,"k":10,"is_paired":true},{"site_id":36,"site_name":"MFP Senior - Addison Place Apartments","lat":42.1001876,"lng":-77.2371924,"mu":30.09090909090909,"sigma":5.02900676982135,"k":3,"is_paired":true},{"site_id":37,"site_name":"MFP Senior - Bragg","lat":42.0896598,"lng":-76.7977954,"mu":66.88888888888889,"sigma":6.03001750504186,"k":5,"is_paired":true},{"site_id":38,"site_name":"MFP Senior - Carpenter Apartments","lat":42.0930065,"lng":-76.7984317,"mu":31.09090909090909,"sigma":6.75950509215793,"k":3,"is_paired":true},{"site_id":39,"site_name":"MFP Senior - Cayuga Meadows","lat":42.464569,"lng":-76.5409393,"mu":25.88888888888889,"sigma":3.33333333333333,"k":3,"is_paired":true},{"site_id":40,"site_name":"MFP Senior - CFS Lakeview","lat":42.3488781,"lng":-77.3110763,"mu":112.0,"sigma":15.563490039905,"k":8,"is_paired":true},{"site_id":41,"site_name":"MFP Senior - Conifer Village","lat":42.4511294,"lng":-76.5323538,"mu":33.8,"sigma":10.3794669098819,"k":3,"is_paired":false},{"site_id":42,"site_name":"MFP Senior - Corning Senior Center","lat":42.1490255,"lng":-77.0619725,"mu":75.0,"sigma":30.9636884107821,"k":6,"is_paired":false},{"site_id":43,"site_name":"MFP Senior - Dayspring","lat":42.1420715,"lng":-77.0454685,"mu":77.18181818181819,"sigma":17.9489174148091,"k":6,"is_paired":true},{"site_id":44,"site_name":"MFP Senior - East Hill Senior Living","lat":42.1141777,"lng":-75.8720703,"mu":39.83333333333334,"sigma":2.16724933890169,"k":4,"is_paired":true},{"site_id":45,"site_name":"MFP Senior - Elizabeth Square, Waverly","lat":42.001546,"lng":-76.541203,"mu":29.0,"sigma":9.67658842962558,"k":3,"is_paired":false},{"site_id":46,"site_name":"MFP Senior - Ellis Hollow","lat":42.435955,"lng":-76.460374,"mu":24.72727272727273,"sigma":13.8064543536051,"k":3,"is_paired":false},{"site_id":47,"site_name":"MFP Senior - Flannery","lat":42.0819957,"lng":-76.8053269,"mu":61.72727272727273,"sigma":10.1103007778296,"k":5,"is_paired":true},{"site_id":48,"site_name":"MFP Senior - Harry L Apartments","lat":42.1237339,"lng":-75.9567565,"mu":32.83333333333334,"sigma":6.24232862533419,"k":3,"is_paired":true},{"site_id":49,"site_name":"MFP Senior - Jefferson Village","lat":42.3829923,"lng":-76.8713304,"mu":24.81818181818182,"sigma":2.78633026822672,"k":3,"is_paired":true},{"site_id":50,"site_name":"MFP Senior - Lincoln Court","lat":42.090217,"lng":-75.910198,"mu":26.0,"sigma":4.89897948556636,"k":3,"is_paired":true},{"site_id":51,"site_name":"MFP Senior - Long Meadow Senior Housing","lat":42.1135325,"lng":-76.2704045,"mu":34.75,"sigma":13.0043698949098,"k":3,"is_paired":false},{"site_id":52,"site_name":"MFP Senior - Metro Plaza Apartments","lat":42.1016977,"lng":-75.9085338,"mu":56.3,"sigma":19.9167712689024,"k":5,"is_paired":false},{"site_id":53,"site_name":"MFP Senior - North Shore Towers","lat":42.0966132,"lng":-75.9103884,"mu":58.33333333333334,"sigma":6.86051504383355,"k":5,"is_paired":true},{"site_id":54,"site_name":"MFP Senior - Northern Broome Senior Center, Whitney Point","lat":42.3269618,"lng":-75.9677778,"mu":50.90909090909091,"sigma":21.9565687002981,"k":4,"is_paired":false},{"site_id":55,"site_name":"MFP Senior - Park Terrace Congregate Apartments","lat":42.051364,"lng":-76.832954,"mu":24.36363636363636,"sigma":4.41073071662117,"k":3,"is_paired":true},{"site_id":56,"site_name":"MFP Senior - Springview Apartments","lat":42.0100399,"lng":-76.533895,"mu":27.58333333333333,"sigma":9.31722094084958,"k":3,"is_paired":false},{"site_id":57,"site_name":"MFP Senior - Titus Towers","lat":42.4317058,"lng":-76.504801,"mu":72.8,"sigma":4.31534728871525,"k":6,"is_paired":true},{"site_id":58,"site_name":"MFP Senior - Villa Serene","lat":42.1295916,"lng":-76.8158267,"mu":69.72727272727273,"sigma":5.0614406860282,"k":6,"is_paired":true},{"site_id":59,"site_name":"MFP Senior - Village Square/Manor","lat":42.159804,"lng":-77.09139,"mu":34.25,"sigma":6.48249390842192,"k":3,"is_paired":true},{"site_id":60,"site_name":"MFP Senior - Wells Apartments","lat":42.1076045,"lng":-75.9607932,"mu":23.5,"sigma":4.55216676124922,"k":3,"is_paired":false},{"site_id":61,"site_name":"MFP Senior - Woodsedge Apartments","lat":42.535151,"lng":-76.501086,"mu":17.2,"sigma":4.23739962188552,"k":2,"is_paired":true},{"site_id":62,"site_name":"MFP The Love Church","lat":42.1412225,"lng":-76.8137998,"mu":259.3333333333333,"sigma":59.8913157057824,"k":18,"is_paired":true},{"site_id":63,"site_name":"MFP Troupsburg","lat":42.0432505,"lng":-77.5456383,"mu":148.7142857142857,"sigma":50.2484304422015,"k":11,"is_paired":false},{"site_id":64,"site_name":"MFP Tuscarora","lat":42.0522655,"lng":-77.2737134,"mu":192.6363636363636,"sigma":29.8639338576576,"k":14,"is_paired":true},{"site_id":65,"site_name":"MFP Van Etten","lat":42.2080409,"lng":-76.579406,"mu":213.7,"sigma":19.9613515463145,"k":15,"is_paired":true},{"site_id":66,"site_name":"MFP Waverly","lat":42.0194466,"lng":-76.5236008,"mu":396.6363636363636,"sigma":51.8753751355548,"k":35,"is_paired":false},{"site_id":67,"site_name":"MFP Wayland","lat":42.5685797,"lng":-77.5957241,"mu":180.4166666666667,"sigma":35.2638324461179,"k":13,"is_paired":false},{"site_id":68,"site_name":"MFP Whitney Point","lat":42.3405329,"lng":-75.9765268,"mu":202.5454545454545,"sigma":30.4478690103713,"k":14,"is_paired":true},{"site_id":69,"site_name":"MFP Windsor","lat":42.0779615,"lng":-75.6427789,"mu":200.9090909090909,"sigma":34.8954281975578,"k":14,"is_paired":true},{"site_id":70,"site_name":"MFP Woodhull","lat":42.0798397,"lng":-77.4111295,"mu":176.0,"sigma":63.5438288256081,"k":12,"is_paired":false}]; +window.FIG1_LINKS = [{"site_i_id":1,"site_j_id":19,"site_i_name":"MFP American Legion - Binghamton","site_j_name":"MFP First Assembly Of God Church","distance":1.348436821527424},{"site_i_id":58,"site_j_id":62,"site_i_name":"MFP Senior - Villa Serene","site_j_name":"MFP The Love Church","distance":0.8092057380985171},{"site_i_id":11,"site_j_id":43,"site_i_name":"MFP College Corning Community College","site_j_name":"MFP Senior - Dayspring","distance":2.2349978091056},{"site_i_id":17,"site_j_id":53,"site_i_name":"MFP Endwell United Methodist Church","site_j_name":"MFP Senior - North Shore Towers","distance":5.804383712418112},{"site_i_id":15,"site_j_id":34,"site_i_name":"MFP Danby","site_j_name":"MFP Salvation Army Ithaca","distance":6.143925955556406},{"site_i_id":4,"site_j_id":23,"site_i_name":"MFP Beaver Dams","site_j_name":"MFP Millport","distance":6.074412981852127},{"site_i_id":5,"site_j_id":7,"site_i_name":"MFP Birnie Transportation Services","site_j_name":"MFP Bradford","distance":13.58110090990046},{"site_i_id":31,"site_j_id":40,"site_i_name":"MFP Rehoboth Deliverance Ministry","site_j_name":"MFP Senior - CFS Lakeview","distance":17.38378084014594},{"site_i_id":32,"site_j_id":57,"site_i_name":"MFP Richford","site_j_name":"MFP Senior - Titus Towers","distance":16.36265720561532},{"site_i_id":28,"site_j_id":36,"site_i_name":"MFP Rathbone","site_j_name":"MFP Senior - Addison Place Apartments","distance":4.720660032776843},{"site_i_id":25,"site_j_id":65,"site_i_name":"MFP Nichols-The Creamery","site_j_name":"MFP Van Etten","distance":16.61032388714411},{"site_i_id":8,"site_j_id":9,"site_i_name":"MFP Campbell","site_j_name":"MFP Canisteo","distance":21.23606041594513},{"site_i_id":2,"site_j_id":59,"site_i_name":"MFP Avoca","site_j_name":"MFP Senior - Village Square/Manor","distance":25.00335559264166},{"site_i_id":18,"site_j_id":35,"site_i_name":"MFP Erin","site_j_name":"MFP Schuyler Outreach","distance":16.40726185975893},{"site_i_id":14,"site_j_id":26,"site_i_name":"MFP Conklin- Maines Community Center","site_j_name":"MFP Owego VFW","distance":22.08036608633093},{"site_i_id":6,"site_j_id":44,"site_i_name":"MFP Boys and Girls Club","site_j_name":"MFP Senior - East Hill Senior Living","distance":2.595672199467672},{"site_i_id":22,"site_j_id":37,"site_i_name":"MFP Lindley","site_j_name":"MFP Senior - Bragg","distance":18.00067727329831},{"site_i_id":3,"site_j_id":49,"site_i_name":"MFP Bath","site_j_name":"MFP Senior - Jefferson Village","distance":22.97654515986252},{"site_i_id":29,"site_j_id":39,"site_i_name":"MFP Reach for Christ Church Freeville","site_j_name":"MFP Senior - Cayuga Meadows","distance":10.18287955207643},{"site_i_id":21,"site_j_id":61,"site_i_name":"MFP Lansing","site_j_name":"MFP Senior - Woodsedge Apartments","distance":1.168058001763275},{"site_i_id":48,"site_j_id":68,"site_i_name":"MFP Senior - Harry L Apartments","site_j_name":"MFP Whitney Point","distance":14.99319217055408},{"site_i_id":50,"site_j_id":69,"site_i_name":"MFP Senior - Lincoln Court","site_j_name":"MFP Windsor","distance":13.72039618876427},{"site_i_id":47,"site_j_id":64,"site_i_name":"MFP Senior - Flannery","site_j_name":"MFP Tuscarora","distance":24.07963686909654},{"site_i_id":38,"site_j_id":55,"site_i_name":"MFP Senior - Carpenter Apartments","site_j_name":"MFP Senior - Park Terrace Congregate Apartments","distance":3.373801467242223}]; diff --git a/task3/figures/fig1_pairing_map.png b/task3/figures/fig1_pairing_map.png index a014b1b..1d7d6cc 100644 Binary files a/task3/figures/fig1_pairing_map.png and b/task3/figures/fig1_pairing_map.png differ diff --git a/task3/figures/fig2_allocation_scatter.png b/task3/figures/fig2_allocation_scatter.png index a5ee808..8e6ad1b 100644 Binary files a/task3/figures/fig2_allocation_scatter.png and b/task3/figures/fig2_allocation_scatter.png differ diff --git a/task3/figures/fig3_sensitivity.png b/task3/figures/fig3_sensitivity.png index 774dd75..54c15ed 100644 Binary files a/task3/figures/fig3_sensitivity.png and b/task3/figures/fig3_sensitivity.png differ diff --git a/task3/figures/fig4_calendar_heatmap.png b/task3/figures/fig4_calendar_heatmap.png index e43ed3e..02be6bd 100644 Binary files a/task3/figures/fig4_calendar_heatmap.png and b/task3/figures/fig4_calendar_heatmap.png differ diff --git a/task3/figures/fig5_risk_distribution.png b/task3/figures/fig5_risk_distribution.png index 3d5186a..0e82eae 100644 Binary files a/task3/figures/fig5_risk_distribution.png and b/task3/figures/fig5_risk_distribution.png differ