單因子變異數分析 — Python實戰:如何決定多廣告的優化策略?(附Python 程式碼)

徐子皓 Nash Xu
Marketingdatascience
11 min readDec 3, 2020

在上一篇文章當中,我們已經完成了資料前處理,接下來請務必將執行後資料保留下來,用以操作接下來的資料分析。

複習一下上一篇文章吧:【單因子變異數分析 — Python實戰:商務資料結構整理】(附Python 程式碼)

Photo by: Shopify Partners

一、事前檢驗

在開始進行單因子變異數分析之前,得先做兩件事以確保分析資料是可以可靠地被使用的:

1. 常態檢定:確認資料呈現常態分配,才可以代表母體作分析。

2. 同質性檢定:確保資料取自變異數相等的母體。

透過程式碼1,可以將這三筆資料分別帶入shapiro模型中進行計算,以利確認資料是否呈現常態分佈:

程式碼1:

import scipy.stats as st
st.shapiro(alist)
st.shapiro(blist)
st.shapiro(clist)

產出:

ShapiroResult(statistic=0.9915865659713745,pvalue=0.6819638013839722)
ShapiroResult(statistic=0.9929247498512268,pvalue=0.805463969707489)
ShapiroResult(statistic=0.9802003502845764,pvalue=0.07433854043483734)

從檢定結果可以看到三者的執行結果P值都>0.05,因此沒有顯著的證據可以證明這三筆資料不符合常態分配。

緊接著,如果要檢驗這三筆資料是否具有同質性,可以透過程式碼2,進行Levene檢驗。

程式碼2:

st.levene(alist, blist, clist, center='mean')

產出:

LeveneResult(statistic=0.6891243668404422,pvalue=0.5026819827621525)

Levene檢定結果為P值約0.5左右,大於普遍定義顯著水準的0.05,因此沒有顯著的證據證明這三筆資料之間不同值。

二、變異數分析

確認完常態性及同質性後,便可透過程式碼3進行變異數分析了。

程式碼3:

f_value, p_value = st.f_oneway(alist, blist, clist)
p_value

產出:

0.0955577857122512

檢測結果P值約為0.095,雖不在普遍定義的0.05內,但如果放寬表準來看也趨近於顯著水準內了,因此本單元仍會繼續後續的分析。

三、事後檢定

即使透過變異數分析判斷了樣本間是否具有顯著差異,我們仍不足以從檢定結果中看出是哪個廣告大、哪個廣告小。因此還需透過「pairwise_tukey」事後檢定做出比較表格,來幫助作為管理決策參考,如程式碼4。

程式碼4:

from pingouin import pairwise_tukey
m_comp = pairwise_tukey(data=data, dv='消費金額', between='廣告')

產出(圖1):

圖 1 事後比較表

從表中可以看到一些適合作為後續分析的欄位:

1. Diff:兩種廣告之間的消費金額平均差異

2. Se:標準誤,可用來協助估算平均值的範圍區間

透過程式碼5,可以將事後比較表中不需要的欄位內容先行刪除,並另外命名為新的資料集-「table」:

程式碼5:

table = m_comp.drop(columns = ['mean(A)', 'mean(B)', 'T', 'p-tukey', 'hedges'])

產出(圖2):

圖 2 整理後事後比較表

從這張表理可以看到不同廣告之間平均消費金額的差異,如第0列廣告1-廣告2平均高出1.54275美金,標準誤差為0.781281美金等。

四、進階資料處理

為了讓比較可以更全面,除了計算出A-B以外,另外也要將B-A的資料放入至資料集中,因此需透過程式碼6,計算出B-A的資料。

程式碼6:

# 「A」欄反轉資料
add_A = table['B'].tolist()
# 「B」欄反轉資料
add_B = table['A'].tolist()
# 「diff」欄反轉資料
diff = (table['diff'] - 2 * table['diff']).tolist()
# 「se」欄反轉資料
se = table['se'].tolist()
# 將反轉資料合併
table2 = pd.DataFrame(zip(add_A, add_B, diff, se), columns = ['A', 'B', 'diff', 'se'])

產出(圖3):

圖 3 反轉後事後比較表-table2

隨後,便可將table1以及table2合併,如程式碼7,製作成完整的資料集-「new_table」,如下(圖4)所示

程式碼7:

new_table = pd.concat([table, table2], ignore_index=True)

產出(圖4):

圖 4 完整比較表

現在的資料集內含有不同組合廣告間的差異及標準誤,已經趨近於完整了。但仍無法直接透過肉眼判斷廣告之間是否有顯著的差異,因此需要計算出在95%信心水準下,廣告之間的差異會落在什麼範圍內,計算方式如下:

下界(範圍內最低點):平均差異-(標準誤*1.96)

上界(範圍內最高點):平均差異+(標準誤*1.96)

透過程式碼8,將上下界加入至「new_table」中:

程式碼8:

new_table['上界'] = new_table['diff'] + new_table['se']*1.96
new_table['下界'] = new_table['diff'] - new_table['se']*1.96

產出(圖5):

圖 5 加入上下界後的比較表

判斷廣告之間平均消費金額是否顯著的方式很簡單,觀察每一種比較組合的上下界是否正負值皆相等。若上下界都為正數,代表資料A的平均消費金額顯著地大於資料B;相反地,若上下界都是負數,則代表資料A的平均消費金額顯著地小於資料B。

為了判斷數值的正負數,在這邊需要用到numpy模組中的一個功能「sign」,「sign」可以自動判斷資料是正數或是負數,在面臨大量資料處理時,對於資料處理人員是一樣非常好用的工具!!

透過程式9,可以了解「sign」是如何運作的。

程式碼9:

import numpy as np
np.sign(12)
np.sign(-5.6)

產出:

1
-1.0

由上可見,當把12放入「sign」中進行判斷時,會回報出數「1」,代表正數,若把-5.6放入「sign」中,則會回報「-1」代表負數。

了解這概念後,便可以透過程式碼10,將「new_table」每一列的上下界做正負數判斷,並儲存至清單「justice」中。若同為正數或負數,則代表顯著有差異;若一正一負,則不顯著。

程式碼10:

justice = []
for i in range(0,new_table.shape[0]):
a = np.sign(new_table.iloc[i,4]) # 上界正負數判斷
b = np.sign(new_table.iloc[i,5]) # 下界正負數判斷
if a == b:
justice.append('Yes')
else:
justice.append('No')

產出(圖6):

圖 6 new_table中每一列的平均消費金額是否顯著有差異的比較結果-「justice」

有了判斷結果之後,再將它回存至比較表「new_table」中,完整的比較表就大功告成了!如程式碼11所示:

程式碼11:

new_table['是否顯著'] = justice

產出(圖7):

圖 7 更新後比較表

五、資料視覺化

最後,終於要產出我們的結果了!!可將比較表內的資料作成視覺化圖片,如下程式碼12所示:

程式碼12:

import plotly.offline as py
import plotly.graph_objects as go
fig = go.Figure()
for i in range(0, new_table.shape[0]):
if new_table.iloc[i,6] == 'Yes':
color = 'firebrick'
name = '顯著'
else:
color = 'green'
name = '不顯著'
fig.add_trace(go.Scatter(
x = [new_table.iloc[i,5], new_table.iloc[i,2], new_table.iloc[i,4]],
y = [new_table.iloc[i,0] + '-' + new_table.iloc[i,1], new_table.iloc[i,0] + '-' + new_table.iloc[i,1], new_table.iloc[i,0] + '-' + new_table.iloc[i,1]],
mode = "lines+markers",
textfont=dict(
family="sans serif",
size=16,
color=color),
line=dict(color=color, width=2),
name = name,
legendgroup = name,
))

首先,先透過for迴圈逐行讀入資料,並透過if判斷式判斷「是否顯著」這個欄位內的內容是「yes」 or 「no」,並依此去分「顯著」組以及「不顯著」組。

繪圖的部分和前面章節所使用的設定有異曲同工之妙,唯多了一行「legendgroup」的設定,會將上面所提到的「顯著」組以及「不顯著」組分別組成一類。

程式碼13的版面設置也和前面有繪圖的單元設定方式一致,在此不多贅述,如下所示:

程式碼13:

fig.update_layout(
title={
'text': "<b>One-Way ANOVA 廣告效益分析</b>",
'y':0.95,
'x':0.5,
'xanchor': 'center',},
width=1800,
height=960,
boxmode='group',
font=dict(
family="Courier New, monospace",
size=20,
color="lightslategrey"
)
)

最後,執行程式碼14,便可輸出互動式網頁圖以及.png圖檔了。

程式碼14:

# 另存互動式網頁
py.plot(fig, filename='One-Way ANOVA 廣告效益分析', auto_open=True)
# 另存.png圖檔
fig.write_image("C:/Users/user/Desktop/單因子變異數分析1.5/One-Way ANOVA 廣告效益分析.png")

產出(圖8):

圖 8 單因子變異數分析-廣告成效比較圖

從圖中(圖8)可以看到:

  1. 平均消費金額在不同廣告推播下的差異區間,紅線代表顯著有差異,綠色代表顯著無差異。
  2. 而圖中唯一有顯著差異的組合為廣告1以及廣告2,可看出廣告1帶來的平均消費金額顯著地大於廣告2。

但請思考看看,這樣的結果是否就代表我們可以完全捨棄廣告2,專放於投放廣告1呢?

答案是:這可不一定哦~~

在本篇文章中沒有考慮到廣告以外的其他因素,包括地區、性別、年紀等其他變數,將這些因素交叉比較後,可能會有更進一步的管理意涵,例如:55歲以上的北部男性會受廣告1刺激消費最深、18~24歲中部女性會受廣告2刺激消費最深等。

若過於武斷地停止使用廣告2,可能會造成部分客群的流失,進而喪失潛在商機。

未來我們也會和大家繼續分享多因子變異數分析的應用,如何透過考量兩個以上的變數去交叉分析,以利找出針對不同族群該推薦什麼樣的廣告最為合適。

若是喜歡我的文章,記得給我拍手哦~
讓我有無窮的動力一直一直一直寫下去!!

完整程式碼:https://reurl.cc/k0X6Rn

作者:徐子皓(臺灣行銷研究特邀作者)、鍾皓軒(臺灣行銷研究有限公司創辦人)

--

--