我們可以構(gòu)建許多復(fù)雜的模型來解決預(yù)測問題。但是,我們常用到的是基于歷史平均值,直覺或者某些特定領(lǐng)域啟發(fā)式發(fā)展出來的Excel模型。這種方法也許足夠解決現(xiàn)在的問題,但是通過一些合理的方式,我們可以為預(yù)測提供更多信息。
蒙特卡羅模擬是一種更直觀地理解潛在結(jié)果的方法,并且有助于避免“均值的缺陷”。整篇文章主要描述如何用python構(gòu)建一個蒙特卡羅模擬用于預(yù)測銷售傭金支出的潛在范圍。這種方法足夠解決你現(xiàn)在所遇到的問題,同時也提供有力的見解,這都是那些靠直覺構(gòu)建的Excel模型所辦不到的。
問題背景
例如,我們試著預(yù)測下一年的銷售傭金支出。這個問題很容易去建模,因為我們知道怎樣算傭金,可能還有記錄之前幾年的傭金支出數(shù)據(jù)。
從業(yè)務(wù)的角度來看,銷售傭金預(yù)測是很有必要的。銷售傭金是一大筆費用,所以有必要合理地計劃這筆支出。另外,對比以前常用的測算銷售傭金的過程,蒙特卡羅模擬可以提供相對低成本的優(yōu)化。
在下面的示例中,5個銷售人員的銷售傭金如下所示:
此例中,傭金計算公式如下:
傭金 = 實際銷售額 × 提成比例
提成比例根據(jù)銷售計劃完成百分比調(diào)整如下:
在構(gòu)建蒙特卡羅模擬前,我們先了解一下在一般情況下預(yù)測明年傭金費用的過程。
銷售傭金預(yù)測 –樸素法
假設(shè)一下,我們的任務(wù)是預(yù)測下一年的銷售傭金支出并為此籌備資金。我們可能假設(shè)每個銷售人員都全額完成銷售目標(biāo),也即按4%比例賺取傭金。如下圖所示:
但在實際業(yè)務(wù)過程中,不是每個人都能確保全額完成銷售目標(biāo),因此該預(yù)測并不合理。
接下來,我們嘗試了兩組對于銷售計劃百分比的預(yù)測:
至此,我們有了更符合實際的預(yù)測,并且可以進(jìn)行下一步籌資活動了。這時有人會產(chǎn)生幾個疑慮a)我們對于以上這種預(yù)測有多少準(zhǔn)確度?b)假如給出500人的銷售團(tuán)隊,并給更多的提成比例階級。我們該怎么做呢?
以上這種預(yù)測支出過程給蒙特卡羅模擬提供了最基礎(chǔ)的迭代方式。將上述預(yù)測支出過程迭代數(shù)百次,我們可以得到一個下年度銷售傭金支出預(yù)算的大致區(qū)間。由于蒙特卡羅模擬中需要大量重復(fù)上述預(yù)測支出的過程,用Excel去計算有些難度,python可以更好地完成迭代任務(wù)。
蒙特卡羅
我們可以更進(jìn)一步討論傭金支出預(yù)算問題。蒙特卡羅模擬是一個有效的工具,主要體現(xiàn)在這種方法能夠根據(jù)每次迭代時輸入的隨機(jī)參數(shù)得到許多不同的情景,并且可以得到目標(biāo)結(jié)果的分布情況。
利用上一節(jié)中的傭金預(yù)算方法,我們可以重復(fù)這一過程100次,甚至1000次之后,我們可以得到潛在的傭金支出的分布情況。該分布可以展示傭金支出落入某個區(qū)間的可能性。歸根結(jié)底,這僅僅是一種預(yù)測,因此我們無法準(zhǔn)確地預(yù)測傭金支出。但是我們擁有了更多的信息去為預(yù)算不足或預(yù)算超支的潛在風(fēng)險制定方案。
要進(jìn)行蒙特卡羅模擬,有兩個要件:
相同的過程
隨機(jī)的輸入變量
我們參照上一節(jié)已經(jīng)清楚了整個預(yù)測支出的流程?,F(xiàn)在我們需要思考的是如何產(chǎn)生隨機(jī)變量。
一種簡易的方法是銷售計劃百分比在0%-200%之間取隨機(jī)值。但是,由于我們過去每年都在支付傭金,因此我們可以理解傭金支出里的更多的細(xì)節(jié)(例如過去幾年的銷售人員銷售進(jìn)度的分布情況)。根據(jù)我們對于過去的經(jīng)驗,我們可以構(gòu)建一個更符合實際的模型。
我們先看看歷史銷售目標(biāo)完成狀況的分布圖,如下:
這個分布看起來似乎是一個均值100%,方差10%的正態(tài)分布。這有利于我們?nèi)ツM隨機(jī)輸入變量,以去匹配更真實的狀況。
假如對于分布類型想要更多的了解,可以自行百度,谷歌搜索更多關(guān)于各種分布的詳細(xì)資料。
建立基于python的蒙特卡羅模擬
這節(jié)主要用pandas去復(fù)刻Excel表格中的計算過程。在python中有其他方法去構(gòu)建蒙特卡羅模擬,這里用pandas庫為了方便Excel基礎(chǔ)的用戶去理解模擬過程。
首先,完成庫的導(dǎo)入和繪圖風(fēng)格的設(shè)置:
import pandas as pd
import numpy as np
import seaborn as sns
sns.set_style('whitegrid')
此次建模,用了numpy庫中的隨機(jī)數(shù)生成器。numpy的優(yōu)勢在于它的隨機(jī)數(shù)生成器可以根據(jù)預(yù)定義的分布去生成隨機(jī)樣本。
此前已經(jīng)知道歷史的傭金支出服從均值100%,方差10%的正態(tài)分布。在python中,我們可以如下定義:
avg = 1
std_dev = .1
num_reps = 500
num_simulations = 1000
現(xiàn)在可以根據(jù)歷史的分布,用numpy產(chǎn)生一組銷售計劃完成百分比模擬數(shù)據(jù):
pct_to_target = np.random.normal(avg, std_dev, num_reps).round(2)
本例中,為了整潔只保留小數(shù)點2位。以下展示500銷售人員中部分的銷售人員的銷售計劃完成百分比預(yù)測情況,以確認(rèn)數(shù)值范圍是否與預(yù)期相符:
array([0.92, 0.98, 1.1 , 0.93, 0.92, 0.99, 1.14, 1.28, 0.91, 1. ])
可以看到使用銷售目標(biāo)百分比的正態(tài)分布可以改善模型。分布的選擇可以根據(jù)你正在研究對象或者模型進(jìn)行調(diào)整。但是,最好在分析并理解了新的數(shù)據(jù)分布之后,再確定所對應(yīng)的分布并應(yīng)用在你的模型中。
這里有一組需要被模擬的實際銷售額目標(biāo)圖,直方圖看起來如下:
很明顯這組數(shù)據(jù)不符合正態(tài)分布。這個分布展示了銷售額目標(biāo)頻數(shù)隨著銷售額的增加而減少。這個分布可以理解成:根據(jù)層級,渠道,地域等等的區(qū)分,需完成的銷售額目標(biāo)也不同。
為了模擬這組數(shù)據(jù),可以使用均勻分布,但是給一些值分配較低的概率。這里我們使用了numpy.random.choice模塊,代碼如下:
sales_target_values = [75_000, 100_000, 200_000, 300_000, 400_000, 500_000]
sales_target_prob = [.3, .3, .2, .1, .05, .05]
sales_target = np.random.choice(sales_target_values, num_reps, p=sales_target_prob)
這組數(shù)據(jù)其實不具有代表性,這樣做只是為了展示如何將不同的分布應(yīng)用在我們的模型中。
現(xiàn)在有了兩組不同分布的模擬數(shù)據(jù)(銷售計劃完成百分比數(shù)組和銷售額目標(biāo)數(shù)組),將它們并入一個DataFrame:
df = pd.DataFrame(index=range(num_reps), data={'Pct_To_Target': pct_to_target,
'Sales_Target': sales_target})
df['Sales'] = df['Pct_To_Target'] * df['Sales_Target']
新的DataFrame如下:
從上邊的代碼中可以看到我們使用了兩組模擬數(shù)組計算出了模擬的最終銷售額。對于這個情況,最終銷售額(sales)也許會隨著年份的變化出現(xiàn)大幅波動,但是這組被組合的新數(shù)組的分布大體是被前兩組數(shù)組確定下來的。
最后一步,需要根據(jù)銷售目標(biāo)完成進(jìn)度映射對應(yīng)的提成比例:
def calc_commission_rate(x):
''' Return the commission rate based on the table:
0-90% = 2%
91-99% = 3%
>= 100 = 4%
'''
if x <= .90:
return .02
if x <= .99:
return .03
else:
return .04
相比Excel,python優(yōu)勢在于可以創(chuàng)建更復(fù)雜的邏輯,且更易實現(xiàn)。現(xiàn)在我們向DataFrame里加入對應(yīng)提成比例和銷售傭金,代碼如下:
df['Commission_Rate'] = df['Pct_To_Target'].apply(calc_commission_rate)
df['Commission_Amount'] = df['Commission_Rate'] * df['Sales']
最新的DataFrame如下:
至此,我們完成了一次銷售傭金預(yù)測。我們復(fù)刻了Excel中的算法,并且加入了兩組隨機(jī)分布的數(shù)組。從上表,可以計算出這次模擬的銷售傭金支出為2,923,100美元。
開始循環(huán)(Loop)
蒙特卡羅魅力之處在于循環(huán)許多次之后,可以看到最終結(jié)果的分布情況。在Excel中,可能需要VBA或者其他插件才能實現(xiàn)多次迭代。在python中,使用for語句可以自己定義循環(huán)次數(shù)。
另外,每一次模擬,可以將需要的結(jié)果保存在DataFrame中,方便分析更多數(shù)組的分布情況。
代碼如下:
# Define a list to keep all the results from each simulation that we want to analyze
all_stats = []
# Loop through many simulations
for i in range(num_simulations):
# Choose random inputs for the sales targets and percent to target
sales_target = np.random.choice(sales_target_values, num_reps, p=sales_target_prob)
pct_to_target = np.random.normal(avg, std_dev, num_reps).round(2)
# Build the dataframe based on the inputs and number of reps
df = pd.DataFrame(index=range(num_reps), data={'Pct_To_Target': pct_to_target,
'Sales_Target': sales_target})
# Back into the sales number using the percent to target rate
df['Sales'] = df['Pct_To_Target'] * df['Sales_Target']
# Determine the commissions rate and calculate it
df['Commission_Rate'] = df['Pct_To_Target'].apply(calc_commission_rate)
df['Commission_Amount'] = df['Commission_Rate'] * df['Sales']
# We want to track sales,commission amounts and sales targets over all the simulations
all_stats.append([df['Sales'].sum().round(0),
df['Commission_Amount'].sum().round(0),
df['Sales_Target'].sum().round(0)])
上述代碼中,每次模擬只有7個python語句。在普通的筆記本電腦上執(zhí)行1000次模擬只需要2.75秒,可以根據(jù)需要去調(diào)整模擬的次數(shù)。在模擬次數(shù)的問題上,一般是隨次數(shù)增加收益遞減的。一百萬次的模擬不一定比10,000次的模擬更有效,筆者的建議是:嘗試不同的模擬次數(shù),看看輸出結(jié)果將如何變化。
為了分析模擬結(jié)果,將最終數(shù)據(jù)寫入all_stats變量中:
results_df = pd.DataFrame.from_records(all_stats, columns=['Sales','Commission_Amount', 'Sales_Target'])
結(jié)果如下:
results_df.describe().style.format('{:,}')
直方圖如下:
可以看到,平均傭金支出為285萬美元,標(biāo)準(zhǔn)差為103,000美元。同時傭金支出最低值250萬美元,最高值320萬美元。
基礎(chǔ)上述結(jié)果,可以確定傭金支出會低于300萬美元嗎?顯然不能(畢竟只是個預(yù)測)!蒙特卡羅模擬的優(yōu)勢在于,可以展示出易于理解的結(jié)果分布情況,并且憑借商業(yè)嗅覺和經(jīng)驗去做出合理的估計。
你也可以根據(jù)不同的假設(shè)去做模擬出不同的結(jié)果,例如:
提成比例調(diào)至5%
減少銷售人員
分布中用更大的標(biāo)準(zhǔn)差
調(diào)整目標(biāo)數(shù)組的分布
現(xiàn)在模型已經(jīng)建立好了,只需要更改這些參數(shù)并重新運行模擬就好了。
蒙特卡羅的另一個優(yōu)勢是便于向最終用戶解釋該預(yù)測。最終用戶也許沒有良好數(shù)學(xué)背景,但也能直觀地了解此模擬正在做什么以及如何評估潛在結(jié)果范圍的可能性。
最后,筆者認(rèn)為,用python比用Excel更易展示和理解。由于python是編程語言的緣故,它將整個計算流程都列了出來。
結(jié)論
蒙特卡羅模模擬是一個非常好用的預(yù)測工具。在Excel中,如果沒有VBA或者一些付費的第三方插件,整個模擬很難進(jìn)行。使用Numpy和Pandas庫去建立模型和產(chǎn)生多組潛在的目標(biāo)數(shù)組,并加以分析都變得相對簡單直觀。
另外,分析師可以通過改變參數(shù)實現(xiàn)更多的模擬情景,也可以根據(jù)需求加入更多的模型。最后,這些結(jié)論還能夠與非技術(shù)人員分享,并且圍繞結(jié)果的不確定性進(jìn)行討論。
本文來自互聯(lián)網(wǎng),由Tushare金融與技術(shù)翻譯興趣小組翻譯,作者:One,西南財經(jīng)大學(xué)應(yīng)用數(shù)據(jù)本科,英國曼徹斯特大學(xué)金融數(shù)學(xué)碩士,金融分析師,專注于利用數(shù)據(jù)建立金融模型,發(fā)掘潛在投資價值。更多內(nèi)容請關(guān)注“挖地兔”公眾號。