2021阿里云供应链大赛--需求预测与单级库存优化参赛总结

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第1张图片

赛事链接: https://tianchi.aliyun.com/competition/entrance/531934/introduction 

        本次竞赛由阿里巴巴集团主办、阿里云承办,赛事共分为初赛、复赛、决赛三个阶段,其中初赛分为初赛A榜(2021年10月21日-2021年12月6日)和初赛B榜(2021年12月6日-2021年12月8日),初赛B榜前TOP50通过代码审核无误后将进入复赛(2021年12月14日-2022年1月10日),复赛前TOP10进入决赛。 赛事奖金共100000元,其中冠军队伍(1支)获得30000元,亚军队伍(2支)获得20000元,季军队伍(3支)获得10000元,此次竞赛全球共计1731支队伍参加。

赛事内容

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第2张图片

        基于给定过去一段时间的历史需求数据,同时结合当前的库存数据、补货时长、补货在途以及补货单元的相关信息(产品维度与地理纬度),参赛者需要自己提出方案,在补货决策日确定每一补货单元的补货量。最直接的方案,可通过历史需求数据,对未来的需求进行预测,结合当前库存水位以及补货在途的货物判断14天后的库存水位能否满足14天后的一系列需求(因为当日补货14天后才能到货),考虑对应的补货量,达到在保障一定服务水平的情况下,实现最低库存成本的效果;当然也可采用end to end的整体优化方案,实现该目标。库存量视角的变化过程如上图所示。

        给定一个训练集,供参赛选手训练模型并验证模型效果使用。同时,初赛时会提供一个测试集,选手需要为按时间读取某时间区间的需求数据,并根据历史需求数据以及补货在途,决定补货量,并把决策结果CSV文件输出到指定位置。

        由于库存控制是一个前后相关的决策过程,两次补货决策并不独立,因此会提供一段时间的数据,由选手在时间轴上进行多次补货决策,最后在较长的时间段内评价选手方案的好坏。在复赛时,选手需要提交一个docker镜像,镜像中需要包含用来进行库存管理所需的所有内容,如模型、脚本等,未来的需求数据以及到货时间将通过流评测的方式给出。镜像中的脚本需要能根据所得的需求量,根据历史需求数据以及先前所做的决策,决定补货量,并把决策结果CSV文件输出到指定位置。

评测指标

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第3张图片

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第4张图片

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第5张图片

说明: 整体指标平衡了库存率和服务水平,达到了一定服务水平的情况下,库存率就会更重要。

数据说明

    训练集包含以下内容: 虚拟资源使用量历史数据(demand_train.csv)、虚拟资源库存数据(inventory_info.csv)、地理拓扑数据(geography_tuopu.csv)、产品层级数据(product_tuopu.csv)、库存单元的权重信息(unit_weight.csv)。

虚拟资源使用量历史数据(demand_train.csv):

字段

unit

ts

qty

geography

geography_level

product

product_level

说明

单元

日期

资源使用量

地理信息

地理聚合维度

产品信息

产品聚合维度

虚拟资源库存数据(inventory_info.csv):

字段

unit

ts

qty

geography

geography_level

product

product_level

说明

单元

日期

库存量

地理信息

地理聚合维度

产品信息

产品聚合维度

地理拓扑数据(geography_tuopu.csv):

字段

geography_level_1

geography_level_2

geography_level_3

说明

地理层级1

地理层级2

地理层级3

产品层级数据(product_tuopu.csv):

字段

product_level_1

product_level_2

说明

产品层级1

产品层级2

库存单元的权重信息(unit_weight.csv):

字段

unit

weight

说明

单元

权重

说明:

        时序预测目标为虚拟资源未来使用量,虚拟资源库存数据是库存决策、时序预测前一天的各库存单元的库存水位,库存单元地理拓扑数据、产品拓扑数据为各库存单元所处的拓扑位置信息,库存单元权重信息代表了各库存单元在决策时的权重大小,权重大的库存单元,如果决策越有效,对评价指标的贡献将会更大;反之,降低评价分数的比重也会更大。

解决方案

        项目共分为时序预测和单级库存优化两部分。

1.解决思路

        对每一个单元,由于进行库存决策时是在一个长时序中的每一个周一,因此时序预测需要在每周一进行,其次由于每次库存决策中使用到的是该库存单元未来14天的需求量数据,所以我们需要预测的是未来14天的需求数据。则时序预测思路为:建立时序预测模型,使用决策区间之前的库存单元历史需求量信息作为训练数据,进行模型训练,在每个决策日,使用之前某区间的时序数据作为输入数据,对未来14天的需求量数据进行预测,移动时间点,在下一个决策日进行重复操作,直到完成决策区间任务。

        在每个决策日,使用时序预测出的未来需求量数据总和作为库存单元未来需求总和,使用一定的安全库存算法(安全库存是为防止未来物资供应或需求的不确定性因素(如大量突发性订货、交货意外中断或突然延期等)而准备的缓冲库存)以应对预测数据与真实数据之间的偏差,根据当前的库存水位和补货在途情况,进行决策,计算得出未来的补货数量。

2.数据预处理

    首先对于导入的虚拟资源使用量历史数据表、虚拟资源库存数据表、地理拓扑数据表、产品层级数据表、库存单元的权重信息表进行表合并,合并成一个含有库存单元的统一表。如下图:

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第6张图片

        删除表中的无用列,同时将‘ts’列中的时间数据由str字符串格式转为time时间格式,导出作为需求demand表,然后按照unit库存单元做groupby分组操作,对每个单元进行操作。

        使用diff功能,每日与前日进行相减操作,由每日的需求累积量信息得到每日的需求量信息。

        在时序预测时,对训练数据需要进行归一化操作。

3.特征工程

  

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第7张图片

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第8张图片

        使用了facebook prophet对数据进行分析,分析得出需求累积量具有按周、按月的规律特征,有一定的变化趋势。但是每个单元的趋势和规律并不相同,需要对每一个补货单元建立模型。某单元时序数据如上图。

        由于数据为时序数据,使用编解码模型对时序数据提取特征,所使用的模型,在每个历史28天窗口期中的数据都是特征数据,而之后的2天则为目标数据,此窗口按照步长为1持续移动,直到窗口到达时序末端,以此则提取到了某个单元的时序数据特征。

        对每个库存单元来说,每日需求量数据就有强烈而且不可忽略的突变性,而需求累积量趋势较为明显,可以提取出一定的特征数据。

需求累计量时序数据:

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第9张图片

每日需求量时序数据:

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第10张图片

4.模型建立

模型筛选:

        当使用RNN下的LSTM和GRU建模预测时:仅具有一定的趋势性,无法很好的捕捉到数据在时序上存在的一些关联特征,对于云供应链赛题不具有适应性(无法应对突变)。尝试使用编码器-解码器框架。

        当使用TCN下的扩张卷积时:由于扩张特性,容易提取不到突变点的特征。而因果卷积:层数加深,但是可以很好的预测突变趋势。

        最终使用了深度时间卷积神经网络(Deep TCN)中的因果卷积,并采用高斯分布概率模型进行数据采样,依靠概率模型进行预测调整。

使用了pytorch下的darts包进行模型的建立,具体参数如下:

TCNModel(#使用Deep TCN模型

       dropout=0.3,#每个卷积层的dropout率

       batch_size=10,#每次训练中使用的时间序列的数量

       n_epochs=30,#训练模型的迭代次数

       optimizer_kwargs={'lr': 1e-2},#学习率

       random_state=1,#控制权重初始化的布尔值

       input_chunk_length=300,#输入层大小

       output_chunk_length=15,#输出层大小

       kernel_size=10,#卷积层内核大小

       num_filters=5,#过滤器数量

       likelihood=GaussianLikelihood())#使用高斯分布概率模型

部分库存单元预测结果如下:

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第11张图片 2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第12张图片

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第13张图片 2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第14张图片

2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第15张图片 2021阿里云供应链大赛--需求预测与单级库存优化参赛总结_第16张图片

预测结果分析:模型已能提取好时序特征,具有较好的应变数据突变的能力。

5.参数调优

模型参数调优:

    由多对一到多对多,最终调整为输入尺度为28、输出尺度为2、batch批次为10时具有较好的预测效果。

安全库存算法调优:        

    安全库存方案1:(最大值-平均值)*平均补货时间

    安全库存方案2:服务系数*正态分布标准差*sqrt(平均补货时间)

    安全库存方案3 = 服务系数*(未来比重系数*未来标准差+历史比重系数*历史标准差)*(补货时长**0.5)+历史正均值-历史负均值

需求总和 = 调整系数*未来最大子序列和+调整系数*未来总和

再补货点 = 未来需求+安全库存

        其中传统供应链安全库存算法(安全库存方案1、2)对具有释放概念的云供应链不具有适应性,创新型云供应链安全库存算法(安全库存方案3)综合了未来与历史、需求与释放,具有适应性。

最终排名

单元数量

数据总量

分数

名次

初赛A榜

632

284832

19.7209/19.8365

42/1731

初赛B榜

295

127188

8.5146/8.7000

14/1731

复赛

951

468861

33.3886/34.1993

37/1731

参赛总结

        此次为第一次参加大数据竞赛,对相关比赛流程比较陌生,由于同时正在学习《大数据分析方法》这门课,对于时序预测方面内容涉猎较少,在前期初赛阶段投入精力较大(尤其在时序预测方面),同时本竞赛算也是一个方案赛,在库存优化方面作为了侧重提分点。复赛中由于和期末复习周时间冲突,无法继续优化。

程序分享

官方baseline程序

import pandas as pd
import numpy as np
import datetime

class ReplenishUnit:
    def __init__(self,
                 unit,
                 demand_hist,
                 intransit,
                 qty_replenish,
                 qty_inventory_today,
                 qty_using_today,
                 arrival_sum,
                 lead_time
                 ):
        '''
        记录各补货单元状态
        :param unit:
        :param demand_hist: 净需求历史
        :param intransit: 补货在途
        :param qty_replenish: 补货记录
        :param qty_inventory_today: 当前可用库存
        :param qty_using_today: 当前已用库存(使用量)
        :param arrival_sum: 补货累计到达
        :param lead_time: 补货时长,交货时间
        '''
        self.unit = unit
        self.demand_hist = demand_hist
        self.intransit = intransit
        self.qty_replenish = qty_replenish
        self.qty_inventory_today = qty_inventory_today
        self.qty_using_today = qty_using_today
        self.arrival_sum = arrival_sum
        self.qty_using_today
        self.lead_time = lead_time

    def update(self,
               date,
               arrival_today,
               demand_today):
        '''
        每日根据当天补货到达与当日净需求更新状态
        :param date:
        :param arrival_today: 当天补货到达
        :param demand_today: 当天净需求
        :return:
        '''
        self.qty_inventory_today += arrival_today
        self.arrival_sum += arrival_today
        inv_today = self.qty_inventory_today
        if demand_today < 0:
            self.qty_inventory_today + min(-demand_today, self.qty_using_today)
        else:
            self.qty_inventory_today = max(self.qty_inventory_today - demand_today, 0.0)
        self.qty_using_today = min(self.qty_using_today + min(demand_today, inv_today), 0.0)
        self.demand_hist = self.demand_hist.append({"ts": date, "unit": self.unit, "qty": demand_today}, ignore_index = True)


    def forecast_function(self,
                          demand_hist):
        demand_average = np.mean(self.demand_hist["qty"].values[-3 * self.lead_time:])
        return [demand_average] * 90

    def replenish_function(self,
                           date):
        '''
        根据当前状态判断需要多少的补货量
        补货的策略由选手决定,这里只给一个思路
        :param date:
        :return:
        '''
        replenish = 0.0
        if date.dayofweek != 0:
            #周一为补货决策日,非周一不做决策
            pass
        else:
            #预测未来需求量
            qty_demand_forecast = self.forecast_function(demand_hist = self.demand_hist)

            #计算在途的补货量
            qty_intransit = sum(self.intransit) - self.arrival_sum

            #安全库存 用来抵御需求的波动性 选手可以换成自己的策略
            safety_stock = (max(self.demand_hist["qty"].values[-3 * self.lead_time:]) - (np.mean(self.demand_hist["qty"].values[- 3 * self.lead_time:]))) * self.lead_time

            #再补货点,用来判断是否需要补货 选手可以换成自己的策略
            reorder_point = sum(qty_demand_forecast[:self.lead_time]) + safety_stock

            #判断是否需要补货并计算补货量,选手可以换成自己的策略,可以参考赛题给的相关链接
            if self.qty_inventory_today + qty_intransit < reorder_point:
                replenish = reorder_point - (self.qty_inventory_today + qty_intransit)
            self.qty_replenish.at[date] = replenish
            self.intransit.at[date + self.lead_time * date.freq] = replenish








class SupplyChainRound1Baseline:
    def __init__(self):
        self.using_hist = pd.read_csv("../data/demand_train_B.csv")
        self.using_future = pd.read_csv("../data/demand_test_B.csv")
        self.inventory = pd.read_csv("../data/inventory_info_B.csv")
        self.last_dt = pd.to_datetime("20210301")
        self.start_dt = pd.to_datetime("20210302")
        self.end_dt = pd.to_datetime("20210607")
        self.lead_time = 14

    def run(self):
        self.using_hist["ts"] = self.using_hist["ts"].apply(lambda x:pd.to_datetime(x))
        self.using_future["ts"] = self.using_future["ts"].apply(lambda x:pd.to_datetime(x))
        qty_using = pd.concat([self.using_hist, self.using_future])
        date_list = pd.date_range(start = self.start_dt, end = self.end_dt)
        unit_list = self.using_future["unit"].unique()
        res = pd.DataFrame(columns = ["unit", "ts", "qty"])

        replenishUnit_dict = {}
        demand_dict = {}

        #初始化,记录各补货单元在评估开始前的状态
        for chunk in qty_using.groupby("unit"):
            unit = chunk[0]
            demand = chunk[1]
            demand.sort_values("ts", inplace = True, ascending = True)

            #计算净需求量
            demand["diff"] = demand["qty"].diff().values
            demand["qty"] = demand["diff"]
            del demand["diff"]
            demand = demand[1:]
            replenishUnit_dict[unit] = ReplenishUnit(unit = unit,
                                                     demand_hist = demand[demand["ts"] < self.start_dt],
                                                     intransit = pd.Series(index = date_list.tolist(), data = [0.0] * (len(date_list))),
                                                     qty_replenish = pd.Series(index = date_list.tolist(), data = [0.0] * (len(date_list))),
                                                     qty_inventory_today = self.inventory[self.inventory["unit"] == unit]["qty"].values[0],
                                                     qty_using_today = self.using_hist[(self.using_hist["ts"] == self.last_dt) & (self.using_hist["unit"] == unit)]["qty"].values[0],
                                                     arrival_sum = 0.0,
                                                     lead_time = self.lead_time)

            #记录评估周期内的净需求量
            demand_dict[unit] = demand[(demand["unit"] == unit) & (demand["ts"] >= self.start_dt)]

        for date in date_list:
            #按每日净需求与每日补货到达更新状态,并判断补货量
            for unit in unit_list:
                demand = demand_dict[unit]
                demand_today = demand[demand["ts"] == date]["qty"].values[0]
                arrival = replenishUnit_dict[unit].intransit.get(date, default = 0.0)
                replenishUnit_dict[unit].update(date = date,
                                                arrival_today = arrival,
                                                demand_today = demand_today)
                replenishUnit_dict[unit].replenish_function(date)

        for unit in unit_list:
            res_unit = replenishUnit_dict[unit].qty_replenish
            res_unit = pd.DataFrame({"unit": unit,
                                     "ts": res_unit.index,
                                     "qty": res_unit.values})
            res_unit = res_unit[res_unit["ts"].apply(lambda x:x.dayofweek == 0)]
            res = pd.concat([res, res_unit])
        #输出结果
        res.to_csv(("../submit/submit_"+datetime.datetime.now().strftime('%Y%m%d_%H%M%S') + ".csv"))

if __name__ == '__main__':
    supplyChainRound1Baseline = SupplyChainRound1Baseline()
    supplyChainRound1Baseline.run()

初赛B榜程序

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from darts.models import TCNModel
from darts.utils.likelihood_models import GaussianLikelihood
from darts import TimeSeries
from darts.dataprocessing.transformers import Scaler
from darts.utils.timeseries_generation import datetime_attribute_timeseries

class Unit:#一个单元
    def __init__(self,demand_true,inventory,weight):
        #数据集
        self.weight = weight#单元权重
        self.demand_true = demand_true.reset_index(drop=True)#单元真实需求量
        self.inventory = inventory['qty'].values[0]#单元库存
        self.demand_pred = pd.DataFrame([])#单元预测需求量
        #时间
        self.last_dt = pd.to_datetime("20210223")#预测开始
        self.start_dt = pd.to_datetime("20210302")#开始时间
        self.end_dt = pd.to_datetime("20210607")#结束时间
        self.date_list = pd.date_range(start = self.start_dt, end = self.end_dt)#时间序列
        self.lead_time = 14#补货在途时间

    def forecast(self):#预测

        data_series = TimeSeries.from_dataframe(self.demand_true[['ts','qty']],'ts','qty')#将数据转换成TimeSeries格式
        scaler_data = Scaler()#数据标准化模型
        series_transformed = scaler_data.fit_transform(data_series)#数据标准化
        train_transformed,val_transformed = series_transformed.split_after(self.last_dt)#从2月23日划分训练集和测试集,用2月23日之前的做训练
        date_series = datetime_attribute_timeseries(series_transformed,attribute='day',one_hot=True)#按日转换为时间序列
        scaler_date = Scaler()#时间序列标准化模型
        covariates = scaler_date.fit_transform(date_series)#时间序列标准化
        train_date,val_date = covariates.split_after(self.last_dt)#从2月23日划分训练集和测试集
        
        model = TCNModel(#使用Deep TCN模型
            dropout=0.3,#每个卷积层的dropout率
            batch_size=10,#每次训练中使用的时间序列(输入和输出序列)的数量
            n_epochs=30,#训练模型的迭代次数
            optimizer_kwargs={'lr': 1e-2},#学习率
            random_state=1,#控制权重初始化的布尔值
            input_chunk_length=28,#输入层大小
            output_chunk_length=2,#输出层大小
            kernel_size=10,#卷积层内核大小
            num_filters=5,#过滤器数量
            likelihood=GaussianLikelihood())#使用高斯分布概率模型


        model.fit(series=train_transformed,#2月23日之前数据作训练集拟合
             past_covariates=train_date,#2月23日之前时间序列作训练集拟合
             verbose=True)#打印进度

        
        forecast_series = model.historical_forecasts(#预测3月8日之后的数据
                series=series_transformed,#输入数据序列
                past_covariates=covariates,#输入时间序列
                num_samples=100,#从概率模型中进行采样的数据
                start=self.last_dt,#从3月8日进行预测,因为补货决策第一次在3月8日(用到的预测数据起始在3月9日)
                forecast_horizon=self.lead_time,#连续预测14天,保证在决策和预测时不会用到未来的数据
                stride=1,#移动窗口,对下一个14天进行预测
                retrain=False,#每次预测无需重新训练
                verbose=True)#打印进度
        

        forecast = scaler_data.inverse_transform(forecast_series)#数据表转换还原  
        self.demand_pred = forecast.pd_dataframe(copy=True)#预测作为需求量
        self.demand_pred['ts'] = self.demand_pred.index#时间序列作为ts列
        self.demand_pred = self.demand_pred.reset_index(drop=True)#重置索引
        self.demand_pred.rename(columns={'0':'qty'},inplace=True)#列改名
        self.demand_pred['qty'] = self.demand_pred['qty'].diff()#需求累积量做diff得需求量
        self.demand_true['ts'] = pd.to_datetime(self.demand_true['ts'],format='%Y-%m-%d')#时间转换
        self.demand_true['qty'] = self.demand_true['qty'].diff()#需求累积量做diff得需求量
        
    def MSS(self,value) -> float:#求最大子序列和
        maxSum = 0.0
        nowSum = 0.0
        for val in value:
            nowSum += val
            if(nowSum > maxSum):
                maxSum = nowSum
            elif nowSum < 0: 
                nowSum = 0
        return maxSum
    
    def replenish(self):#补货
    
        intransit = pd.Series(index=self.date_list.tolist(),data=[0.0]*(len(self.date_list)))#补货到达记录
        arrival_sum = 0.0#累计补货到达
        qty_replenish = pd.Series(index=self.date_list.tolist(),data=[0.0]*(len(self.date_list)))#存放补货记录
        
        for date in self.date_list:
            #对于每一天,进行库存更新
            demand_today = self.demand_true[self.demand_true['ts'] == date]['qty'].values[0]#当天消耗
            arrival_today = intransit.get(date,default = 0.0)#当天到达
            self.inventory = max(self.inventory + arrival_today - demand_today,0.0)#库存更新
            arrival_sum += arrival_today#累计补货到达
            
            if date.dayofweek == 0:#周一为补货决策日
                qty_intransit = sum(intransit) - arrival_sum#补货在途
                #需求量计算
                demand_future_sum = self.demand_pred[(self.demand_pred['ts']>date-self.lead_time*date.freq)&(self.demand_pred['ts']<=date+self.lead_time*date.freq)]["qty"]#未来14天需求量
                demand_history_plus = self.demand_true[(self.demand_true['ts']<=date)&(self.demand_true['qty']>0)]["qty"]#历史真实正需求量
                demand_history_minus = self.demand_true[(self.demand_true['ts']<=date)&(self.demand_true['qty']<0)]["qty"]#历史真实负需求量
                #安全库存和再补货点
                safety_stock = 1.35 * ( 0.9 * demand_future_sum.std() + 0.1 * demand_history_plus.std() ) * (self.lead_time**0.5) + demand_history_plus.mean() - demand_history_minus.mean()
                #安全库存 = 服务系数*(未来比重系数*未来标准差+历史比重系数*历史标准差)*(补货时长**0.5)+历史正均值-历史负均值
                demand_total = 0.55 * self.MSS(demand_future_sum) + 0.2 * np.sum(demand_future_sum)
                #需求总和 = 调整系数*未来最大子序列和+调整系数*未来总和
                reorder_point =  demand_total + safety_stock #再补货点=未来需求+安全库存
                #补货策略
                if self.inventory + qty_intransit < reorder_point:#是否补货判别
                    replenish = reorder_point - (self.inventory + qty_intransit)#计算补货量
                    intransit.at[date + self.lead_time*date.freq] = replenish#添加补货在途
                    qty_replenish.at[date] = replenish#添加补货记录

        return qty_replenish
    
def dealDataSet():
    #导入数据
    demand_train_B = pd.read_csv("../data/Dataset/demand_train_B.csv")#虚拟资源使用量_训练数据
    demand_test_B = pd.read_csv("../data/Dataset/demand_test_B.csv")#虚拟资源使用量_测试数据
    inventory_info_B = pd.read_csv("../data/Dataset/inventory_info_B.csv")#库存数量
    weight_B = pd.read_csv("../data/Dataset/weight_B.csv")#库存单元权重
    geo_topo = pd.read_csv("../data/Dataset/geo_topo.csv")#地理层级
    product_topo = pd.read_csv("../data/Dataset/product_topo.csv")#产品层级
    #列变更
    demand = pd.concat([demand_train_B, demand_test_B])#数据合并
    demand.drop(demand.columns[[0,4,6]],axis=1,inplace=True)#删除无用列
    weight_B.drop(weight_B.columns[[0]],axis=1,inplace=True)#删除无用列
    demand.rename(columns={'geography':'geography_level_3','product':'product_level_2'},inplace=True)#修改列名,便于做合并
    #数据合并
    demand = pd.merge(demand,geo_topo,on='geography_level_3',how='left')#地理层级合并
    demand = pd.merge(demand,product_topo,on='product_level_2',how='left')#产品层级合并
    demand = pd.merge(demand,weight_B,on='unit',how='left')#库存单元权重信息

    return demand,inventory_info_B
def groupDataSet(demand,inventory):
    res = pd.DataFrame(columns = ["unit","ts","qty"])#输出
    for unit in demand.groupby("unit"):#遍历unit_list
        print('Current Unit:'+unit[0])
        
        replenish_unit = Unit(unit[1],inventory[inventory['unit']==unit[0]],unit[1]['weight'].values[0])#创建单元
        replenish_unit.forecast()#预测
        res_unit = replenish_unit.replenish()#补货,并返回补货序列
        res_unit = pd.DataFrame({"unit":unit[0],"ts":res_unit.index,"qty": res_unit.values})#单元数据
        res = pd.concat([res, res_unit[res_unit["ts"].apply(lambda x:x.dayofweek == 0)]])#合并
        
    res.to_csv("../submit/submit.csv")#输出结果
    print("The results have been output!")#运行结束
    
if __name__ == '__main__':
    demand,inventory = dealDataSet()#加载与处理数据
    groupDataSet(demand,inventory)#分单元预测与补货决策

你可能感兴趣的:(时序模型,大数据)