import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import FancyArrowPatch
import numpy as np
# 设置画布大小与分辨率(适当放大画布避免拥挤)
plt.figure(figsize=(22, 14), dpi=120)
ax = plt.gca()
ax.set_xlim(0, 11)
ax.set_ylim(0, 9)
ax.axis('off') # 隐藏坐标轴
# -------------------------- 1. 绘制标题(增大字号,提升辨识度) --------------------------
title = ax.text(5.5, 8.5, '美团外卖大数据杀熟核心技术路线图',
fontsize=18, fontweight='bold', ha='center', va='center',
bbox=dict(boxstyle="round,pad=0.3", facecolor='#F0F8FF', edgecolor='#4169E1')) # 标题加背景框
# -------------------------- 2. 第一阶段:多渠道数据采集(调整文本间距,避免重叠) --------------------------
# 阶段框(子图区域,适当扩大高度)
stage1 = patches.Rectangle((0.3, 5.8), 3.2, 2.2, linewidth=2, edgecolor='#2E86AB',
facecolor='#E6F3FF', alpha=0.8) # alpha提升背景透明度,避免遮挡文字
ax.add_patch(stage1)
ax.text(2, 7.6, '阶段一:多渠道数据采集', fontsize=13, fontweight='bold',
ha='center', va='center', color='#2E86AB')
# 技术1:APP埋点(调整y坐标,增加行间距)
ax.text(0.8, 7.2, '1. APP埋点技术', fontsize=11, fontweight='bold', color='#1A5F7A')
ax.text(0.8, 7.0, '• 部署节点:页面加载、按钮点击、订单提交', fontsize=9, color='#333')
ax.text(0.8, 6.8, '• 采集数据:浏览时长、下单频率、取消订单次数', fontsize=9, color='#333')
# 技术2:Cookie+设备指纹(下移y坐标,与APP埋点错开)
ax.text(0.8, 6.4, '2. Cookie+设备指纹技术', fontsize=11, fontweight='bold', color='#1A5F7A')
ax.text(0.8, 6.2, '• Cookie:记录历史订单、收货地址变化', fontsize=9, color='#333')
ax.text(0.8, 6.0, '• 设备指纹:关联多设备(防账号切换)', fontsize=9, color='#333')
# 技术3:间接数据获取(右移x坐标,避免与左侧技术重叠)
ax.text(2.5, 7.2, '3. 间接数据获取', fontsize=11, fontweight='bold', color='#1A5F7A')
ax.text(2.5, 7.0, '• 支付习惯:信用卡使用、支付频次', fontsize=9, color='#333')
ax.text(2.5, 6.8, '• 外部数据:关联大众点评消费评价', fontsize=9, color='#333')
# 数据池节点(放大尺寸,确保文字容纳)
data_pool = patches.Ellipse((2, 5.5), 1.2, 0.4, linewidth=2, edgecolor='#2E86AB',
facecolor='#FFFFFF', zorder=5) # zorder提升层级,避免被遮挡
ax.add_patch(data_pool)
ax.text(2, 5.5, '用户原始数据池', fontsize=10, fontweight='bold',
ha='center', va='center', color='#2E86AB')
# -------------------------- 3. 数据流转箭头(延长长度,明确指向) --------------------------
arrow1 = FancyArrowPatch((2.6, 5.5), (4.2, 5.5),
arrowstyle='->', mutation_scale=18, color='#666', linewidth=2, zorder=3)
ax.add_patch(arrow1)
ax.text(3.4, 5.6, '数据流转:原始数据→清洗', fontsize=9, ha='center', va='center', color='#666')
# 数据清洗节点(放大尺寸,文字分行调整)
clean_node = patches.Rectangle((3.9, 5.2), 0.8, 0.6, linewidth=2, edgecolor='#F24236',
facecolor='#FFE6E6', zorder=5)
ax.add_patch(clean_node)
ax.text(4.3, 5.5, '数据清洗', fontsize=9, fontweight='bold', ha='center', va='center', color='#F24236')
ax.text(4.3, 5.2, '特征提取', fontsize=9, fontweight='bold', ha='center', va='center', color='#F24236')
# -------------------------- 4. 第二阶段:用户数字画像构建(优化文本布局) --------------------------
# 阶段框(扩大宽度,增加内部间距)
stage2 = patches.Rectangle((4.5, 3.8), 3.5, 2.2, linewidth=2, edgecolor='#F24236',
facecolor='#FFE6E6', alpha=0.8)
ax.add_patch(stage2)
ax.text(6.25, 5.6, '阶段二:用户数字画像构建', fontsize=13, fontweight='bold',
ha='center', va='center', color='#F24236')
# 步骤1:特征转化(调整x坐标,避免拥挤)
ax.text(5.0, 5.2, '步骤1:可量化特征转化', fontsize=11, fontweight='bold', color='#A62639')
ax.text(5.0, 5.0, '下单频率 | 客单价 | 会员等级 | 优惠券使用', fontsize=9, color='#333')
# 步骤2:分类算法聚类(下移y坐标)
ax.text(5.0, 4.6, '步骤2:分类算法聚类', fontsize=11, fontweight='bold', color='#A62639')
ax.text(5.0, 4.4, '逻辑回归/决策树 → 划分3类用户', fontsize=9, color='#333')
ax.text(5.0, 4.2, '• 高忠诚度高消费用户 • 价格敏感型普通用户 • 低频尝鲜用户', fontsize=8, color='#333')
# 步骤3:标签赋值(继续下移)
ax.text(5.0, 3.8, '步骤3:差异化标签赋值', fontsize=11, fontweight='bold', color='#A62639')
ax.text(5.0, 3.6, '• 高忠诚度→价格不敏感 • 普通用户→价格敏感 • 尝鲜用户→潜在敏感', fontsize=8, color='#333')
# 画像库节点(放大尺寸,文字换行显示)
portrait_lib = patches.Ellipse((6.25, 3.3), 1.5, 0.5, linewidth=2, edgecolor='#F24236',
facecolor='#FFFFFF', zorder=5)
ax.add_patch(portrait_lib)
ax.text(6.25, 3.4, '用户数字画像库', fontsize=10, fontweight='bold',
ha='center', va='center', color='#F24236')
ax.text(6.25, 3.2, '(含价格敏感度标签)', fontsize=9,
ha='center', va='center', color='#F24236')
# -------------------------- 5. 数据流转箭头(调整角度,避免遮挡) --------------------------
arrow2 = FancyArrowPatch((7.0, 3.3), (8.2, 3.3),
arrowstyle='->', mutation_scale=18, color='#666', linewidth=2, zorder=3)
ax.add_patch(arrow2)
ax.text(7.6, 3.4, '画像输入:标签→定价算法', fontsize=9, ha='center', va='center', color='#666')
# 算法输入节点(放大尺寸)
input_node = patches.Rectangle((7.9, 3.0), 0.8, 0.6, linewidth=2, edgecolor='#FBBF24',
facecolor='#FFF9E6', zorder=5)
ax.add_patch(input_node)
ax.text(8.3, 3.3, '动态定价', fontsize=9, fontweight='bold', ha='center', va='center', color='#FBBF24')
ax.text(8.3, 3.0, '算法输入', fontsize=9, fontweight='bold', ha='center', va='center', color='#FBBF24')
# -------------------------- 6. 第三阶段:三维动态定价(拆分文本,避免重叠) --------------------------
# 阶段框(扩大面积)
stage3 = patches.Rectangle((8.5, 1.8), 2.2, 2.2, linewidth=2, edgecolor='#FBBF24',
facecolor='#FFF9E6', alpha=0.8)
ax.add_patch(stage3)
ax.text(9.6, 3.6, '阶段三:三维动态定价', fontsize=13, fontweight='bold',
ha='center', va='center', color='#FBBF24')
ax.text(9.6, 3.4, '(需求-成本-敏感度)', fontsize=10, ha='center', va='center', color='#FBBF24')
# 维度1:基础配送费(分行显示,增大间距)
ax.text(8.8, 3.1, '维度1:基础配送费', fontsize=10, fontweight='bold', color='#92400E')
ax.text(8.8, 2.9, '• 区域订单量 • 配送员数量', fontsize=8, color='#333')
ax.text(8.8, 2.7, '• 时段调整:早高峰+2元', fontsize=8, color='#333')
# 维度2:敏感度系数(下移)
ax.text(8.8, 2.3, '维度2:敏感度系数', fontsize=10, fontweight='bold', color='#92400E')
ax.text(8.8, 2.1, '• 不敏感用户:+5%-15%', fontsize=8, color='#333')
ax.text(8.8, 1.9, '• 敏感用户:-3%-8%', fontsize=8, color='#333')
# 维度3:实时场景(右移x坐标)
ax.text(10.0, 3.1, '维度3:实时场景', fontsize=10, fontweight='bold', color='#92400E')
ax.text(10.0, 2.9, '• 恶劣天气 • 紧急订单', fontsize=8, color='#333')
ax.text(10.0, 2.7, '• 节假日需求波动', fontsize=8, color='#333')
# 价格输出节点(放大,文字加粗)
price_output = patches.Rectangle((9.2, 1.5), 1.0, 0.4, linewidth=2, edgecolor='#FBBF24',
facecolor='#FFFFFF', zorder=5)
ax.add_patch(price_output)
ax.text(9.7, 1.7, '输出最终配送费', fontsize=10, fontweight='bold',
ha='center', va='center', color='#FBBF24')
# -------------------------- 7. 最终杀熟案例对比(扩大案例框,加粗关键数据) --------------------------
# 案例1:会员用户
case1 = patches.Rectangle((0.5, 1.0), 3.0, 0.6, linewidth=2, edgecolor='#666',
facecolor='#F5F5F5', zorder=5)
ax.add_patch(case1)
ax.text(2.0, 1.4, '案例1:高忠诚度会员(早高峰)', fontsize=10, fontweight='bold',
ha='center', va='center', color='#333')
ax.text(2.0, 1.1, '基础3元 + 10%上浮 → 3.3元', fontsize=9, fontweight='bold',
ha='center', va='center', color='#D32F2F') # 关键价格加粗标红
# 案例2:普通用户
case2 = patches.Rectangle((4.0, 1.0), 3.0, 0.6, linewidth=2, edgecolor='#666',
facecolor='#F5F5F5', zorder=5)
ax.add_patch(case2)
ax.text(5.5, 1.4, '案例2:普通价格敏感用户(同期)', fontsize=10, fontweight='bold',
ha='center', va='center', color='#333')
ax.text(5.5, 1.1, '基础3元 + 3%上浮 → 3.09元', fontsize=9, fontweight='bold',
ha='center', va='center', color='#1976D2') # 关键价格加粗标蓝
# 杀熟结果节点(加背景色突出)
result = patches.Rectangle((7.8, 1.0), 2.8, 0.6, linewidth=2, edgecolor='#F24236',
facecolor='#FFE6E6', zorder=5)
ax.add_patch(result)
ax.text(9.2, 1.4, '最终结果:形成价差,实现“大数据杀熟”', fontsize=11, fontweight='bold',
ha='center', va='center', color='#F24236')
# -------------------------- 8. 图例(放大字号,调整位置) --------------------------
legend_elements = [
patches.Patch(color='#E6F3FF', edgecolor='#2E86AB', label='阶段一:多渠道数据采集'),
patches.Patch(color='#FFE6E6', edgecolor='#F24236', label='阶段二:用户数字画像构建'),
patches.Patch(color='#FFF9E6', edgecolor='#FBBF24', label='阶段三:三维动态定价')
]
ax.legend(handles=legend_elements, loc='upper left', bbox_to_anchor=(0.02, 0.98),
fontsize=11, frameon=True, facecolor='#FFFFFF') # 图例加白色背景,提升可读性
# 保存图片(确保文字完整容纳)
plt.savefig('meituan_tech_roadmap_fixed.png', bbox_inches='tight', dpi=150, facecolor='white')
plt.close()
print("修复版技术路线图已生成,保存为:meituan_tech_roadmap_fixed.png")