探索变量之间的关系(python3)
在做数据分析的过程中。我们往往是–理解需求–获取数据–清洗数据–简单描述统计–统计型分析报告;
其实到这里还没完。如果我们还需要深入探索数据的价值,那么,单变量的分布检验–探索变量间的关系–建立关系模型–评估–总结等。
接下来就来看看数据分析的其中重要的一环–探索变量间的关系。
探索变量之间的关系
探索数据变量之间是否存在某种关系/关联。大致步骤有:
- 变量的类型:类别型/数值型
- 可视化给出可能的方向:散点图、箱型图、直方图、…
- 需建立更严格的分析方式:假设检验。假设变量间存在某种函数/逻辑等关联关系,进行检验。
一、准备工具和数据
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)
上一篇: 详解使用Spring Boot开发Restful程序
下一篇: 杨辉三角形