库存监控 - 异常检测算法

库存监控 - 异常检测算法,第1张

库存监控 - 异常检测算法

目录

背景

第一部分 - 数据整理

1. 数据录入

2. 清洗数据

3. 数据调整、聚合

第二部分 - 探索库存数据(EDA)

1. 了解库存整体情况

2. 可视化


背景

        对于零售行业来讲,无论是跨境电商还是传统零售商,库存的高低决定企业流动资产的高低,库存的流动速度决定企业现金流动是否健康。不同的企业会有不同的衡量标准,也会在不同的时间段处于不同的状态。财务上有很多指标可以在整个公司的维度监控库存水平高低,但如果细致到SKU这个层面的差异化分析,则会衍生许多标准问题以及工作量。本文旨在探究使用非监督学习的方法,从数据的整理到最终的可视化,如何实现零售行业的库存监控问题。

第一部分 - 数据整理 1. 数据录入
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import gridspec
from math import ceil

# 读取数据
df = pd.read_csv('inventory.csv')

# 由于数据有重复,删除重复的数据(数据中包含产品分仓库的明细数据以及仓库栏为‘All’的加总数据)
df = df[df['Warehouse'] != 'All']

# 显示头5行数据
df.head()
 Part Number        Item Number    Warehouse  Total On Hand  
0    BAILLIE WALNUT  BF481825.39901727  KY - Hebron              0   
1     BAILLIE BEECH  BF481825.39901731  KY - Hebron              0   
2  NIELSEN BROWN PU  BF763074.46849385  KY - Hebron              0   
3     Scargill Mint  BF767982.46854251  KY - Hebron              0   
4     Scargill Blue  BF767982.46854254  KY - Hebron             34   

   Total Available  Total Unavailable  Total On Transfer  Qty Allocated  
0                0                  0                  0              0   
1                0                  0                  0              0   
2                0                  0                  0              0   
3                0                  0                  0              0   
4                0                  1                 33              0   

   Total On Order  Qty Received(12 Months)  Qty Shipped (30 Days)  
0               0                      201                      0   
1               0                        0                      0   
2               0                        0                      0   
3               0                        0                      0   
4               0                      170                      0   

   Qty Reserved  Qty Unpickable  Qty On Hold  Qty Unprocessed Cycle Count  
0             0               0            0                            0   
1             0               0            0                            0   
2             0               0            0                            0   
3             0               0            0                            0   
4             0               0            1                            0   

   Qty Unprocessed Adjustment  
0                           0  
1                           0  
2                           0  
3                           0  
4                           0  
2. 清洗数据
# 检查数据,清洗
tab_info = pd.Dataframe(df.dtypes).T.rename(index={0:'column Type'}) 
tab_info = tab_info.append(pd.Dataframe(df.isnull().sum()).T.rename(index={0:'null values (nb)'}))
tab_info = tab_info.append(pd.Dataframe(df.isnull().sum()/df.shape[0]*100).T.
                                       rename(index={0: 'null values (%)'}))
tab_info
Part Number Item Number Warehouse Total On Hand  
column Type           object      object    object         int64   
null values (nb)           3           0         0             0   
null values (%)    0.0883132           0         0             0   

                 Total Available Total Unavailable Total On Transfer  
column Type                int64             int64             int64   
null values (nb)               0                 0                 0   
null values (%)                0                 0                 0   

                 Qty Allocated Total On Order Qty Received(12 Months)  
column Type              int64          int64                   int64   
null values (nb)             0              0                       0   
null values (%)              0              0                       0   

                 Qty Shipped (30 Days) Qty Reserved Qty Unpickable  
column Type                      int64        int64          int64   
null values (nb)                     0            0              0   
null values (%)                      0            0              0   

                 Qty On Hold Qty Unprocessed Cycle Count  
column Type            int64                       int64   
null values (nb)           0                           0   
null values (%)            0                           0   

                 Qty Unprocessed Adjustment  
column Type                           int64  
null values (nb)                          0  
null values (%)                           0  

会用到的列是:

  • Total On Hand: 产品库存数量
  • Qty Shipped (30 Days): 过去30天的销售数量总和

调查数据的Null缺失值情况良好,可以直接使用。

3. 数据调整、聚合
# 将数据聚合到item number这个层面
df_alinv = df.groupby(['Item Number'])['Total On Hand','Qty Shipped (30 Days)'].sum().reset_index()
df_alinv = df_alinv.sort_values(by = 'Total On Hand', ascending = False).reset_index(drop=True)

# 剔除库存为0的产品
df_alinv = df_alinv[df_alinv['Total On Hand'] > 0]

print(df_alinv.head())
Item Number  Total On Hand  Qty Shipped (30 Days)
0  TNFI1501.41585776          10737                    340
1  TNFI1499.41585774           7723                    161
2  TNFI1081.33297150           3171                    620
3  TNFI1263.38188681           3135                      1
4  TNFI1087.33298045           2530                    101
第二部分 - 探索库存数据(EDA) 1. 了解库存整体情况
# 产品种类、对应库存数量以及分布
no_sku = len(df['Item Number'].unique())
qty_all_inve = df['Total On Hand'].sum()
qtySold30days = df_alinv["Qty Shipped (30 Days)"].sum()
daysOfGoodSold = ceil(qty_all_inve / qtySold30days * 30)

print(f'产品种类数目(包含库存为0的产品):{no_sku}种')
print(f'有库存的产品种类数目:{len(df_alinv)}种')
print(f'库存总量:{qty_all_inve}件')
print(f'过去30天销量:{qtySold30days}件')
print(f'目前库存可以继续卖:{daysOfGoodSold}天')
产品种类数目(包含库存为0的产品):833种
有库存的产品种类数目:463种
库存总量:116142件
过去30天销量:18665件
目前库存可以继续卖:187天
2. 可视化
# 库存量 TOP N SKU 占比
def get_pct(top_n):
    return df_alinv.iloc[:top_n]['Total On Hand'].sum() / qty_all_inve

acc_pct = [i for i in map(get_pct, [i for i in range(len(df_alinv))])]

# 增加变量 - 周转天数
df_alinv['turnover days'] = df_alinv['Total On Hand'] / df_alinv['Qty Shipped (30 Days)'] * 30
df_alinv['turnover days (max=365)'] = df_alinv.apply(lambda x: x['turnover days'] if x['turnover days'] < 365 else 365, axis = 1)

# 库存分布图
fig = plt.figure(figsize=(10, 8))
gs = gridspec.GridSpec(4, 2, width_ratios=[10, 1])

ax0 = plt.subplot(gs[0])
ax0.set_title('SKU Total On Hand distribution')
ax0.bar(df_alinv.index, df_alinv['Total On Hand'])

ax1 = plt.subplot(gs[1])
ax1.set_title('boxplot')
ax1.boxplot(df_alinv['Total On Hand'])

ax2 = plt.subplot(gs[2])
ax2.set_title('SKU TOH acc% evolution')
ax2.plot([i for i in range(len(df_alinv))], acc_pct)

ax3 = plt.subplot(gs[4])
ax3.set_title('SKU Qty Sold in 30 days')
ax3.bar(df_alinv.index, df_alinv['Qty Shipped (30 Days)'])

ax4 = plt.subplot(gs[5])
ax4.set_title('boxplot')
ax4.boxplot(df_alinv['Qty Shipped (30 Days)'])

ax5 = plt.subplot(gs[6])
ax5.set_title('SKU Turnover Days (max=365)')
ax5.set_xlabel('SKU ranked by Total On Hand')
ax5.bar(df_alinv.index, df_alinv['turnover days (max=365)'])

ax6 = plt.subplot(gs[7])
ax6.set_title('boxplot')
ax6.boxplot(df_alinv['turnover days (max=365)'])

plt.tight_layout()
plt.show()

# top 5 sku
print(df_alinv.sort_values(by='Total On Hand', ascending = False).head())

         Item Number  Total On Hand  Qty Shipped (30 Days)  turnover days  
0  TNFI1501.41585776          10737                    340     947.382353   
1  TNFI1499.41585774           7723                    161    1439.068323   
2  TNFI1081.33297150           3171                    620     153.435484   
3  TNFI1263.38188681           3135                      1   94050.000000   
4  TNFI1087.33298045           2530                    101     751.485149   

   turnover days (max=365)  
0               365.000000  
1               365.000000  
2               153.435484  
3               365.000000  
4               365.000000  

从上图可得:

  1. 从库存量来看,大部分的库存集中在少数的商品当中(库存排名前100的sku占总库存量的80%以上),需要考虑在采购过程给单个sku设置上限;
  2. 从销量来看,库存量大的头部产品销售优势不明显,考虑加大去库存的促销力度;
  3. 从库存周转情况来看,库存周转天数中位数在300天左右,其中将近一半的sku周转天数在365天(实际大于等于365天),库存流动整体缓慢。结合之前计算的整体库存可卖187天来看(整体库存量/过去30天整体销售量*30),产品的库存和销售错配情况严重,需要重点关注流动率低的大库存产品。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5575534.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-14
下一篇 2022-12-14

发表评论

登录后才能评论

评论列表(0条)

保存