@zqbinggong
2018-07-20T15:00:45.000000Z
字数 20458
阅读 3314
览众
前两周销售量加权平均的两倍
1. 计算前需要判断下是都为空值
D[idx_stc]['targ_lb'] = sale_max / weight_max * weight
D[idx_stc]['targ_ub'] = sale_max / weight_min * weight
expr.addTerms(CDL[idx_stc]['ub'], DTU[idx_stc]) # 对超出上限的添加惩罚系数DTU
expr.addTerms(CDL[idx_stc]['lb'], DTL[idx_stc]) # 对超出上限的添加惩罚系数DTL
expr.addTerms(CID[idx_stc], RQ[idx_stc])
这里把max变成两个不等式约束,并通过obj1和2来让让其等价
# DTL = max(D - IT, 0)
m.addConstr(DTL[idx_stc] >= 0)
m.addConstr(DTL[idx_stc] >= D[idx_stc]['targ_lb'] - IT[idx_stc])
# DTU = max(IT - D, 0)
m.addConstr(DTU[idx_stc] >= 0)
m.addConstr(DTU[idx_stc] >= IT[idx_stc] - D[idx_stc]['targ_ub'])
这里的绝对值也是变成了两个不等式约束,并通过obj3让其等价
m.addConstr(quicksum(IT[prod_id, color_id, size, store_id] for store_id in SI)
<= quicksum(I0[prod_id, color_id, size, store_id]['ava'] for store_id in dict(SI, **WI)))
m.addConstr((ITF[idx_skcs] == 1) >> (quicksum(F0B.values()) >= 1))
idx_skcs = (prod_id, color_id, store_id)
for size in PI[prod_id][color_id]['size']:
idx_stc = (prod_id, color_id, size, store_id)
m.addConstr((ITF[idx_skcs] == 0) >> (IT[idx_stc] <= 0)) # # =0
(prod_id, color_id) | size | year | ''' |
---|
(store_id) | prov_Id | city_id | dist_id | store_lvl |
---|
(wh_id) | prov_id |
---|
(prod_id, color_id, size, org_id) | quant_stock_instore | quant_stock_onorder |
---|---|---|
str | int,无空值,非负 | int,无空值,非负 |
(prod_id, color_id, size, store_id) | date_sell | quant_sell |
---|---|---|
str | dt, 由date_dec确定范围 | int,无空值,非负 |
(org_send_id, org_rec_id) | trans_freq |
---|---|
str | int |
(prod_id, color_id, size, store_id) | quant_stock_targ |
---|---|
str | int,无空值,非负 |
计算这季度这一大类某(p_id,c_id)占的销售比
1. s_df + pi_df + si_df
- 主表
- 提供season_id class_0
2. s_df.quant_sell:
- sum --> groupby(p_id, c_id,store_id)
- mean --> groupby(p_id, c_id)
3. join pi_df
- 选取p_id,c_id,season_id,class_0这4列并对其去重(先释放index)
- 将index设为p_id,c_id(以进行join)
4. 将index设为season_id,class_0,并将quant_sell的负值改为0
5. 计算这季度这一大类某(p_id,c_id)占的销售比
- quant_sell_class = quant_sell --> sum(level = season_id, class_0)
- 将quant_sell_class的非正值设为1(防止下一步分母为零)
- prop_sales_skc = quant_sell / quant_sell_class
- 设置index为p_id,c_id,去除多余列
(prod_id, color_id) | prop_sales_skc |
---|
计算该size在index确定的所有门店的所有skc的平均销售额
1. s_df + pi_df + si_df
- 主表
- 提供
2. s_df.quant_sell:
- sum --> groupby(p_id, c_id, size, store_id) (这里注意到s_df没有去重,因为num数据有累加的意义)
- 去掉值不大于零的行
- 得到sales_skss
- - index: (p_id, c_id, size, store_id)
- - columns: quant_sell
3. 从sales_skss中获取skc_list=(p_id, c_id)和store_list=(store_id),用以筛选si_df和pi_df中的数据;这里两个list的顺序是对应的可以保证下面的到的sales_prop_size的index与sales_skss一一对应上
- pi_sel: 用skc_list进行筛选,并选取需要的列,再加入值全为1的列col_on作为index,用以join(具体实现使用字典的方式),col=p_id,c_id,size,size_order,class_0,size_grp_id
- si_sel: 用store_list进行筛选,并选取需要的列,再加入值全为1的列col_on作为index,用以join(具体实现使用字典的方式), col=store_id,dist_id
4. sales_prop_size = pi_sel.join(si_sel)
- join sales_skss,并fillna(0), 获取quant_sell (即为sales_skss表获取相应的商品信息和门店信息)
- quant_sell:
- - sum --> 所有其他列 (由于2.1已经保证了(p_id,c_id,size,store_id)是唯一的即这里的其他列也唯一,因而此处的求和显得多余)
- - 去掉p_id,c_id
- - mean --> 所有其他列 (得到该size在该店不同skc的平均销售额)
- - 去掉store_id
- - mean --> 所有其他列 (得到该size在这些店的平均销售额)
- - index为dist_id,class_0,size_grp_id (col=size_order, quant_sell)
- - 小于零的值设为0
5. 计算prop
- quant_sell_class = quant_sell --> sum(level=dist_id,class_0,size_grp_id) (即对size_order聚合,但是size-order和size一一对应,因而sum只起到qu'd)
- 将quant_sell_class的非正值设为1(防止下一步分母为零)
- prop_sales_size = quant_sell / quant_sell_class
- 排序sort_values(['dist_id', 'class_0', 'size_grp_id', 'size_order'], inplace=True)
- 设置index为'dist_id', 'class_0', 'size_grp_id', 'size',去除多余列
6. 表结构
(class_0, size_grp_id, dist_id, size) | prop_salse_size |
---|---|
某size在index确定的所有门店的所有skc的平均销售额 |
某赛季某大类某门店在index确定下的平均销售额
1. s_df + pi_df
- 主表
- 提供
2. s_df.quant_sell:
- sum --> groupby(p_id, c_id, store_id)
- 得到sales_skcs
- - index: (p_id, c_id, store_id)
- - columns: quant_sell
3. sales_prop_store = sales_skcs -->
- join(pi_df[['season_id', 'class_0']].drop_duplicates())
- groupby(['season_id', 'class_0', 'store_id']).mean()
(该门店该赛季该大类所有skc(skc即sks之和)的平均销售额)
- reset_index(['store_id']
- 将quant_sell的负值改为0
4. 计算prop
- quant_sell_class = quant_sell --> sum(level=season_id,class_0)
- 将quant_sell_class的非正值设为1(防止下一步分母为零)
- prop_sales_size = quant_sell / quant_sell_class
- 排序sort_values(['dist_id', 'class_0', 'size_grp_id', 'size_order'], inplace=True)
- 设置index为'season_id', 'class_0', 'store_id',去除多余列
5. 表结构
(season_id, class_0, store_id) | prop_salse_store |
---|---|
某赛季某大类某门店在index确定下的平均销售额 |
(class_0, size_grp_id, dist_id) | size |
---|---|
出现在主要尺码组中的尺码(个数为min(sales_prop_size里size个数,len_main_size_grp) |
drop(['season_id', 'class_0', 'size_grp_id', 'dist_id']
fillna(0)
c_min = sales_we_sum['prop_sales_skc'].min()
c_max = sales_we_sum['prop_sales_skc'].max() + 1.0E-10
sales_we_sum['skc_we'] = \
sales_we_sum['prop_sales_skc'].map(lambda x: np.exp((x - c_min)/(c_max - c_min) - 1))
(prod_id, color_id, size, store_id) | skc_we | size_we | store_we | sum_we |
---|
PI : dict
Product information
key : prod_id
value : dict
key : color_id
value : dict
key : size, class_0, size_grp_id
SI : dict
Store information
key : store_id
value : dict
key : prov_id, city_id, dist_id, store_lvl
WI : dict
Warehouse information
key : wh_id
value : dict
key : prov_id
MS : dict
The main sizes
key : prod_id, color_id, store_id
value : [sizes]
I0 : dict
Initial inventory
key : prod_id, color_id, size, org_id
value : dict
key : ava, sum
value : available and sum inventory
IO : dict
Markers of existed transferring in/out
key : prod_id, color_id, size, store_id
value : dict
key : has_in, has_out
value : bool(此时值为0)
IT : dict
Target inventory
key : prod_id, color_id, size, store_id
value : target inventory
确定齐码组的长度,上限是QSF_base
QSF : dict
The minimal continue-size length in a full-size group
key : prod_id, color_id, store_id
value : size number
W0B = cop.mark_fullsize_init(MS, I0_aft_rep, QSF)
W0B : dict
Whether or not initial inventory full-size group number is positive
If initial inventory is 0, W0B is 1
key : prod_id, color_id, store_id
value : bool
CTQ : dict
Unit cost by transferring quantity
key : prod_id, color_id, size, org_send_id, org_rec_id
value : transferring quantity cost
CTP : dict
Unit cost by transferring packages
key : org_send_id, org_rec_id
value : transferring package cost
CID : dict
Unit cost by inventory mismatch
key : prod_id, color_id, size, store_id
value : inventory mismatch cost
we = sales_we_sum['sum_we'].get(idx_stc, sales_we_sum['sum_we'].min())
CID[idx_stc] = CID_base*we
CDP : dict
Unit cost by the maximal inventory mismatch
key : prod_id, color_id, size
value : the maximal inventory mismatch cost
we = sales_we_sks['sks_we'].get(idx_sks, sales_we_sks['sks_we'].min())
CDP[idx_sks] = CDP_base*we
CDL : dict
Unit cost by demand lossing
key : prod_id, color_id, size, store_id
value : dict
key : lb, ub
value : demand lossing cost
we = sales_we_sum['sum_we'].get(idx_stc, sales_we_sum['sum_we'].min())
CDL[idx_stc]['lb'] = 5*CDL_base*we
CDL[idx_stc]['ub'] = CDL_base*wec
# Q : dict
# Replenishment quantity of each sks/send_warehouse/receive_store
# key : prod_id, color_id, size, wh_send_id, store_rec_id
# value : replenishment quantity
Q[idx_rep] = m.addVar(obj=0, vtype=GRB.INTEGER, name=var_name)
I : dict
End inventory of each skc/size/store
key : prod_id, color_id, size, store_id
value : end inventory
ID : dict
Difference between end and target inventory of each skc/size/store
key : prod_id, color_id, size, store_id
value : dict
key : in, out
value : inventory difference
IDP : dict
The maximal difference between end and target inventory of each sks
key : prod_id, color_id, size
value : dict
key : in, out
value : the maximal inventory difference
QSP : dict
Sum replenishment quantity of each package
key : wh_send_id, store_rec_id
value : sum transferring quantity
QPB : dict
Whether or not replenishment quantity is positive of each package
key : wh_send_id, store_rec_id
value : bool
idx_rep = (prod_id, color_id, size, wh_send_id, store_rec_id)
expr.addTerms(CTQ[idx_rep], Q[idx_rep])
idx_ptp = (wh_send_id, store_rec_id)
expr.addTerms(CTP[idx_ptp], QPB[idx_ptp])
idx_stc = (prod_id, color_id, size, store_id)
expr.addTerms(CID[idx_stc], ID[idx_stc])
idx_sks = (prod_id, color_id, size)
expr.addTerms(CDP[idx_sks], IDP[idx_sks])
QIS[idx_stc] == quicksum(Q[prod_id, color_id, size, wh_send_id, store_id] for wh_send_id in WI))
QOS[idx_stc] == quicksum(Q[prod_id, color_id, size, wh_send_id, store_id] for store_rec_id in SI))
idx_ptp = (wh_send_id, store_rec_id)
m.addConstr(QSP[idx_ptp] ==
quicksum(Q[prod_id, color_id, size, wh_send_id, store_rec_id]
for prod_id in PI
for color_id in PI[prod_id]
for size in PI[prod_id][color_id]['size']))
门店
idx_stc = (prod_id, color_id, size, store_id)
m.addConstr(I[idx_stc] == I0[idx_stc]['sum'] + QIS[idx_stc])
仓库
idx_stc = (prod_id, color_id, size, wh_id)
m.addConstr(I[idx_stc] == I0[idx_stc]['ava'] - QOS[idx_stc])
idx_stc = (prod_id, color_id, size, wh_id)
m.addConstr(QOS[idx_stc] <= I0[idx_stc]['ava'])
ITS = sum(IT[prod_id, color_id, size, store_id] for size in PI[prod_id][color_id]['size'])
if ITS == 0:
for size in PI[prod_id][color_id]['size']:
idx_stc = (prod_id, color_id, size, store_id)
m.addConstr(QIS[idx_stc] == 0)
m.addConstr(ID[idx_stc] == abs_(I[idx_stc] - IT[idx_stc]))
m.addConstr(IDP[idx_sks] == max_([ID[prod_id, color_id, size, store_id] for store_id in SI]))
若包裹容量为负,即QPB=0,则数量为0
若包裹容量为正,即QPB=1,则数量至少为1
idx_ptp = (wh_send_id, store_rec_id)
m.addConstr(QSP[idx_ptp] + M * (1 - QPB[idx_ptp]) >= 1)
m.addConstr(QSP[idx_ptp] + M * QPB[idx_ptp] >= 0)
m.addConstr(QSP[idx_ptp] - M * QPB[idx_ptp] <= 0)
W0B = cop.mark_fullsize_init(MS, I0_aft_rep, QSF)
W0B : dict
Whether or not initial inventory full-size group number is positive
If initial inventory is 0, W0B is 1
key : prod_id, color_id, store_id
value : bool
VSI = {'LZ001': {}}
1. 将虚拟门店的sum和ava库存设为0
2. 将CTQ(发送和接收)设为 1000
3. 将CTP (发送和接收)设为 1000
dist_set = set(SI中所有门店所在的dist)
# Q : dict
# Replenishment quantity of each sks/send_warehouse/receive_store
# key : prod_id, color_id, size, wh_send_id, store_rec_id
# value : replenishment quantity
Q[idx_rep] = m.addVar(obj=0, vtype=GRB.INTEGER, name=var_name)
IB : dict
Whether or not end inventory is positive of each sks/store
key : prod_id, color_id, size, store_id
value : bool
IS : dict
End inventory after transferring of each skc/store
key : prod_id, color_id, store_id
value : end inventory
ISB : dict
Whether or not end inventory is positive of each skc/store
key : prod_id, color_id, store_id
value : bool
NW : dict
End inventory full-size group number of each skc/store
key : prod_id, color_id, store_id
value : full-size group number
WB : dict
Whether or not end inventory full-size group number is positive of each skc/store.
If end inventory is 0, WB is 1
key : prod_id, color_id. store_id
value : bool
idx_trans = (prod_id, color_id, size, wh_send_id, store_rec_id)
expr.addTerms(CTQ[idx_trans], Q[idx_trans])
idx_ptp = (wh_send_id, store_rec_id)
expr.addTerms(CTP[idx_ptp], QPB[idx_ptp])
idx_stc = (prod_id, color_id, size, store_id)
expr.addTerms(CID[idx_stc], ID[idx_stc])
idx_sks = (prod_id, color_id, size)
expr.addTerms(CDP[idx_sks], IDP[idx_sks])
QIS[idx_stc] == quicksum(Q[prod_id, color_id, size, store_send_id, store_id] for store_send_id in DVSI if store_send_id != store_id))
QOS[idx_stc] == quicksum(Q[prod_id, color_id, size, store_id, store_rec_id] for store_rec_id in DVSI if store_rec_id != store_id))
idx_ptp = (wh_send_id, store_rec_id)
m.addConstr(QSP[idx_ptp] ==
quicksum(Q[prod_id, color_id, size, store_send_id, store_rec_id]
for prod_id in PI
for color_id in PI[prod_id]
for size in PI[prod_id][color_id]['size']))
store_id in SI:
idx_stc = (prod_id, color_id, size, store_id)
m.addConstr(I[idx_stc] == I0[idx_stc]['sum'] + QIS[idx_stc] - QOS[idx_stc])
idx_skcs = (prod_id, color_id, store_id)
m.addConstr(IS[idx_skcs] == quicksum(I[prod_id, color_id, size, store_id]
for size in PI[prod_id][color_id]['size']))
idx_stc = (prod_id, color_id, size, wh_id)
m.addConstr(QOS[idx_stc] <= I0[idx_stc]['ava'])
ITS = sum(IT[prod_id, color_id, size, store_id] for size in PI[prod_id][color_id]['size'])
if ITS == 0:
for size in PI[prod_id][color_id]['size']:
idx_stc = (prod_id, color_id, size, store_id)
m.addConstr(QIS[idx_stc] == 0)
m.addConstr(ID[idx_stc] == abs_(I[idx_stc] - IT[idx_stc]))
m.addConstr(IDP[idx_sks] == max_([ID[prod_id, color_id, size, store_id] for store_id in SI]))
idx_skcs = (prod_id, color_id, store_id)
NS == quicksum(IB[prod_id, color_id, size, store_id] for size in size_grp))
,即上面对应的尺码组有多好个是有库存(trans后)的NW[idx_skcs] == quicksum(IFB.values())
(num of whole_size)ISB[idx_skcs] == 0) >> (WB[idx_skcs] == 1))
idx_skcs = (prod_id, color_id, store_id)
m.addConstr(WB[idx_skcs] >= W0B[idx_skcs])
for store_id in SI:
idx_skcs = (prod_id, color_id, store_id)
MS_rem[idx_skcs] = MS[idx_skcs]
将区域间发生调拨的门店间的CTP值设为0
prod_id, color_id, size, store_send_id, store_rec_id = idx_trans
if (Q_trans[idx_trans] > 0) and (store_send_id, store_rec_id) in CTP:
CTP[store_send_id, store_rec_id] = 0
W0B = cop.mark_fullsize_init(MS_rem, I0_aft_rep, QSF)
W0B : dict
Whether or not initial inventory full-size group number is positive
If initial inventory is 0, W0B is 1
key : prod_id, color_id, store_id
value : bool
将两次trans优化得到的变量值进行求和得到最终的变量值,再计算库存
将变量值存入df(大于零)
('prod_id', 'color_id', 'size', 'org_send_id', 'org_rec_id') | quant_mov |
---|---|
>0 |
('prod_id', 'color_id', 'size', 'org_send_id', 'org_rec_id') | quant_mov |
---|---|
>0 |
对得到的Q_trans_df进行补充其他信息
为收发方的store添加city_id(city_send(rec)_id)和dist_id(dist_send(rec)_id)
IRP_df, ITR_df <-- I0_aft_rep/trans
to be continue