import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from itertools import combinations
import random
import time
import matplotlib as mpl
from tqdm import tqdm
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Micro Hei']
plt.rcParams['axes.unicode_minus'] = False
# ====================================================
# 任务1: 订单需求分析
# ====================================================
print("正在执行任务1: 订单需求分析...")
# 加载订单数据
demand_df = pd.read_csv('OrderDemand.csv')
# 计算每个区域的总需求和日均需求
region_demand = demand_df.groupby('Region')['OrderVolume'].sum().reset_index()
region_demand['AvgDailyDemand'] = region_demand['OrderVolume'] / 3
region_demand_sorted = region_demand.sort_values(by='AvgDailyDemand', ascending=False)
# 计算每个小时的总需求
hourly_demand = demand_df.groupby('Hour')['OrderVolume'].sum().reset_index()
hourly_demand['AvgHourlyDemand'] = hourly_demand['OrderVolume'] / 3
# 识别高峰时段
peak_hours = hourly_demand.nlargest(3, 'AvgHourlyDemand')['Hour'].tolist()
# 输出分析结果
print("\n=== 高需求区域Top 5 ===")
print(region_demand_sorted.head(5))
print("\n=== 高峰时段 ===")
print(f"高峰时段(小时): {peak_hours}")
# 可视化需求分析
plt.figure(figsize=(14, 7))
bars = plt.bar(region_demand_sorted['Region'], region_demand_sorted['AvgDailyDemand'], color='skyblue')
plt.title('区域日均需求分析', fontsize=16)
plt.xlabel('区域', fontsize=12)
plt.ylabel('日均需求', fontsize=12)
plt.xticks(rotation=45)
plt.grid(axis='y', linestyle='--', alpha=0.7)
# 添加数据标签
for bar in bars:
height = bar.get_height()
plt.annotate(f'{height:.1f}',
xy=(bar.get_x() + bar.get_width() / 2, height),
xytext=(0, 3),
textcoords="offset points",
ha='center', va='bottom', fontsize=9)
plt.tight_layout()
plt.savefig('region_demand.png', dpi=300)
plt.close()
plt.figure(figsize=(14, 7))
plt.plot(hourly_demand['Hour'], hourly_demand['AvgHourlyDemand'],
marker='o', linestyle='-', color='royalblue', linewidth=2.5)
plt.title('小时平均需求分布', fontsize=16)
plt.xlabel('小时', fontsize=12)
plt.ylabel('平均需求', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)
plt.xticks(range(0, 24))
# 标记高峰时段
for i, hour in enumerate(peak_hours):
plt.axvline(x=hour, color='r', linestyle='--', alpha=0.8)
plt.text(hour, max(hourly_demand['AvgHourlyDemand']) * (0.85 - i*0.1),
f'高峰 {hour}:00',
rotation=90, ha='right', va='top', color='r', fontsize=11,
bbox=dict(facecolor='white', alpha=0.8, edgecolor='none'))
plt.legend(['平均需求', '高峰时段'], loc='upper left')
plt.tight_layout()
plt.savefig('hourly_demand.png', dpi=300)
plt.close()
# ====================================================
# 任务2: 网络优化模型
# ====================================================
print("\n正在执行任务2: 网络优化模型...")
# 加载距离矩阵
dist_matrix = pd.read_csv('DistanceMatrix.csv', index_col=0)
# 分配区域到最近的DC
dc_allocation = {}
for region in dist_matrix.columns:
closest_dc = dist_matrix[region].idxmin()
dc_allocation[region] = closest_dc
# 为每个DC创建服务区域列表
dc_regions = {dc: [] for dc in dist_matrix.index}
for region, dc in dc_allocation.items():
dc_regions = dc_regions[dc]
if region not in dc_regions:
dc_regions[dc].append(region)
# 生成区域坐标(模拟位置)
np.random.seed(42)
positions = {}
for dc in dist_matrix.index:
positions[dc] = (random.uniform(0, 10), random.uniform(0, 10))
for region in dc_regions[dc]:
# 区域在DC附近随机分布
positions[region] = (
positions[dc][0] + random.uniform(-2, 2),
positions[dc][1] + random.uniform(-2, 2)
)
# 计算区域间距离
def calculate_distance(pos1, pos2):
return np.sqrt((pos1[0]-pos2[0])**2 + (pos1[1]-pos2[1])**2)
# 最近邻算法求解TSP
def nearest_neighbor_tsp(nodes, positions):
if not nodes:
return []
unvisited = nodes.copy()
current = unvisited.pop(0)
tour = [current]
while unvisited:
# 找到最近邻居
nearest = min(unvisited, key=lambda x: calculate_distance(positions[current], positions[x]))
tour.append(nearest)
unvisited.remove(nearest)
current = nearest
return tour
# 为每个DC规划路径
dc_paths = {}
for dc, regions in dc_regions.items():
if regions:
# 包含DC的节点列表
nodes = [dc] + regions
path = nearest_neighbor_tsp(nodes, positions)
dc_paths[dc] = path
else:
dc_paths[dc] = [dc]
# 计算路径总距离
def calculate_path_distance(path, positions):
if len(path) < 2:
return 0
distance = 0
for i in range(len(path)-1):
distance += calculate_distance(positions[path[i]], positions[path[i+1]])
# 添加返回起点的距离
distance += calculate_distance(positions[path[-1]], positions[path[0]])
return distance
# 输出任务2结果
print("\n=== DC分配结果 ===")
for dc, regions in dc_regions.items():
print(f"{dc} 服务区域: {regions}")
print("\n=== 配送路径 ===")
total_distance = 0
for dc, path in dc_paths.items():
path_distance = calculate_path_distance(path, positions)
total_distance += path_distance
path_str = ' → '.join(path) + f" → {path[0]}" if len(path) > 1 else path[0]
print(f"{dc} 路径: {path_str} | 距离: {path_distance:.2f} km")
print(f"总配送距离: {total_distance:.2f} km")
# ====================================================
# 任务3: 多车辆路径调度模型 (修复版)
# ====================================================
print("\n正在执行任务3: 多车辆路径调度模型...")
# 加载车辆信息
vehicle_df = pd.read_csv('VehicleInfo.csv')
vehicles = vehicle_df.to_dict('records')
# 区域需求数据(来自任务1)
region_demands = region_demand.set_index('Region')['AvgDailyDemand'].to_dict()
# 增强版节约算法
def enhanced_savings_algorithm(depot, nodes, demands, positions, vehicles, service_time_per_demand=5):
# 如果没有节点,返回空列表
if not nodes:
return []
# 创建可用车辆列表的深拷贝
available_vehicles = deepcopy(vehicles)
routes = []
assigned_nodes = set()
# 步骤1: 创建初始路线(每个节点一条路线)
for node in nodes:
# 计算单点路线的需求和时间
total_demand = demands[node]
travel_time = (calculate_distance(positions[depot], positions[node]) * 2) * 1.5 # 往返时间
service_time = total_demand * service_time_per_demand
total_time = travel_time + service_time
# 寻找合适的车辆
vehicle_found = False
for i, vehicle in enumerate(available_vehicles):
if vehicle['Capacity'] >= total_demand and vehicle['MaxServiceTime'] >= total_time:
routes.append({
'nodes': [node],
'demand': total_demand,
'time': total_time,
'vehicle_id': vehicle['VehicleID']
})
assigned_nodes.add(node)
# 移除已使用的车辆
del available_vehicles[i]
vehicle_found = True
break
# 如果没有找到合适的车辆,使用最小容量的车辆(即使超载)
if not vehicle_found and available_vehicles:
min_capacity_vehicle = min(available_vehicles, key=lambda x: x['Capacity'])
routes.append({
'nodes': [node],
'demand': total_demand,
'time': total_time,
'vehicle_id': min_capacity_vehicle['VehicleID']
})
assigned_nodes.add(node)
available_vehicles.remove(min_capacity_vehicle)
# 步骤2: 计算节约值矩阵
savings = []
for i, j in combinations(nodes, 2):
# 如果两个点都已被分配,计算节约值
if i in assigned_nodes and j in assigned_nodes:
saving = calculate_distance(positions[depot], positions[i]) + \
calculate_distance(positions[depot], positions[j]) - \
calculate_distance(positions[i], positions[j])
savings.append((i, j, saving))
# 按节约值降序排序
savings.sort(key=lambda x: x[2], reverse=True)
# 步骤3: 合并路线
for i, j, saving in savings:
# 找到包含i和j的路线
route_i = next((r for r in routes if i in r['nodes']), None)
route_j = next((r for r in routes if j in r['nodes']), None)
# 确保是两条不同的路线
if route_i is None or route_j is None or route_i == route_j:
continue
# 尝试不同的合并方式
merge_success = False
# 方式1: i_end -> j_start
if route_i['nodes'][-1] == i and route_j['nodes'][0] == j:
new_nodes = route_i['nodes'] + route_j['nodes']
merge_success = True
# 方式2: j_end -> i_start
elif route_j['nodes'][-1] == j and route_i['nodes'][0] == i:
new_nodes = route_j['nodes'] + route_i['nodes']
merge_success = True
# 方式3: i_end -> j_end (需要反转j路线)
elif route_i['nodes'][-1] == i and route_j['nodes'][-1] == j:
new_nodes = route_i['nodes'] + list(reversed(route_j['nodes']))
merge_success = True
# 方式4: i_start -> j_start (需要反转i路线)
elif route_i['nodes'][0] == i and route_j['nodes'][0] == j:
new_nodes = list(reversed(route_j['nodes'])) + route_i['nodes']
merge_success = True
if merge_success:
# 计算新路线的总需求
total_demand = sum(demands[node] for node in new_nodes)
# 计算新路线的总时间
path_distance = calculate_path_distance([depot] + new_nodes + [depot], positions)
travel_time = path_distance * 1.5 # 速度40km/h,转换为分钟
service_time = total_demand * service_time_per_demand
total_time = travel_time + service_time
# 检查车辆是否满足新路线的需求
current_vehicle_id = route_i['vehicle_id']
current_vehicle = next((v for v in vehicles if v['VehicleID'] == current_vehicle_id), None)
if current_vehicle and current_vehicle['Capacity'] >= total_demand and current_vehicle['MaxServiceTime'] >= total_time:
# 合并路线
new_route = {
'nodes': new_nodes,
'demand': total_demand,
'time': total_time,
'vehicle_id': current_vehicle_id
}
# 移除旧路线,添加新路线
routes.remove(route_i)
routes.remove(route_j)
routes.append(new_route)
# 释放route_j的车辆
released_vehicle_id = route_j['vehicle_id']
if released_vehicle_id != current_vehicle_id:
released_vehicle = next((v for v in vehicles if v['VehicleID'] == released_vehicle_id), None)
if released_vehicle:
available_vehicles.append(released_vehicle)
# 返回格式化结果
result = []
for route in routes:
result.append((
route['vehicle_id'],
route['nodes'],
route['demand'],
route['time']
))
return result
# 为每个DC规划多车辆路径
dc_vehicle_routes = {}
start_time = time.time()
for dc, regions in tqdm(dc_regions.items(), desc="规划车辆路径"):
if regions:
routes = enhanced_savings_algorithm(dc, regions, region_demands, positions, vehicles)
dc_vehicle_routes[dc] = routes
else:
dc_vehicle_routes[dc] = []
# 输出任务3结果
print("\n=== 多车辆路径调度结果 ===")
total_vehicles_used = 0
total_vehicles_available = len(vehicles)
for dc, routes in dc_vehicle_routes.items():
if not routes:
print(f"\n{dc} 车辆调度方案: 无可用路径")
continue
print(f"\n{dc} 车辆调度方案:")
for vehicle_id, route_nodes, demand, time_used in routes:
total_vehicles_used += 1
# 获取车辆信息
vehicle = next((v for v in vehicles if v['VehicleID'] == vehicle_id), None)
if not vehicle:
vehicle_capacity = "未知"
vehicle_maxtime = "未知"
else:
vehicle_capacity = vehicle['Capacity']
vehicle_maxtime = vehicle['MaxServiceTime']
# 计算利用率
capacity_util = (demand / vehicle_capacity * 100) if vehicle_capacity > 0 else 0
time_util = (time_used / vehicle_maxtime * 100) if vehicle_maxtime > 0 else 0
# 打印结果
route_str = f"{dc} → {' → '.join(route_nodes)} → {dc}"
print(f" 车辆 {vehicle_id}:")
print(f" 路径: {route_str}")
print(f" 需求: {demand:.2f} / {vehicle_capacity} ({capacity_util:.1f}%)")
print(f" 时间: {time_used:.2f} min / {vehicle_maxtime} min ({time_util:.1f}%)")
# 计算总体利用率
utilization_rate = (total_vehicles_used / total_vehicles_available * 100) if total_vehicles_available > 0 else 0
print(f"\n总使用车辆数: {total_vehicles_used} / {total_vehicles_available}")
print(f"车辆利用率: {utilization_rate:.1f}%")
print(f"路径规划耗时: {time.time()-start_time:.2f}秒")
# ====================================================
# 保存结果
# ====================================================
# 保存任务1结果
region_demand_sorted.to_csv('region_demand_analysis.csv', index=False)
hourly_demand.to_csv('hourly_demand_analysis.csv', index=False)
# 保存任务2结果
dc_allocation_df = pd.DataFrame({
'Region': list(dc_allocation.keys()),
'AssignedDC': list(dc_allocation.values())
})
dc_allocation_df.to_csv('dc_allocation.csv', index=False)
# 保存任务3结果
vehicle_routes_data = []
for dc, routes in dc_vehicle_routes.items():
for vehicle_id, route_nodes, demand, time_used in routes:
vehicle = next((v for v in vehicles if v['VehicleID'] == vehicle_id), None)
vehicle_capacity = vehicle['Capacity'] if vehicle else 0
vehicle_routes_data.append({
'DC': dc,
'VehicleID': vehicle_id,
'Route': ' → '.join([dc] + route_nodes + [dc]),
'TotalDemand': demand,
'VehicleCapacity': vehicle_capacity,
'CapacityUtilization(%)': (demand / vehicle_capacity * 100) if vehicle_capacity > 0 else 0,
'TotalTime(min)': time_used,
'MaxServiceTime(min)': vehicle['MaxServiceTime'] if vehicle else 0,
'TimeUtilization(%)': (time_used / vehicle['MaxServiceTime'] * 100) if vehicle else 0
})
vehicle_routes_df = pd.DataFrame(vehicle_routes_data)
vehicle_routes_df.to_csv('vehicle_routes.csv', index=False)
# 生成配送网络图
plt.figure(figsize=(16, 12))
# 创建颜色映射
dc_colors = {}
color_palette = plt.cm.tab10.colors
for idx, dc in enumerate(dist_matrix.index):
dc_colors[dc] = color_palette[idx % len(color_palette)]
# 绘制DC位置
for dc in dist_matrix.index:
pos = positions[dc]
plt.scatter(pos[0], pos[1], s=400, c=[dc_colors[dc]], marker='s', edgecolor='k', linewidth=2, zorder=10)
plt.text(pos[0], pos[1], dc, fontsize=14, ha='center', va='center', fontweight='bold', zorder=11)
# 绘制区域位置和路径
for dc, routes in dc_vehicle_routes.items():
color = dc_colors[dc]
# 绘制该DC下的所有区域
for region in dc_regions[dc]:
pos = positions[region]
plt.scatter(pos[0], pos[1], s=120, c=[color], marker='o', alpha=0.8, zorder=9)
plt.text(pos[0], pos[1], region, fontsize=10, ha='center', va='bottom', zorder=9)
# 绘制该DC的所有车辆路径
for route in routes:
_, route_nodes, _, _ = route
path = [dc] + route_nodes + [dc]
# 绘制路径线
for i in range(len(path)-1):
start = positions[path[i]]
end = positions[path[i+1]]
plt.plot([start[0], end[0]], [start[1], end[1]], c=color, linestyle='-',
linewidth=1.5, alpha=0.7, zorder=5)
# 标记路径起点
plt.scatter(positions[dc][0], positions[dc][1], s=150, c='red', marker='*', zorder=12)
# 添加图例和标题
plt.title('物流配送网络与车辆路径规划', fontsize=18)
plt.xlabel('X 坐标', fontsize=14)
plt.ylabel('Y 坐标', fontsize=14)
plt.grid(True, linestyle='--', alpha=0.5)
# 创建自定义图例
from matplotlib.lines import Line2D
legend_elements = [
Line2D([0], [0], marker='s', color='w', label='配送中心(DC)',
markerfacecolor='red', markersize=12),
Line2D([0], [0], marker='o', color='w', label='配送区域',
markerfacecolor='blue', markersize=10),
Line2D([0], [0], marker='*', color='w', label='路径起点',
markerfacecolor='red', markersize=12),
Line2D([0], [0], color='green', lw=2, label='配送路径')
]
plt.legend(handles=legend_elements, loc='upper left', fontsize=12)
plt.tight_layout()
plt.savefig('delivery_network.png', dpi=300)
plt.close()
print("\n所有结果已保存到CSV文件和图表") TypeError: list indices must be integers or slices, not str给我解决这个问题的完整代码