2023年第二十届五一数学建模竞赛模
B题:快递需求分析问题
by:公从号:数模孵化园
整体看来很复杂,问题特别多,但是这种特别多的题目一般是循序渐进做的,比如说第一问用这个模型,第二问就会在第一问的模型上改个参数,或者改个变量来进行。每一小问难度不大,主要是计算量。比如问题二和问题三模型都差不多,就变量少许不同。
问题1:附件1为该快递公司记录的2018年4月19日—2019年4月17日的站点城市之间(发货城市-收货城市)的快递运输数据,请从收货量、发货量、快递数量增长/减少趋势、相关性等多角度考虑,建立数学模型,对各站点城市的重要程度进行综合排序,并给出重要程度排名前5的站点城市名称,将结果填入表1。
表1 问题1结果
排序 |
1 |
2 |
3 |
4 |
5 |
城市名称 |
附件一有四列,每列的名字分别是:
日期(年/月/日) (Date Y/M/D)
发货城市 (Delivering city)
收货城市 (Receiving city)
快递运输数量(件) (Express delivery quantity (PCS))
我懒得去算了,这题不难,纯计算项目,比较花时间给出一个通用的计算方法:
问题1:对各站点城市的重要程度进行综合排序。
步骤1:数据预处理
从附件1中提取数据,整理为如下格式:
发货城市 (Delivering city)
收货城市 (Receiving city)
发货量 (Total Delivering)
收货量 (Total Receiving)
快递运输数量 (Express delivery quantity)
步骤2:计算指标
对于每个城市,计算以下指标:
发货量占总发货量的百分比
收货量占总收货量的百分比
快递运输数量占总运输量的百分比
步骤3:计算增长/减少趋势
对于每个城市,根据时间序列数据计算以下增长/减少趋势:
发货量增长/减少趋势
收货量增长/减少趋势
快递运输数量增长/减少趋势
步骤4:计算相关性
对于每个城市,计算其与其他城市的快递运输数量相关性。平均相关性将作为该城市的一个指标。
步骤5:综合评分
对于每个城市,根据其在各指标上的表现,计算一个综合评分。综合评分可以使用加权平均法,将各指标按照一定的权重进行加权求和。具体权重可以根据实际情况进行调整。
步骤6:排序
根据综合评分,对所有城市进行降序排序。最高分的城市即为最重要的城市。
最终,根据步骤6的排序结果,填入表1中的城市名称。
问题二:
问题2:请利用附件1数据,建立数学模型,预测2019年4月18日和2019年4月19日各“发货-收货”站点城市之间快递运输数量,以及当日所有“发货-收货”站点城市之间的总快递运输数量,并在表2中填入指定的站点城市之间的快递运输数量,以及当日所有“发货-收货”站点城市之间的总快递运输数量。
预测2019年4月18日和2019年4月19日各“发货-收货”站点城市之间快递运输数量,以及当日所有“发货-收货”站点城市之间的总快递运输数量。
我们可以使用时间序列分析方法(例如ARIMA模型、指数平滑法、Facebook Prophet等)对历史快递运输数量数据进行预测。
步骤1:数据预处理
从附件1中提取数据,整理为如下格式:
日期 (Date Y/M/D)
发货城市-收货城市 (Delivering-Receiving city pair)
快递运输数量(件) (Express delivery quantity (PCS))
步骤2:创建时间序列数据
将数据按照日期排序,并为每个城市对创建一个时间序列数据集。
步骤3:建立预测模型
对每个城市对的时间序列数据,使用时间序列分析方法(如ARIMA、指数平滑法、Facebook Prophet等)建立预测模型。
步骤4:预测未来快递运输数量
使用步骤3中建立的预测模型,分别预测2019年4月18日和2019年4月19日的快递运输数量。
步骤5:计算总运输数量
将所有城市对在2019年4月18日和2019年4月19日的预测运输数量相加,得到当日所有“发货-收货”站点城市之间的总快递运输数量。
步骤6:填入表格
将步骤4和步骤5得到的预测结果填入表2中。
ARIMA预测的具体步骤和代码:
导入所需库:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
读取并预处理数据:
# 读取CSV数据
data = pd.read_csv('附件1.csv')
# 数据预处理:以日期为索引,发货-收货城市对为列名,快递运输数量为值
data_pivot = data.pivot_table(index='Date Y/M/D', columns='Delivering city-Receiving city', values='Express delivery quantity (PCS)')
data_pivot.index = pd.to_datetime(data_pivot.index)
确定ARIMA模型的参数(p, d, q):
# 选择一个城市对进行分析
city_pair = '城市A-城市B'
ts = data_pivot[city_pair].dropna()
# 判断时间序列是否平稳,使用ADF检验
def test_stationarity(timeseries):
adftest = adfuller(timeseries, autolag='AIC')
return adftest[1] # 返回p-value
# 若时间序列非平稳,进行差分操作
d = 0
while test_stationarity(ts) > 0.05:
ts = ts.diff().dropna()
d += 1
# 计算ACF和PACF,以确定p和q
fig, ax = plt.subplots(2, 1)
plot_acf(ts, ax=ax[0])
plot_pacf(ts, ax=ax[1])
plt.show()
根据ACF和PACF图,可以确定p和q的大致范围。
接着要使用网格搜索选择最佳参数:
import itertools
from statsmodels.tsa.arima.model import ARIMA
# 确定参数搜索范围
p_range = range(0, 3) # 可以根据PACF图调整
q_range = range(0, 3) # 可以根据ACF图调整
pdq = list(itertools.product(p_range, [d], q_range))
# 网格搜索寻找最佳参数
best_aic = np.inf
best_param = None
for param in pdq:
try:
model = ARIMA(ts, order=param)
results = model.fit()
if results.aic < best_aic:
best_aic = results.aic
best_param = param
except:
continue
print(f'Best parameters (p, d, q): {best_param}')
使用最佳参数建立ARIMA模型并进行预测:
# 使用最佳参数建立ARIMA模型
best_model = ARIMA(ts, order=best_param)
results =
问题三:附件2为该快递公司记录的2020年4月28日—2023年4月27日的快递运输数量。由于受到突发事件影响,部分城市之间快递线路无法正常运输,导致站点城市之间无法正常发货或收货(无数据表示无法正常收发货,0表示无发货需求)。请利用附件2数据,建立数学模型,预测2023年4月28日和2023年4月29日可正常“发货-收货”的站点城市对(发货城市-收货城市),并判断表3中指定的站点城市对是否能正常发货,如果能正常发货,给出对应的快递运输数量,并将结果填入表3。
问题三很枯燥,跟第二问一样:
预测2023年4月28日和2023年4月29日可正常“发货-收货”的站点城市对,并判断表3中指定的站点城市对是否能正常发货。
我们可以使用时间序列分析方法(例如ARIMA模型、指数平滑法、Facebook Prophet等)对历史快递运输数量数据进行预测,并结合突发事件对城市之间快递线路的影响,判断站点城市对是否能正常发货。
步骤1:数据预处理
从附件2中提取数据,整理为如下格式:
日期 (Date Y/M/D)
发货城市-收货城市 (Delivering-Receiving city pair)
快递运输数量(件) (Express delivery quantity (PCS))
步骤2:创建时间序列数据
将数据按照日期排序,并为每个城市对创建一个时间序列数据集。
步骤3:建立预测模型
对每个城市对的时间序列数据,使用时间序列分析方法(如ARIMA、指数平滑法、Facebook Prophet等)建立预测模型。
步骤4:预测未来快递运输数量
使用步骤3中建立的预测模型,分别预测2023年4月28日和2023年4月29日的快递运输数量。
步骤5:考虑突发事件影响
结合突发事件对城市之间快递线路的影响,判断站点城市对是否能正常发货。如果某个城市对受到突发事件影响无法正常发货或收货,则将其预测快递运输数量设为0或缺失值。
步骤6:填入表格
将步骤5得到的预测结果填入表3中
问题4:图1给出了所有站点城市间的铁路运输网络,铁路运输成本由以下公式计算:成本=固定成本×1+实际装货量额定装货量3。在本题中,假设实际装货量允许超过额定装货量。所有铁路的固定成本、额定装货量在附件3中给出。在运输快递时,要求每个“发货-收货”站点城市对之间使用的路径数不超过5条,请建立数学模型,给出该快递公司成本最低的运输方案。利用附件2和附件3的数据,计算该公司2023年4月23—27日每日的最低运输成本,填入表4。
备注:为了方便计算,不对快递重量和大小进行区分,假设每件快递的重量为单位1。仅考虑运输成本,不考虑中转等其它成本。
这题有意思。
我们可以将其看作一个运输网络优化问题。
介绍一下:
运输网络优化问题是指在给定的物流运输网络中,如何最优化地安排物流运输活动,以实现最大效益、最小成本或最快速度等目标。具体来说,它包括如何选择运输路线、运输方式、运输车辆、运输容器、装载方案、配送路线等问题。
在运输网络优化问题中,需要考虑许多因素,例如货物的种类和数量、运输距离、交通状况、天气因素、运输成本、运输时间、配送顺序等。解决这些问题的关键是建立数学模型,利用运筹学和优化理论等方法进行求解,以实现最优的物流配送方案。
运输网络优化问题在物流管理、供应链管理等领域中具有广泛应用,可以帮助企业实现更高效、更经济、更可靠的物流运输服务。
在本问中,我们使用线性规划(Linear Programming)方法可以求解该问题。
具体代码:
导入所需库:
import numpy as np
import pandas as pd
from scipy.optimize import linprog
读取并预处理数据
# 读取附件2数据
data2 = pd.read_csv('附件2.csv')
# 数据预处理:以日期为索引,发货-收货城市对为列名,快递运输数量为值
data2_pivot = data2.pivot_table(index='Date Y/M/D', columns='Delivering city-Receiving city', values='Express delivery quantity (PCS)')
data2_pivot.index = pd.to_datetime(data2_pivot.index)
# 读取附件3数据
data3 = pd.read_csv('附件3.csv')
根据给定的铁路运输网络和成本计算方法,构建成本矩阵:
# 获取所有城市
cities = list(set(data3['City A']).union(set(data3['City B'])))
# 构建成本矩阵
num_cities = len(cities)
cost_matrix = np.full((num_cities, num_cities), np.inf)
for index, row in data3.iterrows():
city_a = cities.index(row['City A'])
city_b = cities.index(row['City B'])
fixed_cost = row['Fixed cost']
rated_capacity = row['Rated capacity']
cost_matrix[city_a, city_b] = fixed_cost * (1 + (data2_pivot.loc[:, row['City A'] + '-' + row['City B']].sum() / rated_capacity)**3)
cost_matrix[city_b, city_a] = cost_matrix[city_a, city_b]
根据成本矩阵和需求数据,使用线性规划方法求解每日最低运输成本:
# 定义线性规划问题
def min_transportation_cost(cost_matrix, demand_matrix):
num_cities = cost_matrix.shape[0]
# 目标函数
c = cost_matrix.flatten()
# 约束条件
A_eq = []
b_eq = []
for i in range(num_cities):
for j in range(num_cities):
if i != j:
constraint = np.zeros((num_cities, num_cities))
constraint[i, j] = 1
constraint[j, i] = -1
A_eq.append(constraint.flatten())
b_eq.append(demand_matrix[i, j])
A_eq = np.array(A_eq)
b_eq = np.array(b_eq)
# 求解线性规划问题
result = linprog(c, A_eq=A_eq, b_eq=b_eq)
return result.fun
# 计算每日最低运输成本
daily_min_cost = []
for date in pd.date_range('2023-04-23', '2023-04-27'):
demand_matrix = np.zeros((num_cities, num_cities))
for city_pair, quantity in data2_pivot.loc[date].items():
city_a, city_b = city_pair.split('-')
demand_matrix[cities.index(city_a), cities.index(city_b
计算每日最低运输成本:
demand_matrix[cities.index(city_a), cities.index(city_b)] = quantity
daily_min_cost.append(min_transportation_cost(cost_matrix, demand_matrix))
# 将结果填入表4
table4 = pd.DataFrame({'Date': pd.date_range('2023-04-23', '2023-04-27'), 'Minimum Transportation Cost': daily_min_cost})
问题5:通常情况下,快递需求由两部分组成,一部分为固定需求,这部分需求来源于日常必要的网购消费(一般不能简单的认定为快递需求历史数据的最小值,通常小于需求的最小值);另一部分为非固定需求,这部分需求通常有较大波动,受时间等因素的影响较大。假设在同一季度中,同一“发货-收货”站点城市对的固定需求为一确定常数(以下简称为固定需求常数);同一“发货-收货”站点城市对的非固定需求服从某概率分布(该分布的均值和标准差分别称为非固定需求均值、非固定需求标准差)。请利用附件2中的数据,不考虑已剔除数据、无发货需求数据、无法正常发货数据,解决以下问题。
这题计算就行。傻瓜式操作:
首先,从附件2中提取数据并进行预处理,以便于分析。根据题目要求,我们将剔除无发货需求数据和无法正常发货数据。
将数据分为四个季度,并计算每个季度每个“发货-收货”站点城市对的最小值。我们将这些最小值作为固定需求常数的初始估计。
为了验证固定需求常数的准确性,我们将固定需求常数从每个季度的每个“发货-收货”站点城市对的实际需求中减去。然后,我们将计算每个季度的非固定需求的均值和标准差。我们可以进一步调整固定需求常数,以使非固定需求的均值和标准差在各个季度之间保持一致。例如,可以尝试使用不同的百分位数作为固定需求常数的初始估计,然后观察哪个百分位数能使非固定需求的均值和标准差在各个季度之间最接近。使用最终确定的固定需求常数填入表5。
代码:
import pandas as pd
import numpy as np
# 读取附件2数据
data2 = pd.read_csv('附件2.csv')
# 数据预处理:以日期为索引,发货-收货城市对为列名,快递运输数量为值
data2_pivot = data2.pivot_table(index='Date Y/M/D', columns='Delivering city-Receiving city', values='Express delivery quantity (PCS)')
data2_pivot.index = pd.to_datetime(data2_pivot.index)
# 剔除无发货需求数据和无法正常发货数据
data2_pivot = data2_pivot.dropna(axis=1)
# 将数据按季度分组
data2_pivot_quarterly = data2_pivot.resample('Q').agg(['min', 'mean', 'std'])
# 计算固定需求常数
fixed_demand_constants = data2_pivot_quarterly.loc[:, ('min', slice(None))]
# 验证固定需求常数的准确性
non_fixed_demand = data2_pivot - fixed_demand_constants.values
non_fixed_demand_quarterly = non_fixed_demand.resample('Q').agg(['mean', 'std'])
# 填写表5
table5 = fixed_demand_constants
给出非固定需求概率分布估计方法,并将指定季度、指定“发货-收货”站点城市对的非固定需求均值、标准差,以及当季度所有“发货-收货”城市对的非固定需求均值总和、非固定需求标准差总和,填入表5。
为了估计非固定需求的概率分布,我们可以使用最大似然估计(MLE)方法。首先,我们需要计算每个季度每个“发货-收货”站点城市对的非固定需求。接下来,我们可以尝试不同的概率分布(例如正态分布、泊松分布等)来拟合非固定需求数据,使用最大似然估计得到概率分布的参数。
具体代码:
import scipy.stats as stats
# 计算每个季度每个“发货-收货”站点城市对的非固定需求
non_fixed_demand = data2_pivot - fixed_demand_constants.values
non_fixed_demand_quarterly = non_fixed_demand.resample('Q').agg(['mean', 'std'])
# 选择可能的概率分布
distributions = [stats.norm, stats.poisson]
# 初始化结果字典
results = {}
# 使用最大似然估计估计每个季度的非固定需求概率分布
for distribution in distributions:
dist_name = distribution.__class__.__name__
results[dist_name] = {}
for quarter, quarter_data in non_fixed_demand.groupby(pd.Grouper(freq='Q')):
params = distribution.fit(quarter_data)
results[dist_name][quarter] = {'params': params, 'mean': params[0], 'std': params[1]}
# 将结果填入表5
table5_non_fixed_demand = pd.DataFrame.from_dict({(i, j): results[i][j]
for i in results.keys()
for j in results[i].keys()},
orient='index')
table5_non_fixed_demand = table5_non_fixed_demand.reset_index().rename(columns={'level_0': 'Distribution', 'level_1': 'Quarter'})
作者:新加坡国立大学计算机硕士在读,热爱数模,欢迎关注点赞收藏。gzh:数模孵化园