阿里天池大賽實戰記錄之菜鳥-需求預測與分倉規劃

概述

新賽季,天池終於迎來了我本專業的命題:菜鳥物流規劃和需求預測。接下來的比賽中,本文會陸續記錄比賽的詳細過程,可是不能保證最後的結果優劣,但願對你們有一些啓發,文章僅供參考,請勿模仿。segmentfault

什麼是天池大賽

下面是官方介紹架構

天池平臺基於阿里雲的海量數據離線處理服務ODPS,向學術界提供科研數據和雲計算資源,旨在打造「數據衆智、衆創」第一平臺。app

簡單來講,天池就是相似於 Kaggle 的一個數據挖掘比賽平臺,不一樣於 Kaggle 的是,天池提供的比賽命題更與實際結合,提供的數據也須要咱們作更多的清洗工做。ide

問題概述

賽題介紹

下面是官方介紹函數

阿里巴巴旗下電商擁有海量的買家和賣家交易場景下的數據。利用數據挖掘技術,咱們能對將來的商品需求量進行準確地預測,從而幫助商家自動化不少供應鏈過程當中的決策。這些以大數據驅動的供應鏈可以幫助商家大幅下降運營成本,提高用戶的體驗,對整個電商行業的效率提高起到重要做用。這是一個困難可是很是重要的問題。咱們但願經過此次的大數據競賽中獲得一些對這個問題的新穎解法,朝智能化的供應鏈平臺方向更加邁進一步。測試

高質量的商品需求預測是供應鏈管理的基礎和核心功能。本賽題以歷史一年海量買家和賣家的數據爲依據,要求參賽者預測某商品在將來二週全國和區域性需求量。選手們須要用數據挖掘技術和方法精準刻畫商品需求的變更規律,對將來的全國和區域性需求量進行預測,同時考慮到將來的不肯定性對物流成本的影響,作到全局的最優化。更精確的需求預測,可以大大地優化運營成本,下降收貨時效,提高整個社會的供應鏈物流效率。大數據

簡單的說就是物流的需求預測優化

評測指標

在本賽題中,參賽者須要提供對於每一個商品在將來兩週的全國最優目標庫存和分倉區域最優目標庫存的預測。咱們會提供每個商品的補少成本(A)和補多成本(B),而後根據用戶預測的目標庫存值跟實際的需求的差別來計算總的成本。參賽者的目標是讓總的成本最低。阿里雲

這裏的補多主要是 商品積壓帶來的資金成本、倉儲成本等。補少成本主要是商品不足帶來的錯失銷售時機等缺貨成本。雲計算

過多的供應會帶來庫存成本的增長,過少的供應又會錯失銷售時機,因此需求預測是供應鏈管理很是關鍵的一環。

簡單來講,目標函數是全國的物流成本最低,變量是各個倉庫的商品數量,約束條件是補多和補少。

數聽說明

下面是官方說明:

咱們提供商品從20141001到20151227的全國和區域分倉數據。參賽者需給出後面兩週(20151228-20160110)的全國和區域分倉目標庫存。 商品在全國的特徵包括商品的自己的一些分類:類目、品牌等,還有歷史的一些用戶行爲特徵:瀏覽人數、加購物車人數,購買人數。注意咱們要預測的將來需求是「非聚划算支付件數」(qty_alipay_njhs)

咱們同時也提供商品的區域分倉歷史數據,這些數據的維度跟全國的數據同樣,僅有的差異是這些數據表達的是某個倉負責的地理區域內的用戶行爲。好比qty_alipay_njhs在這裏表達的是這個倉負責的區域內的用戶的「非聚划算支付件數」。

咱們還提供每一個商品在全國和分倉區域的補少、補多的成本,能夠用來計算總成本

請注意咱們這裏須要預測的是將來兩週的「非聚划算」銷量,即去掉了商品參加聚划算產生的銷量。咱們提供的數據通過了脫敏,和實際商品的銷量、瀏覽量和成本等有一些差距,可是不會影響這個問題的可解性。

下載官網數據咱們能夠看到:

-rwxr-xr-x@ 1 harryzhu  staff   116K Mar 25 17:57 config1.csv # 每一個商品在全國和分倉區域的補少、補多的成本
-rwxr-xr-x@ 1 harryzhu  staff    22M Apr  8 12:03 item_feature1.csv # 商品粒度相關特徵
-rwxr-xr-x@ 1 harryzhu  staff    84M Apr  8 12:04 item_store_feature1.csv # 商品和分倉區域粒度相關特徵
-rw-r-----@ 1 harryzhu  staff    63K Apr  9 20:47 sample_submission.csv # 提交示例

數據清洗

# 加載依賴包
library(data.table)
library(tidyr)
library(dplyr)

# read data 數據讀取
config1  = fread("config1.csv")
item_feature1  = fread("item_feature1.csv")
item_store_feature1 = fread("item_store_feature1.csv")
sample_submission = fread("sample_submission.csv")

# structure 構建表結構
colnames(item_feature1) = c("date","item_id","cate_id","cate_level_id","brand_id","supplier_id","pv_ipv","pv_uv","cart_ipv","cart_uv","collect_uv","num_gmv","amt_gmv","qty_gmv","unum_gmv","amt_alipay","num_alipay","qty_alipay","unum_alipay","ztc_pv_ipv","tbk_pv_ipv","ss_pv_ipv","jhs_pv_ipv","ztc_pv_uv","tbk_pv_uv","ss_pv_uv","jhs_pv_uv","num_alipay_njhs","amt_alipay_njhs","qty_alipay_njhs","unum_alipay_njhs")
colnames(item_store_feature1) = c("date","item_id","store_code","cate_id","cate_level_id","brand_id","supplier_id","pv_ipv","pv_uv","cart_ipv","cart_uv","collect_uv","num_gmv","amt_gmv","qty_gmv","unum_gmv","amt_alipay","num_alipay","qty_alipay","unum_alipay","ztc_pv_ipv","tbk_pv_ipv","ss_pv_ipv","jhs_pv_ipv","ztc_pv_uv","tbk_pv_uv","ss_pv_uv","jhs_pv_uv","num_alipay_njhs","amt_alipay_njhs","qty_alipay_njhs","unum_alipay_njhs")
colnames(config1) = c("item_id","store_code","a_b")
colnames(sample_submission) = c("item_id","store_code","target")

# 創建新表
hc = as.numeric(as.vector(tstrsplit(config1$a_b,"_")[[1]]))
lc = as.numeric(as.vector(tstrsplit(config1$a_b,"_")[[2]]))
config = cbind(config1,hc,lc)[,.(item_id,store_code,hc,lc),]
head(config)
##
hc_config <- config %>% dplyr::select(item_id,store_code,hc) %>% tidyr::spread(key=store_code,value=hc)
lc_config <- config %>% dplyr::select(item_id,store_code,lc) %>% tidyr::spread(key=store_code,value=lc)

# 結果錶轉化
wide_sumbmission = sample_submission %>% tidyr::spread(key=store_code,value=target)
wide_sumbmission$all <- apply(wide_sumbmission[,-1,with=F][,-6,with=F],1,sum)
long_submission  = wide_sumbmission %>% tidyr::gather(key=item_id,value=target)

目標函數

# 計算成本
zeroMatrix = matrix(0,dim(wide_sumbmission)[1],dim(wide_sumbmission)[2]-1)
hc_configMatrix = as.matrix(hc_config[,-1,with=F]);rownames(hc_configMatrix) = hc_config$item_id
lc_configMatrix = as.matrix(lc_config[,-1,with=F]);rownames(lc_configMatrix) = lc_config$item_id
wide_sumbmissionMatrix = as.matrix(wide_sumbmission[,-1,with=F]);rownames(wide_sumbmissionMatrix) = wide_sumbmission$item_id

targetSales = wide_sumbmissionMatrix
actualSales = zeroMatrix

marginSalesHc = targetSales - actualSales
marginSalesLc = actualSales - targetSales

# 將 <= 0 的部分變成 0
marginSalesHc[marginSalesHc<=zeroMatrix]<-0

# 將 >= 0 的部分變成 0
marginSalesLc[marginSalesLc<=zeroMatrix]<-0

# 總成本計算
sum(hc_configMatrix*marginSalesHc + lc_configMatrix*marginSalesLc)

數據探索

以1號倉庫的全部商品爲例,咱們先將數據轉化爲時間序列格式。通過測試發現,數據存在大量的缺失值。有的是某些商品缺失開始一段的數據,有的是缺失後面一段的數據,還有的是該種商品沒有出現任何銷售記錄。
結合實際經驗,這裏應該理解爲,有的商品尚未上架因此起初的銷售量是缺失值,或者後來被下架了最後一段時間的銷售量是缺失值,亦或者在該倉庫從未出現任何銷售記錄。
這裏,我統一將這些缺失值都置爲0。

library(dplyr)
library(zoo)
library(quantmod)
library(tseries)
library(forecast)

store1 = item_store_feature1[store_code==1][,.(date,item_id,qty_alipay_njhs),] %>%
  tidyr::spread(key=item_id,value=qty_alipay_njhs)

store1$date <- as.POSIXct(as.character(store1$date),tz="",format="%Y%m%d")

store1[is.na(store1)] <- 0

tstore1 = read.zoo(store1)

經過快速數據可視化,咱們對這裏的時間序列有一個整體的觀察。

for(i in seq(names(tstore1))){
  plot(tstore1[,i])
}

問題分析

題目的目標是預測將來幾天各個倉庫各類商品的平均非聚划算銷量,這裏除了基本的時間序列外,還提供了用戶行爲特徵、商品特徵信息。

因此整體思想就是分析時間序列、用戶行爲、商品特徵這三方面的因素對非聚划算銷量的影響。

而傳統的時間序列分析方法一般會將時間序列從這三個方面拆分:

  1. 趨勢因素:總的來看長期的走勢是增加、降低或者停滯。

  2. 季節因素:許多時間序列(好比銷售指標、溫度指數)都是以必定週期變化的(好比年),咱們須要移除這些數據的季節因素。

  3. 不規則波動:從數據集中除了趨勢和週期性波動因素外,對剩下的序列咱們還須要觀察它是不是純隨機的。

以1號倉庫爲例,這裏的庫存量很是符合上述規律。

chartSeries(apply(tstore1,1,sum),name="No.1 Store")

tstore1

一階差分以後:

chartSeries(apply(diff(tstore1),1,sum),name="No.1 Store")

diff tstore1

問題假設

假設電商物流量的變化收到消費週期、消費趨勢的影響最大,暫時忽略各平臺的流量影響,和用戶偏好的影響。

模型設計

時間序列模型

對於短時間的時間預測,ARIMA模型是公認的有效手段。首先,咱們對時間序列的平穩性作一下平穩性檢驗。接着,咱們構建ARIMA模型,利用forecast包,自動選擇參數,並做出預測。因爲這裏參數選擇的過程循序漸進,這裏我單獨寫了一個函數來處理每個商品的狀況。

predictItemStore <- function(tstore){
    if(sum(tstore) != 0){
      (adfTestPvalue = broom::tidy(adf.test(tstore,alternative = "stationary"))$p.value)
      if(adfTestPvalue < 0.1){
        (predictValue = broom::tidy(forecast::forecast(forecast::auto.arima(na.omit(tstore)),14)$mean)[1,])
        print(paste0("prediction value: ",predictValue))
      }
    }else{
      (predictValue = 0)
      print(paste0("prediction value: ",predictValue))
    }
    return(predictValue)
}
system.time(apply(tstore1,2,predictItemStore))

運行時間還算能夠接受 總共花費了140s。

# user  system elapsed 
# 128.453  10.216 140.872

結果檢驗

rank

參考文獻

相關文章
相關標籤/搜索