在做數據分析的過程中。我們往往是–理解需求–獲取數據–清洗數據–簡單描述統計–統計型分析報告;
其實到這裏還沒完。如果我們還需要深入探索數據的價值,那麼,單變量的分佈檢驗–探索變量間的關係–建立關係模型–評估–總結等。
接下來就來看看數據分析的其中重要的一環–探索變量間的關係。
探索變量之間的關係
探索數據變量之間是否存在某種關係/關聯。大致步驟有:
- 變量的類型:類別型/數值型
- 可視化給出可能的方向:散點圖、箱型圖、直方圖、…
- 需建立更嚴格的分析方式:假設檢驗。假設變量間存在某種函數/邏輯等關聯關係,進行檢驗。
一、準備工具和數據
1.1、工具:我們用python3
通常我們把用於做數據分析的幾個庫直接先導入,基本是固定導入使用。
import numpy as np #科學計算基礎庫,多維數組對象ndarray
import pandas as pd #數據處理庫,DataFrame(二維數組)
import matplotlib as mpl #畫圖基礎庫
import matplotlib.pyplot as plt #最常用的繪圖庫
from scipy import stats #scipy庫的stats模塊
mpl.rcParams["font.family"]="SimHei" #使用支持的黑體中文字體
mpl.rcParams["axes.unicode_minus"]=False # 用來正常顯示負號 "-"
plt.rcParams['font.sans-serif']=['SimHei'] # 用來正常顯示中文標籤
# % matplotlib inline #jupyter中用於直接嵌入圖表,不用plt.show()
import warnings
warnings.filterwarnings("ignore") #用於排除警告
#用於顯示使用庫的版本
print("numpy_" + np.__version__)
print("pandas_" + pd.__version__)
print("matplotlib_"+ mpl.__version__)
numpy_1.17.4
pandas_0.23.4
matplotlib_2.2.3
1.2、案例數據
某餐廳顧客消費記錄數據作爲主要的案例數據。
數據源:https://download.csdn.net/download/weixin_41685388/12144418
'''
某餐廳顧客消費記錄.
解釋數據結構:
total_bill:消費,tip:小費,sex:服務員性別,
smoker:是否抽菸,day:星期幾,time:午餐/晚餐,size:本桌人數
'''
tips = pd.read_csv(r"E:\tips.txt",sep='\t',encoding='utf-8') #導入txt格式數據
display(tips.shape) #樣本量
display(tips.sample(5)) #隨機抽樣5行
(244, 7)
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
96 | 27.28 | 4.00 | Male | Yes | Fri | Dinner | 2 |
230 | 24.01 | 2.00 | Male | Yes | Sat | Dinner | 4 |
17 | 16.29 | 3.71 | Male | No | Sun | Dinner | 3 |
110 | 14.00 | 3.00 | Male | No | Sat | Dinner | 2 |
235 | 10.07 | 1.25 | Male | No | Sat | Dinner | 2 |
二、數據變量與變量間的關係
2.1、類別型數據 ~ 類別型數據間的獨立性檢驗
通用:用卡方檢驗
小樣本:用費舍爾檢驗(劣勢:只能檢驗2*2)
- 問題1:探索案例數據中服務員性別與星期幾的關係?
卡方檢驗
'''①看變量的類型:類別型/數值型'''
display(tips.sample(3)) #隨機抽樣3行
statistics = tips[["sex","day"]].describe() #統計
display(statistics)
#兩個變量均是類別型數據,統計往往是進行分類彙總,即需要生成交叉表,用pd.crosstab()
count=pd.crosstab(tips.sex, tips.day)
display(count)
total_bill | tip | sex | smoker | day | time | size | |
---|---|---|---|---|---|---|---|
116 | 29.93 | 5.07 | Male | No | Sun | Dinner | 4 |
146 | 18.64 | 1.36 | Female | No | Thur | Lunch | 3 |
169 | 10.63 | 2.00 | Female | Yes | Sat | Dinner | 2 |
sex | day | |
---|---|---|
count | 244 | 244 |
unique | 2 | 4 |
top | Male | Sat |
freq | 157 | 87 |
day | Fri | Sat | Sun | Thur |
---|---|---|---|---|
sex | ||||
Female | 9 | 28 | 18 | 32 |
Male | 10 | 59 | 58 | 30 |
'''②可視化給出可能的方向'''
count.T.plot(kind='bar')
plt.show()
count=pd.crosstab(tips.sex, tips.day) #構造交叉表
print(stats.chi2_contingency(count,correction=False))
#chi2 卡方檢驗 contingency列聯表 列聯表中每個格子數量至少爲5
#對性別和星期幾進行卡方檢驗
chi2, p, dof, ex = stats.chi2_contingency(count, correction=False) #卡方檢驗,p值很小,AB有關
print(p)
#P值很小,拒絕原假設,說明性別與星期幾有關係
#獨立性檢驗中,H0:AB無關,H1:AB有關係
(13.22200137240661, 0.004180302092822257, 3, array([[ 6.77459016, 31.0204918 , 27.09836066, 22.10655738],
[12.22540984, 55.9795082 , 48.90163934, 39.89344262]]))
0.004180302092822257
- 問題2:探索服務員性別與顧客是否吸菸是否有關?
費舍爾精確檢驗(小樣本)
count=pd.crosstab(tips.sex,tips.smoker)
print(count)
oddsratio,pvalue=stats.fisher_exact(count) #費舍爾精確檢驗
print(oddsratio,pvalue)
#P值很大,不能拒絕H0,是否吸菸和男女沒有關係
smoker No Yes
sex
Female 54 33
Male 97 60
1.0121836925960637 1.0
#如果修改一下列聯表中數據
count.iat[0,0]=2 #手動將交叉表中的[0,0]位置的元素修改爲2
print(count)
oddsratio,pvalue=stats.fisher_exact(count) ##費舍爾精確檢驗
print(oddsratio,pvalue)
#0.03748828491096532 3.9900059898475383e-10
#P值很小,拒絕H0,吸菸和男女有關係
#fisher精確檢驗的優勢:每個表格中數據不一定大於五
# 費舍爾精確檢驗劣勢:只能檢驗2*2
chi2, p, dof, ex = stats.chi2_contingency(count, correction=False) #卡方檢驗
print(p)
#卡方檢驗結果:P值很小,拒絕H0,吸菸和男女有關係
smoker No Yes
sex
Female 2 33
Male 97 60
0.03748828491096532 3.9900059898475383e-10
1.9484526423911992e-09
2.數值型~數值型間獨立性檢驗
-
pearson:積差相關係數,反應兩個變量之間的線性相關性
-
spearman:等級相關係數(Ranked data)—常用
spearman相關係數:先對所有變量進行排序,在做線性相關。與pearson不同,不假設變量爲正態分佈 -
Kendall’s Tau:非參數等級相關係數
tau=(P-Q)/sqrt((P+Q+T)*(P+Q+U))
P:同步數據對數,Q:異步,T:tie in x,U:tie in y
區別及選用
-
pearson和spearman區別:
如果有線性模式,也有一些離散點,spearman線性相關係數要大一些,因爲離散點破壞了線性相關性,但是對rank排序影響不太大
pearson只能處理兩組數據,spearman可以處理多組序列
-
如何選擇:
Kendall’s tau-b(肯德爾)等級相關係數:用於反映分類變量相關性的指標,適用於兩個分類變量均爲有序分類的情況。對相關的有序變量進行非參數相關檢驗;取值範圍在-1-1之間,此檢驗適合於正方形表格;
計算積距pearson相關係數,連續性變量纔可採用;計算Spearman秩相關係數,適合於定序變量或不滿足正態分佈假設的等間隔數據; 計算Kendall秩相關係數,適合於定序變量或不滿足正態分佈假設的等間隔數據。
計算相關係數:當資料不服從雙變量正態分佈或總體分佈未知,或原始數據用等級表示時,宜用 spearman或kendall相關。
總結:在大多數情況下選用spearman檢驗即可
'''可視化給出可能的方向'''
plt.scatter(tips.total_bill,tips.tip)
plt.title("total_bill與tip散點圖")
plt.xlabel("total_bill")
plt.ylabel("tip")
plt.show()
#pearson就是統計學中學過的相關係數
print(stats.pearsonr(tips.total_bill,tips.tip))
#線性相關度和P值
#相關係數=0.6757341092113643,p=6.692470646864041e-34
#p<0.01,拒絕原假設,存在相關性
(0.6757341092113643, 6.692470646864041e-34)
# spearman:等級相關係數(Ranked data)
rho,pval=stats.spearmanr(tips.total_bill,tips.tip)
print(rho,pval)
#p<0.01,拒絕原假設,兩變量存在相關性
0.6789681219001009 2.501158440923619e-34
#多組數據的spearmanr獨立性檢驗
x2n=np.random.randn(100,2)
y2n=np.random.randn(100,2)
rho,pval=stats.spearmanr(x2n,y2n)
print(rho)
print(pval)
print("\n")
rho,pval=stats.spearmanr(x2n.T,y2n.T,axis=1)
print(rho)
print(pval)
[[ 1. 0.04819682 0.22357036 -0.0589619 ]
[ 0.04819682 1. 0.02417042 -0.09821782]
[ 0.22357036 0.02417042 1. -0.05353735]
[-0.0589619 -0.09821782 -0.05353735 1. ]]
[[0. 0.63394219 0.02535319 0.56008632]
[0.63394219 0. 0.81133805 0.33095957]
[0.02535319 0.81133805 0. 0.59679005]
[0.56008632 0.33095957 0.59679005 0. ]]
[[ 1. 0.04819682 0.22357036 -0.0589619 ]
[ 0.04819682 1. 0.02417042 -0.09821782]
[ 0.22357036 0.02417042 1. -0.05353735]
[-0.0589619 -0.09821782 -0.05353735 1. ]]
[[0. 0.63394219 0.02535319 0.56008632]
[0.63394219 0. 0.81133805 0.33095957]
[0.02535319 0.81133805 0. 0.59679005]
[0.56008632 0.33095957 0.59679005 0. ]]
#Kendall's Tau:非參數等級相關係數
x=[1, 2, 3, 4, 5, 5, 4, 6,-1]
y=[3, 4, 3, 4, 5, 5, 3, 7, 7]
'''
tau=(P-Q)/sqrt((P+Q+T)*(P+Q+U))
P:同步數據對數,Q:異步,T:tie in x,U:tie in y
x中1-2增加,y中3-4增加,即爲同步P
x中2-3增加,y中4-3減少,即爲異步Q
x中兩個數相等,但y中不相等,即爲T
y中兩個數相等,但x中不相等,即爲U
'''
plt.scatter(x,y)
plt.title("x與y散點圖")
plt.xlabel("x")
plt.ylabel("y")
plt.show()
tau, p_value = stats.kendalltau(x,y)#Kendall's Tau:非參數等級相關係數
print(tau, p_value) #p>0.05,接受原假設,不相關
rho,pval=stats.spearmanr(x,y) #用spearmanr獨立性檢驗
print(rho,pval) #p>0.05,接受原假設,不相關
0.3444233600968322 0.22913623766848912
0.3247514077354534 0.3938410708377398
3.數值型~類別型(自變量類別型,因變量數值型)
- 服務員男女性別中顧客給小費是否不同?
t檢驗:比較兩個樣本(比較兩組均值)
stats.ttest_ind(rvs1,rvs2) # 對兩個樣本進行t檢驗
#結果分析:原假設兩樣本無差異,當p值越小時越拒絕
rvs1 = stats.norm.rvs(loc=5, scale=10, size=500)
rvs2 = stats.norm.rvs(loc=5, scale=10, size=500)
print(stats.ttest_ind(rvs1, rvs2))
#p值較大,接受原假設,兩個樣本的均值無明顯差異
Ttest_indResult(statistic=-0.7847691950148168, pvalue=0.4327751444523158)
rvs1=tips[tips['sex']=='Male']['tip']
print(rvs1.mean())
rvs2=tips[tips['sex']=='Female']['tip']
print(rvs2.mean())
print(stats.ttest_ind(rvs1, rvs2))
#P值較大,不能拒絕原假設:男女性別與小費的多少無差異
3.0896178343949052
2.833448275862069
Ttest_indResult(statistic=1.3878597054212687, pvalue=0.16645623503456763)
#如果同一個樣本重複抽樣,stats.ttest_rel()需要用這個檢驗,但必須保證兩組數據長度相等
print(stats.ttest_rel(rvs1[:87],rvs2))
#Ttest_relResult(statistic=1.3639526706337008, pvalue=0.1761417001637746)
#t檢驗比較的是兩組數的點估計和理想值得比較
Ttest_relResult(statistic=1.3639526706337008, pvalue=0.1761417001637746)
Kolmogorov-Smirnov 雙樣本檢測 ks_2samp(比較兩組分佈)
stats.ks_2samp(rvs1, rvs2) #判斷兩樣本分佈是否有差異
#檢查兩樣本分佈之間是否存在差異,原假設兩樣本之間不存在差異
rvs1=tips[tips['sex']=='Male']['tip']
rvs2=tips[tips['sex']=='Female']['tip']
print(stats.ks_2samp(rvs1, rvs2))
#p值較大,接受原假設,兩樣本的分佈不存在差異
#畫圖檢驗一下,加上total_bill作爲x軸,tip作爲y軸
plt.title("total_bill與tip關係圖")
plt.xlabel('total_bill')
plt.ylabel('tip')
plt.plot(tips[tips['sex']=='Male']['total_bill'],tips[tips['sex']=='Male']['tip'],'bo',label='Male')
plt.plot(tips[tips['sex']=='Female']['total_bill'],tips[tips['sex']=='Female']['tip'],'go',label='Female')
plt.legend(["Male","Female"],loc="best", frameon=True, title=" sex", ncol=1)
plt.show()
Ks_2sampResult(statistic=0.0797276520975182, pvalue=0.8545295823550555)
#進一步探索,費率,在國外餐廳費率即指小費/消費
rvs1=tips[tips['sex']=='Male']['tip']/tips[tips["sex"]=='Male']['total_bill']
rvs2=tips[tips['sex']=='Female']['tip']/tips[tips["sex"]=='Female']['total_bill']
fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(10, 4))
ax0.set_title('Male')
ax0.set_xlabel("tip/total_bill")
ax0.set_ylabel("數量")
ax0.hist(rvs1, bins=50, facecolor='g', alpha=0.8)
ax1.set_title('Female')
ax1.set_xlabel("tip/total_bill")
ax1.set_ylabel("數量")
ax1.hist(rvs2, bins=50, rwidth=0.8)
#fig.tight_layout()
plt.show()
#從兩個直方圖上看有些分佈相似,但需要進一步探索
#看一下均值和標準差
print('Male:',rvs1.mean(),rvs1.std())
print('Female:',rvs2.mean(),rvs2.std())
#t檢驗(均值是否有明顯不同,原假設是均值基本一樣無差異)
print(stats.ttest_ind(rvs1, rvs2))
#P值較大,不能拒絕原假設:男女性別對費率的影響無差異
#正態分佈檢驗
print(stats.normaltest(rvs1)) #結果拒絕
print(stats.normaltest(rvs2)) #結果拒絕
print(stats.ks_2samp(rvs1, rvs2)) #判斷兩樣的分佈是否存在差異
# p=0.08440693699139794,可以說是接受原假設,具有相同的分佈,但是正態分佈檢驗拒絕了,所以還可以做更多的探索
# 由於p值相對較小,也可以說拒絕原假設,不具有相同的分佈,p值的大小需要根據實際業務邏輯進行設定
# 最常見的p值設置是0.01,0.05,0.1三個閥值
# ANOVA方差分析、ANCOVA協方差分析、MANOVA多因素方差分析等方法做更多的探索
Male: 0.15765054700429748 0.06477787270951386
Female: 0.16649073632892475 0.0536317262299475
Ttest_indResult(statistic=-1.0833972270477996, pvalue=0.2797103849605489)
NormaltestResult(statistic=174.74433683510114, pvalue=1.1343560308586256e-38)
NormaltestResult(statistic=36.21379581439844, pvalue=1.368592496283602e-08)
Ks_2sampResult(statistic=0.1651658247309466, pvalue=0.08440693699139794)
單因素ANOVA方差分析
原假設H0:所有組的均數相同
方差分析檢驗對數據的假設條件:
1. 樣本之間相互獨立
2. 樣本均來自正態分佈
3. 方差齊次性:各組方差相等
對誤差平方和,因素平方和進行F檢驗。如果F檢驗爲1左右,認爲不能拒絕原假設
print(stats.f_oneway(rvs1,rvs2))
#接着上面構造的數據
rvs1=tips[tips['sex']=='Male']['tip']/tips[tips["sex"]=='Male']['total_bill']
rvs2=tips[tips['sex']=='Female']['tip']/tips[tips["sex"]=='Female']['total_bill']
print(stats.f_oneway(rvs1,rvs2)) #p=0.279710384960558,認爲不能拒絕原假設,rvs1與rvs2均值相同
'''
注意:
原假設的三個條件,其中方差齊次性:各組方差相等,但也不是說需要完全相等。
如何檢驗方差齊次性假設?
方差齊次性:stats.fligner(rvs1,rvs2) # 原假設方差相等
'''
x=[1,2,3,4,5]
y=[1,2,3,4,5]
print(stats.fligner(x,y))
print('兩組數的方差:',rvs1.var(),rvs2.var())
print(stats.fligner(rvs1,rvs2)) #在方差檢驗過程中我們也接受方差是齊次的,雖然我們計算他們的方差明顯是不相等的,但相差不大
F_onewayResult(statistic=1.1737495515748593, pvalue=0.279710384960558)
FlignerResult(statistic=6.590905151254103e-31, pvalue=0.9999999999999993)
兩組數的方差: 0.0041961727927699805 0.0028763620584040393
FlignerResult(statistic=0.9521374099494737, pvalue=0.3291758341219779)
ANOVA的非參數版本:Kruskal-Wallis H-test
H0: 各組中值近似相等
對數據亦有假設條件:Chi2分佈,因此樣本容量需不小於5
stats.kruskal(rvs1,rvs2)
相比於單因素ANOVA方差分析更加寬鬆
rvs1=tips[tips['sex']=='Male']['tip']/tips[tips["sex"]=='Male']['total_bill']
rvs2=tips[tips['sex']=='Female']['tip']/tips[tips["sex"]=='Female']['total_bill']
print(stats.kruskal(rvs1,rvs2)) #p值較大,接受原假設,即中位數基本相同,兩組變量無明顯差異,且相互獨立
KruskalResult(statistic=2.2351202029645436, pvalue=0.13490613264268328)
#練習:星期幾對小費是否有影響
display(pd.crosstab(tips.tip,tips.day).head()) #生成交叉表
Thur=tips[tips['day']=='Thur']['tip']
Fri=tips[tips['day']=='Fri']['tip']
Sat=tips[tips['day']=='Sat']['tip']
Sun=tips[tips['day']=='Sun']['tip']
print(stats.f_oneway(Thur,Fri,Sat,Sun)) #單因素ANOVA方差分析
#F_onewayResult(statistic=1.6723551980998699, pvalue=0.1735885553040592)
'''
方差分析檢驗對數據得假設:
1.樣本之間相互獨立
2.樣本均來自正態分佈
3.方差齊次性:各組方差相等
'''
print(stats.fligner(rvs1,rvs2)) #方差齊次性檢驗
#FlignerResult(statistic=1.6183029033640117, pvalue=0.20332858592898514)
#P值較大,認爲方差齊次
#如果不滿足以上任意一條
'''
ANOVA的非參數版本
Kruskal-Wallis H-test
H0:各組中值相等
對數據也有假設:Chi2卡方分佈,因此樣本容量需不小於5
給出得結果寬鬆一些,沒有ANOVA強
'''
count=pd.crosstab(tips.tip,tips.day).head() #構造交叉表
print(stats.chi2_contingency(count,correction=False)) #卡方檢驗,p值較大,各類別變量間相互獨立
print(stats.kruskal(rvs2,rvs1)) #ANOVA的非參數版本檢驗
#KruskalResult(statistic=0.7615717066668545, pvalue=0.38283710822789807)
day | Fri | Sat | Sun | Thur |
---|---|---|---|---|
tip | ||||
1.00 | 1 | 3 | 0 | 0 |
1.01 | 0 | 0 | 1 | 0 |
1.10 | 0 | 1 | 0 | 0 |
1.17 | 0 | 1 | 0 | 0 |
1.25 | 0 | 2 | 0 | 1 |
F_onewayResult(statistic=1.6723551980998699, pvalue=0.1735885553040592)
FlignerResult(statistic=0.9521374099494737, pvalue=0.3291758341219779)
(13.809523809523808, 0.3130368414958159, 12, array([[0.4, 2.8, 0.4, 0.4],
[0.1, 0.7, 0.1, 0.1],
[0.1, 0.7, 0.1, 0.1],
[0.1, 0.7, 0.1, 0.1],
[0.3, 2.1, 0.3, 0.3]]))
KruskalResult(statistic=2.2351202029645436, pvalue=0.13490613264268328)