销量预测模型1:
需要字段:
商品id:pid str
销售和进货的时间:时间 datetime
类型:type(销售数据以销售_开头) str
销售数量 float
当前销售时对应的 库存量 float
计算日销量
思路:日销量 = 总销量/(周期内最后一次购买时间天数-周期内第一次购买时间天数)
选取2021 5,6月数据做验证集,计算日销量
选取 2020.11-2021-5做测试集,计算日销量
评价指标:|预测值-真实值|/真实值
细节:
1.真实购买时间=周期内最后一次购买时间天数-周期内第一次购买时间天数-补货时间(库存为0到进货时间间隔天数之和)
2.部分商品时间跨度达不到周期内的不参与计算
3.验证集/测试集 内 商品销售数据时间跨度至少2天,才能参与计算。(只有一条数据或者同一天多条数据,无法通过销量减去时间间隔计算日销量)
def 补货公式预测销量(df):
'''
预测日销量
:param df: dataframe类型
'''
#删除部分数据
df.drop(index=df[df['时间'] <= '2020-11-01'].index, inplace=True)
df.drop(index=df[df['时间'] >= '2021-07-01'].index, inplace=True)
drop_spid_list = [] # 不参与计算的商品id
day_sale_evaluate_list = [] # 日均销量评估值列表
#循环每种商品计算
for k, v in dict(df['spid'].value_counts()).items():
data = df[df['spid'] == k]#去除商品id为***的数据
if len(data[data['type'].str.contains('零售')]['时间'])==0:#全是进货数据,没有销售数据,不参与计算
# print(k,'商品没有零售数据')
continue
test_max_time = data[data['type'].str.contains('零售_')]['时间'].max() # 该商品售卖最新时间
test_min_time = test_max_time - relativedelta(months=+2)#测试集时间跨度为月
test_data = data[((data['时间'] >= test_min_time) & (data['时间'] <= test_max_time))]#通过时间拆分测试集和验证集
train_data = data[data['时间'] <= test_min_time]
# 该品种测试数据周期内销售无数据,说明该商品上架售卖时间不足指定周期,不参与计算
if train_data[train_data['type'].str.contains('零售_')].shape[0] == 0:
drop_spid_list.append(k)
continue
#验证集销售的最大的时间
train_max_time = train_data[train_data['type'].str.contains('零售_')]['时间'].max()
# train_max_time = train_data['时间'].max()
def 计算日销售量(data, max_time):
'''
:param data: dataframe
:param max_time: dataframe最大时间
:return: 日销量
'''
#指定周期内的销售记录的时间必须大于一天,才有时间间隔可以计算日销量
if data[data['type'].str.contains('零售_')]['时间'].drop_duplicates().shape[0] == 1:#一天有两条销售记录的也不参与计算
# print(k, '商品指定周期期内只有一天数据')
return 0
# 计算日销量 日销量 = 总销量/ (数据销售的时间跨度-库存为0到下一次进货/有销售记录 那一段天数)
inventory_0_index = data[data['月平均库存量'] == 0]['时间'].index
inventory_0_time = data[data['月平均库存量'] == 0]['时间'].values
day_span_list = []#库存为0无法售卖的天数列表
for i, index in enumerate(inventory_0_index):
if len(data[data.时间 > inventory_0_time[i]]['时间']) > 0: # 如果库存为0的不是该商品销售记录时间最大的一条
inventory_0_nextday = data[data.时间 > inventory_0_time[i]]['时间'].min()
day_span = ((inventory_0_nextday - data[data.index == (index)]['时间']).values / np.timedelta64(
60 * 24, 'm')).tolist()[0] # 库存为0的日期到下次进货/有销售记录 那一段天数
day_span_list.append(day_span)
else:
day_span_list.append(0)
total_day_span = sum(day_span_list)
sale_day = (max_time - data[data['type'].str.contains('零售_')]['时间'].min()).days - total_day_span+1#真实销售天数
if sale_day == 0:
print(k,'计算异常')
drop_spid_list.append(k)
return 0
else:
filter_data = data[data['type'].str.contains('零售_')]#进货的数量不计算在内
day_sale = filter_data['销售数量'].sum() /sale_day
return day_sale
test_day_sale = 计算日销售量(test_data, test_max_time)
train_day_sale = 计算日销售量(train_data, train_max_time)
if test_day_sale!= 0 and train_day_sale!=0:
eval_score = abs(train_day_sale - test_day_sale) / test_day_sale
day_sale_evaluate_list.append(eval_score)
# print(k, eval_score)
sale_score_pd =pd.DataFrame(np.array(day_sale_evaluate_list))
print('不同种类的商品预测偏差分布详情:',sale_score_pd.describe() )
#四分位差法 去除异常值
Q1 = np.percentile(sale_score_pd, 25)
Q3 = np.percentile(sale_score_pd, 75)
IQR = Q3 - Q1
outlier_step = 1.5 * IQR
lower_bound = Q1 -outlier_step
upper_bound = Q3 + outlier_step
print('不同种类的商品使用四分位差法去除异常值预测偏差分布详情:', pd.DataFrame(np.array([x for x in day_sale_evaluate_list if lower_bound<x<upper_bound])).describe())
#
filter_dict = dict(df[df['type'].str.contains('零售')]['spid'].value_counts()) # 每种类别数量
print('使用模型预测销售量的商品覆盖率', len(day_sale_evaluate_list) / len(list(filter_dict.keys())),
'具体占比为', len(day_sale_evaluate_list), '/', len(list(filter_dict.keys())))
# print('上架时间不够一个月,不做预测的商品列表有', drop_spid_list)
销量预测模型2:
预测月销量
数据:
时间’YYYY-mm’ datetime
月销量 float
1.对商店的商品进行分类。(可以根据变异系数划分,商品有新品,热销稳定sigma<=0.5,长尾不稳定sigma>1,周期性强的商品
2.稳定的商品使用指数平滑预测,长尾使用泊松分布,
周期性强的商品使用 时间序列分析 (可通过做差分判断,序列是否平稳)/趋势法(去年月销量除以均值,得到在1左右的12个数,今年的销量预测使用今年销量的均值*对应趋势值,如果有两年数据,判断两年的趋势列表可知周期性是否强)
思路:
通过时间拆分验证集和测试集
数据难点:
0.销量为0的商品在数据库没有流水记录,没有’2020-7’:0这样数据(设置一个年-月时间列表和一个销量空列表,循环年月列表,如果该年-月找不到记录, 销量列表的该年月索引处的值设置为0)
1.部分商品有些月份销量为0(延长时间跨度 ,或者使用平滑法/中位数/均值填充)
2.部分商品 的 销售开始日期 不一致(不做预测)
3.部分商品在周期截止前下架(比如近3月没有数据,或者库存为0判断,使用之前月销量均值填充下架后月份对应的销量,这样商品覆盖率会全些)