本文内容根据Datawhale和GitModel开源学习教程整理,学习链接:https://github.com/Git-Model/Modeling-Universe/tree/main/Data-Story
感谢课程开发者的付出与贡献!
本节作业链接:戳我进入
以下内容总结常见的不基于统计模型的假设检验。
问题描述:
必胜中学初三年级学生的数学成绩 X X X 服从正态分布 X ∼ N ( μ , σ 2 ) X \sim N\left(\mu, \sigma^{2}\right) X∼N(μ,σ2) ,抽取一班学生成绩 做样本,已知样本均值 x ˉ = 108.2 \bar{x}=108.2 xˉ=108.2 ,样本标准差 s = 4 , s=4 , s=4, 一班人数 n = 25 n=25 n=25 ,能否认为总体均值 μ > 110 \mu>110 μ>110 ?
“总体均值 μ > 110 \mu>110 μ>110 ”是否正确涉及如下两个假设问题:
H 0 : μ ⩽ 110 ↔ H 1 : μ > 110 H_{0}: \mu \leqslant 110 \leftrightarrow H_{1}: \mu>110 H0:μ⩽110↔H1:μ>110
H 0 H_{0} H0被称为原假设, H 1 H_{1} H1被称为备择假设,两个假设必须为互斥关系,因为只有这样,拒绝假设 H 0 H_{0} H0 才等价于接受假设 H 1 H_{1} H1 ,命题成立的探讨就转化为了拒绝原假设 H 0 H_{0} H0 的探讨
最常见的三种单参数检验问题,以均值检验为例:
H 0 : μ ⩽ μ 0 ↔ H 1 : μ > μ 0 H_{0}: \mu \leqslant \mu_{0} \leftrightarrow H_{1}: \mu>\mu_{0} H0:μ⩽μ0↔H1:μ>μ0 单边检验
H 0 : μ ⩾ μ 0 ↔ H 1 : μ < μ 0 H_{0}: \mu \geqslant \mu_{0} \leftrightarrow H_{1}: \mu<\mu_{0} H0:μ⩾μ0↔H1:μ<μ0 单边检验
H 0 : μ = μ 0 ↔ H 1 : μ ≠ μ 0 H_{0}: \mu=\mu_{0} \leftrightarrow H_{1}: \mu \neq \mu_{0} H0:μ=μ0↔H1:μ=μ0 双边检验
以上问题的原假设有另一种更常见的表达方式
H 0 : μ = μ 0 ↔ H 1 : μ > μ 0 H_{0}: \mu=\mu_{0} \leftrightarrow H_{1}: \mu>\mu_{0} H0:μ=μ0↔H1:μ>μ0
H 0 : μ = μ 0 ↔ H 1 : μ < μ 0 H_{0}: \mu=\mu_{0} \leftrightarrow H_{1}: \mu<\mu_{0} H0:μ=μ0↔H1:μ<μ0
H 0 : μ = μ 0 ↔ H 1 : μ ≠ μ 0 H_{0}: \mu=\mu_{0} \leftrightarrow H_{1}: \mu \neq \mu_{0} H0:μ=μ0↔H1:μ=μ0
这种写法与之前的写法并不互斥,但从结果上与前者等效
当检验某样本所在总体是否服从某个特定分布时(如正态性检验),通常会设置如下假设:
H 0 H_{0} H0 : 样本所在总体服从某分布 ↔ H 1 \leftrightarrow H_{1} ↔H1 :样本所在总体不服从某分布
在单参数检验中,等号只会出现在 H 0 H_0 H0中,不会出现在 H 1 H_1 H1中,即我们不会做这样的假设
H 0 : μ ≠ 110 ↔ H 1 : μ = 110 H_{0}: \mu \neq 110 \leftrightarrow H_{1}: \mu=110 H0:μ=110↔H1:μ=110
上述例子中的 x ˉ − 110 > C \bar{x}-110>C xˉ−110>C 也叫拒绝域,它可以被写成:
{ x ˉ : x ˉ > 110 + C } \{\bar{x}: \bar{x}>110+C\} {xˉ:xˉ>110+C}
拒绝域还可以直接用检验统计量与对应的分布分位点表示,这样更省时间,不用再计算临界值 C C C
t > t n − 1 ( 1 − α ) t>t_{n-1}(1-\alpha) t>tn−1(1−α)
由于抽样具有随机性,凭借样本信息去判断总体的性质总是会有犯错的概率——即不论我们是否拒绝原假设 H 0 H_0 H0,都有概率犯以下两类错误中的一种:
由于两种犯错概率互相对立,因此采取折中方案——通常只限制犯第一类错误的概率 α \alpha α
再决定临界值时,要保证发生第一类错误的概率 α \alpha α在一个给定的、较小的水平(通常取 α = 0.05 / 0.1 \alpha=0.05 / 0.1 α=0.05/0.1),此时 α \alpha α被称为显著性水平
确定临界值 C C C的标准是:原假设 H 0 H_0 H0为真,但数据却落入拒绝域的概率恰好为给定的 α \alpha α,在上述例子中该概率可写为
P H 0 is true ( x ˉ − μ 0 > C ) = P ( x ˉ − 110 > C ) = α P_{H_{0} \text { is true }}\left(\bar{x}-\mu_{0}>C\right)=P(\bar{x}-110>C)=\alpha PH0 is true (xˉ−μ0>C)=P(xˉ−110>C)=α
将 P ( x ˉ − 110 > C ) P(\bar{x}-110>C) P(xˉ−110>C)构造成某个分布的分位数形式,这样可以计算出临界值 C C C,概率实际为:
P ( x ˉ − E ( x ˉ ) > C ) , E ( x ˉ ) = μ 0 = 110 P(\bar{x}-E(\bar{x})>C), E(\bar{x})=\mu_{0}=110 P(xˉ−E(xˉ)>C),E(xˉ)=μ0=110
由于 x ˉ \bar{x} xˉ服从正态分布,可以构造t统计量:
P ( x ˉ − E ( x ˉ ) > C ) = P ( x ˉ − E ( x ˉ ) s > C s ) = α , x ˉ − E ( x ˉ ) s ∼ t n − 1 P(\bar{x}-E(\bar{x})>C)=P\left(\frac{\bar{x}-E(\bar{x})}{s}>\frac{C}{s}\right)=\alpha, \frac{\bar{x}-E(\bar{x})}{s} \sim t_{n-1} P(xˉ−E(xˉ)>C)=P(sxˉ−E(xˉ)>sC)=α,sxˉ−E(xˉ)∼tn−1
代入公式有
P ( x ˉ − E ( x ˉ ) s > C s ) = P ( x ˉ − μ 0 s > s ⋅ t n − 1 ( 1 − α ) s ) P\left(\frac{\bar{x}-E(\bar{x})}{s}>\frac{C}{s}\right)=P\left(\frac{\bar{x}-\mu_{0}}{s}>\frac{s \cdot t_{n-1}(1-\alpha)}{s}\right) P(sxˉ−E(xˉ)>sC)=P(sxˉ−μ0>ss⋅tn−1(1−α))
= P ( x ˉ > μ 0 + s ⋅ t n − 1 ( 1 − α ) ) = α =P\left(\bar{x}>\mu_{0}+s \cdot t_{n-1}(1-\alpha)\right)=\alpha =P(xˉ>μ0+s⋅tn−1(1−α))=α
得出:只需 x ˉ > 110 + s ⋅ t n − 1 ( 1 − α ) \bar{x}>110+s \cdot t_{n-1}(1-\alpha) xˉ>110+s⋅tn−1(1−α) ,就可以在显著性水平 α \alpha α 下拒绝原假设
假设检验思路:构造检验统计量——输出对应分布的分位点——计算临界值(拒绝域)——做出判断
## 加载包
import numpy as np
import pandas as pd
from scipy.stats import t
n=25
x_bar=108.2
s=4
mu=110
# 计算检验统计量
tvalue=(x_bar-mu)
print('t值为:{}'.format(tvalue))
# 输出分位点
'''
ppf:单侧左分位点
isf:单侧右分位点
interval:双侧分位点
'''
#由于备择假设是大于号,因此应当选用单侧右分位点,0.05为显著性水平a,n-1为自由度
T_isf=t.isf(0.05,n-1)
# 如果备择假设是小于号,则应选用单侧左分位点ppf,里面的参数设置不变,依次为显著性水平a与分布自由度
print('分位点为:{}'.format(T_isf))
# 拒绝域
Deny_domain=110+s*T_isf
print('拒绝域的临界点为:{}'.format(Deny_domain))
# 判断
print('样本均值是否位于拒绝域:{}'.format(x_bar>Deny_domain))
print('因此,不能拒绝原假设,不能认为总体均值大于110.')
t值为:-1.7999999999999972
分位点为:1.7108820799094282
拒绝域的临界点为:116.84352831963771
样本均值是否位于拒绝域:False
因此,不能拒绝原假设,不能认为总体均值大于110.
# 直接用检验统计量与分布分位点判断
print('检验统计量是否位于拒绝域:{}'.format(tvalue>T_isf))
检验统计量是否位于拒绝域:False
# 进行双边检验
## 计算双侧分位点
T_int=t.interval(1-0.05,n-1) # 对于双侧检验(双侧分位点),分位点参数应该输入1-a,这里是1-0.05=0.95
print('检验统计量t的绝对值:{}'.format(np.abs(tvalue)))
print('双侧分位点:{}'.format(T_int))
print('显然,检验统计量不在拒绝域内,因此无法拒绝原假设')
检验统计量t的绝对值:1.7999999999999972
双侧分位点:(-2.0638985616280205, 2.0638985616280205)
显然,检验统计量不在拒绝域内,因此无法拒绝原假设
采用拒绝域的方法进行假设检验有一个缺点:分位点值与显著性水平 α \alpha α是相关的。如果要在不同的显著水平下检验,就需要计算不同的分位点再比较,因此需要利用p值这个指标来判断是否拒绝原假设
p值是在确定的样本观测值下,给出的能拒绝原假设的最小显著性水平,只与样本观测值和我们做的假设检验有关。p值越小越可以拒绝原假设
p \mathrm{p} p 值的形式与我们做的备择假设 H 1 H_{1} H1 有关
若 H 1 H_{1} H1 的符号为 ≠ \neq = ,则: p v a l u e pvalue pvalue = P ( ∣ X ∣ > ∣ =P(|X|>\mid =P(∣X∣>∣ Test statistics ∣ ) \mid) ∣)
若 H 1 H_{1} H1 的符号为 > > >,则: p v a l u e pvalue pvalue = P ( X > =P(X> =P(X> Test statistics ) ) )
若 H 1 H_{1} H1 的符号为 < < < ,则: p u a l u e pualue pualue = P ( X < =P(X< =P(X< Test statistics ) ) )
# 利用example.1的数据进行三种假设检验
# 利用p值进行假设检验
'''
sf:右尾累积概率
cdf:左尾累积概率
'''
# 若备择假设为mu>110
pvalue=t.sf(tvalue,n-1)
print('备择假设为mu>110的p值为:{}'.format(pvalue))
# 若备择假设为mu<110
pvalue=t.cdf(tvalue,n-1)
print('备择假设为mu<110的p值为:{}'.format(pvalue))
# 若备择假设为mu不等于110
pvalue=t.cdf(tvalue,n-1)*2 # 之所以是左尾累积概率的两倍,是因为右尾累积概率大于0.5,而p值不可能大于1。
print('备择假设为mu不等于110的p值为:{}'.format(pvalue))
备择假设为mu>110的p值为:0.9577775745385242
备择假设为mu<110的p值为:0.042222425461475775
备择假设为mu不等于110的p值为:0.08444485092295155
# 生成1000个服从正态分布的数据
data_norm = stats.norm.rvs(loc=10, scale=10, size=1000) # rvs(loc,scale,size):生成服从指定分布的随机数,loc:期望;scale:标准差;size:数据个数
# 生成1000个服从卡方分布的数据
data_chi=stats.chi2.rvs(2,3,size=1000)
# 画出两个概率图
fig=plt.figure(figsize=(12,6))
ax1=fig.add_subplot(1,2,1)
plot1=stats.probplot(data_norm,plot=ax1) # 正态数据
ax2=fig.add_subplot(1,2,2)
plot2=stats.probplot(data_chi,plot=ax2) # 卡方分布数据
名称 | 适用问题 | 原因 | 样本量适用范围 |
---|---|---|---|
Shapiro-Wilk检验 | 小样本正态性检验 | 该检验在每一个样本值都是唯一时的检验效果最好 | 样本量最低为8,<50最佳,<2000效果较好,>5000不再适用 |
D’Agostino’s K-squared test | 大样本正态性检验 | 分布曲线的偏度和峰度容易受到数据量的影响,数据量越大,偏度与峰度的计算就越准确 | 样本量最低为4,除此以外越大越好 |
data_small = stats.norm.rvs(0, 1, size=30) # 小样本正态性数据集
data_large = stats.norm.rvs(0, 1, size=6000) # 大样本正态性数据集
# 定义一个正态性检验函数,它可以输出:
## 正态概率图
## 小样本Shapiro-Wilk检验的p值
## 大样本D'Agostino's K-squared检验的p值
from statsmodels.stats.diagnostic import lilliefors
from typing import List
def check_normality(data: np.ndarray, show_flag: bool=True) -> List[float]:
"""
输入参数
----------
data : numpy数组或者pandas.Series
show_flag : 是否显示概率图
Returns
-------
两种检验的p值;概率图
"""
if show_flag:
_ = stats.probplot(data, plot=plt)
plt.show()
pVals = pd.Series(dtype='float64')
# D'Agostino's K-squared test
_, pVals['Omnibus'] = stats.normaltest(data)
# Shapiro-Wilk test
_, pVals['Shapiro-Wilk'] = stats.shapiro(data)
print(f'数据量为{len(data)}的数据集正态性假设检验的结果 : ----------------')
print(pVals)
check_normality(data_small,show_flag=True)
check_normality(data_large,show_flag=False) # 当样本量大于5000,会出现警告
若样本数据非正态,应使用wilcoxon符号秩和检验。该检验是非参数检验
什么是“秩”。设 x 1 , ⋯ , x n x_1,\cdots ,x_n x1,⋯,xn为来自连续分布的简单随机样本,将它们从小到大排序,得到有序样本 x ( 1 ) ⩽ ⋯ ⩽ x ( n ) x_{\left( 1 \right)}\leqslant \cdots \leqslant x_{\left( n \right)} x(1)⩽⋯⩽x(n)。观测值 x i x_i xi在有序样本中的序号 r r r被称为 x i x_i xi的秩。秩其实就是样本值 x i x_i xi在所有样本中“第几小”的意思
秩和检验,要有“秩的和”
设 x 1 , ⋯ , x n x_1,\cdots ,x_n x1,⋯,xn为样本,对它们做绝对值变换,记 R i R_i Ri为 ∣ x i ∣ |x_i| ∣xi∣在 ( ∣ x 1 ∣ , ⋯ , ∣ x n ∣ ) \left( |x_1|,\cdots ,|x_n| \right) (∣x1∣,⋯,∣xn∣)的秩
记 I ( x i > 0 ) = { 1 , x i > 0 0 , x i ≠ 0 I\left(x_{i}>0\right)=\left\{\begin{array}{ll} 1, & x_{i}>0 \\ 0, & x_{i} \neq 0 \end{array}\right. I(xi>0)={1,0,xi>0xi=0
则称 W + = ∑ i = 1 n R i I ( x i > 0 ) W^{+}=\sum_{i=1}^{n} R_{i} I\left(x_{i}>0\right) W+=i=1∑nRiI(xi>0) 为秩和统计量
单样本均值比较的wilcoxon符号秩和检验的两个假设依旧为 H 0 : μ = μ 0 ↔ H 1 : μ ≠ μ 0 H_0:\mu =\mu_0\leftrightarrow H_1:\mu \ne \mu_0\, H0:μ=μ0↔H1:μ=μ0
对于待分析样本 x 1 , ⋯ , x n x_1,\cdots ,x_n x1,⋯,xn,让所有样本减去对比值 μ 0 \mu_0 μ0,得: x 1 − μ 0 , ⋯ , x n − μ 0 x_1-\mu_0,\cdots ,x_n-\mu_0 x1−μ0,⋯,xn−μ0,计算出它们的秩和统计量 W + W^{+} W+
然后,检验统计量便可以计算为 T e s t s t a t i s t i c s = W + − n ( n + 1 ) 4 n ( n + 1 ) ( 2 n + 1 ) 24 Test\,\,statistics=\frac{W^{+}-\frac{n(n+1)}{4}}{\sqrt{\frac{n(n+1)(2 n+1)}{24}}} Teststatistics=24n(n+1)(2n+1)W+−4n(n+1) 检验统计量近似服从分布 T e s t s t a t i s t i c s → N ( 0 , 1 ) Test\,\,statistics\rightarrow N\left( 0,1 \right) Teststatistics→N(0,1) 其中, n n n为样本量
最后,p值的计算与备择假设 H 1 H_1 H1的符号有关,这一点与t检验相同(Python中也有相应的api)
该方法最好在样本量>25的情况下使用,这样检验统计量才近似服从正态分布
data=np.array([136,136,134,136,131,133,142,145,137,140])
# 定义一个单组样本均值检验函数,使它可以同时输出t检验与wilcoxon符号秩和检验的p值
def check_mean(data,checkvalue,confidence=0.05,alternative='two-sided'):
'''
输入参数
----------
data : numpy数组或者pandas.Series
checkvalue : 想要比较的均值
confidence : 显著性水平
alternative : 检验类型,这取决于我们备择假设的符号:two-sided为双侧检验、greater为右侧检验、less为左侧检验
输出
-------
在两种检验下的p值
在显著性水平下是否拒绝原假设
'''
pVal=pd.Series(dtype='float64')
# 正态性数据检验-t检验
_, pVal['t-test'] = stats.ttest_1samp(data, checkvalue,alternative=alternative)
print('t-test------------------------')
if pVal['t-test'] < confidence:
print(('目标值{0:4.2f}在显著性水平{1:}下不等于样本均值(p={2:5.3f}).'.format(checkvalue,confidence,pVal['t-test'])))
else:
print(('目标值{0:4.2f}在显著性水平{1:}下无法拒绝等于样本均值的假设.(p={2:5.3f})'.format(checkvalue,confidence,pVal['t-test'])))
# 非正态性数据检验-wilcoxon检验
_, pVal['wilcoxon'] = stats.wilcoxon(data-checkvalue,alternative=alternative)
print('wilcoxon------------------------')
if pVal['wilcoxon'] < confidence:
print(('目标值{0:4.2f}在显著性水平{1:}下不等于样本均值(p={2:5.3f}).'.format(checkvalue,confidence,pVal['wilcoxon'])))
else:
print(('目标值{0:4.2f}在显著性水平{1:}下无法拒绝等于样本均值的假设.(p={2:5.3f})'.format(checkvalue,confidence,pVal['wilcoxon'])))
return pVal
check_mean(data,137,0.05)
若两个样本的总体都服从正态分布,那么可以使用双样本t检验
双样本t检验有两个分类
两种检验的假设问题是相同的: H 0 : μ x = μ y ↔ H 1 : μ x ≠ μ y H_0:\mu _x=\mu _y\leftrightarrow H_1:\mu _x\ne \mu _y\, H0:μx=μy↔H1:μx=μy
若两个样本的总体方差相等,则检验统计量为 T e s t s t a t i s t i c s = x ˉ − y ˉ s w 1 m + 1 n , s w = 1 m + n − 2 [ ∑ i = 1 m ( x i − x ˉ ) 2 + ∑ i = 1 n ( y i − y ˉ ) 2 ] Test\,\,statistics=\frac{\bar{x}-\bar{y}}{s_w\sqrt{\frac{1}{m}+\frac{1}{n}}}\,\,, s_w=\sqrt{\frac{1}{m+n-2}\left[ \sum_{i=1}^m{\left( x_i-\bar{x} \right) ^2+\sum_{i=1}^n{\left( y_i-\bar{y} \right) ^2}} \right]} Teststatistics=swm1+n1xˉ−yˉ,sw=m+n−21[i=1∑m(xi−xˉ)2+i=1∑n(yi−yˉ)2]
若两个样本的总体方差不相等,则检验统计量为 T e s t s t a t i s t i c s = x ˉ − y ˉ s x 2 m + s y 2 n Test\,\,statistics=\frac{\bar{x}-\bar{y}}{\sqrt{\frac{s_{x}^{2}}{m}+\frac{s_{y}^{2}}{n}}}\, Teststatistics=msx2+nsy2xˉ−yˉ
在大多数情况下,总体的信息是很难知晓的,即我们很难知道两个总体的标准差是否相等,这时我们可以用方差齐性检验levene检验进行判断。
# 定义一个单组样本均值检验函数,使它可以同时输出t检验与mannwhitneyu检验的p值
def unpaired_data(group1:np.ndarray,group2:np.ndarray,confidence=0.05,alternative='two-sided'):
"""
输入参数
----------
group1/2 : 用于比较的两组数据
confidence : 显著性水平
alternative : 检验类型,这取决于我们备择假设的符号:two-sided为双侧检验、greater为右侧检验、less为左侧检验
输出
-------
在两种检验下的p值
在显著性水平下是否拒绝原假设
"""
pVal=pd.Series(dtype='float64')
# 先进行两组数据的方差齐性检验
_,pVal['levene']=stats.levene(group1,group2)
# t检验-若数据服从正态分布
if pVal['levene']<confidence:
print('在显著性水平{0:}下,两组样本的方差不相等(p={1:.4f}),因此需要使用方差不等的t检验'.format(confidence,pVal['levene']))
print('------------------------------------')
_, pVal['t-test'] = stats.ttest_ind(group1, group2,equal_var=False,alternative=alternative) # 因为方差不相等,因此是False
print('t检验p值:{}'.format(pVal['t']))
else:
print('在显著性水平{0:}下,不能拒绝两组样本方差相等的假设(p={1:.4f}),因此需要使用方差相等的t检验'.format(confidence,pVal['levene']))
print('------------------------------------')
_, pVal['t-test'] = stats.ttest_ind(group1, group2,equal_var=True,alternative=alternative) # 因为方差相等,因此是True
print('t检验p值:{:.3f}'.format(pVal['t-test']))
# mannwhitneyu检验-数据不服从正态检验
_, pVal['mannwhitneyu'] = stats.mannwhitneyu(group1, group2,alternative=alternative)
print('Mann-Whitney检验p值:{:.3f}'.format(pVal['mannwhitneyu']))
# --- >>> STOP stats <<< ---
# 两组样本均值的散点图可视化
print('------------------------------------')
print('两组样本均值的散点图可视化')
plt.plot(group1, 'bx', label='group1')
plt.plot(group2, 'ro', label='group2')
plt.legend(loc=0)
plt.show()
return pVal
# A班级
group1=data
# B班级
group2=np.array([134,136,135,145,147,140,142,137,139,140,141,135])
unpaired_data(group1,group2)
def paired_data(group1:np.ndarray,group2:np.ndarray,confidence,alternative='two-sided'):
"""
输入参数
----------
group1/2 : 用于比较的两组数据,注意,两组数据的样本顺序必须相同
confidence : 显著性水平
alternative : 检验类型,这取决于我们备择假设的符号:two-sided为双侧检验、greater为右侧检验、less为左侧检验
输出
-------
在两种检验下的p值
在显著性水平下是否拒绝原假设
"""
pVal=pd.Series(dtype='float64')
# 配对t检验-样本服从正态分布
_, pVal['t-test'] = stats.ttest_1samp(post - pre, 0,alternative=alternative)
print('t-test------------------------')
if pVal['t-test'] < confidence:
print(('在显著性水平{0:}下,两组配对样本的均值不相等(p={1:5.3f}).'.format(confidence,pVal['t-test'])))
else:
print(('在显著性水平{0:}下无法拒绝等于样本均值的假设.(p={1:5.3f})'.format(confidence,pVal['t-test'])))
# wilcoxon秩和检验
_, pVal['wilcoxon'] = stats.wilcoxon(group1,group2, mode='approx',alternative=alternative)
print('wilcoxon------------------------')
if pVal['wilcoxon'] < confidence:
print(('在显著性水平{0:}下,两组配对样本的均值不相等(p={1:5.3f}).'.format(confidence,pVal['wilcoxon'])))
else:
print(('在显著性水平{0:}下无法拒绝等于样本均值的假设.(p={1:5.3f})'.format(confidence,pVal['wilcoxon'])))
return pVal
# 第一次测验
pre=data
# 第二次测验
post=np.array([139,141,137,136,135,132,141,148,145,139])
paired_data(pre,post,0.05)
问题举例:
商标、销售方式、居民区这些区分不同组别的依据就是因素(也叫因子),通常用大写字母 A , B , C A,B,C A,B,C等表示这些因素,而一个因素所处的不同的状态被称为水平,用 A 1 , A 2 A_1,A_2 A1,A2等表示
这三个例子中,因素都只有一个,它们都是单因素方差分析;若因素有多个,则被称为多因素方差分析
方差分析的多个总体均值该如何比较?并非两两相互比较,而是同时比较,写出假设检验的形式有:
H 0 : μ 1 = μ 2 = μ i = ⋯ μ k ↔ H 1 : μ i 不全相等 H_0:\mu _1=\mu _2=\mu _i=\cdots \mu _k\leftrightarrow H_1: \mu _i\text{不全相等} H0:μ1=μ2=μi=⋯μk↔H1:μi不全相等 其中, k k k为样本个数(因子水平个数)
既然我们检验的是不同总体的均值是否相等,那么很自然便想观测各样本的样本均值的“差异程度”,如果各样本的均值差异很大,那么它们的总体均值也有很大可能存在差异
**样本间均值的“差异程度”**是一个很好的评判指标,但这并不足够,还有一个指标也十分重要:各样本的样本内差异程度
在相同的样本间差异程度下,样本内差异程度越大,各总体间均值存在差异的可能性就越小,为什么呢?简单来说,就是样本内差异程度越大,“偶然性”越大,我们越难判断两个不相等的均值是否真的不相等
举例来说:
小红均分是91,小刚均分是89,考试成绩如下表所示:
次数 | 小红分数 | 小刚分数 | 样本内部标准差 | 样本内部方差 | 两者均值差 |
---|---|---|---|---|---|
A | 91 | 89 | 0 | 0 | 2 |
B1 | 79 | 93 | 6 | 36 | 2 |
B2 | 94 | 70 | 6 | 36 | 2 |
原理解析
记 y i j y_{ij} yij为第 i i i个样本的第 j j j个样品,假设有r个样本,每个样本的样品数量都同为 m m m,于是我们有 r × m r\times m r×m个样品: y i j , i = 1 , 2 , ⋯ , r , j = 1 , 2 , ⋯ , m , y_{i j}, \quad i=1,2, \cdots, r, \quad j=1,2, \cdots, m, yij,i=1,2,⋯,r,j=1,2,⋯,m,
既然要研究“差异程度”,那么观察每个样本值 y i j y_{ij} yij与平均值的偏差一定是必不可少的
数据 y i j y_{ij} yij与所有数据的总平均 y ˉ \bar{y} yˉ的偏差可用 y i j − y ˉ y_{ij}-\bar{y} yij−yˉ表示,这个总偏差可以解构为两个偏差之和: y i j − y ˉ = ( y i j − y ˉ i ⋅ ) + ( y ˉ i ⋅ − y ˉ ) y_{ij}-\bar{y}=\left( y_{ij}-\bar{y}_{i·} \right) +\left( \bar{y}_{i·}-\bar{y} \right) yij−yˉ=(yij−yˉi⋅)+(yˉi⋅−yˉ)
完成对一个样本值与平均值偏差的解构后,接下来将所有的偏差汇总起来,计算总的“差异程度”
各 y i j y_{ij} yij间总的差异大小可以用总偏差平方和 S T S_T ST表示 S T = ∑ i = 1 r ∑ j = 1 m ( y i j − y ˉ ) 2 , f T = n − 1 S_{T}=\sum_{i=1}^{r} \sum_{j=1}^{m}\left(y_{i j}-\bar{y}\right)^{2}, \quad f_{T}=n-1 ST=i=1∑rj=1∑m(yij−yˉ)2,fT=n−1
仅由随机误差引起的数据间差异可用组内偏差平方和 S e S_e Se表示
S e = ∑ i = 1 r ∑ j = 1 m ( y i j − y ˉ i . ) 2 , f e = r ( m − 1 ) = n − r . S_{e}=\sum_{i=1}^{r} \sum_{j=1}^{m}\left(y_{i j}-\bar{y}_{i} .\right)^{2}, \quad f_{e}=r(m-1)=n-r . Se=i=1∑rj=1∑m(yij−yˉi.)2,fe=r(m−1)=n−r.
由于效应不同引起的数据差异可用组间偏差平方和 S A S_A SA表示
S A = m ∑ i = 1 ( y ˉ i . − y ˉ ) 2 , f A = r − 1 S_{A}=m \sum_{i=1}\left(\bar{y}_{i} .-\bar{y}\right)^{2}, \quad f_{A}=r-1 SA=mi=1∑(yˉi.−yˉ)2,fA=r−1 其中, f f f为它们各自的自由度
以上三种偏差平方和的大小和数据的个数(或者自由度)有关,通常而言,数据越多,偏差平方和越大,这不利于进行偏差平方和之间的比较。因此需要统一它们的“量纲”
定义: M S A = S A f A , M S e = S e f e M S_{A}=\frac{S_{A}}{f_{A}}, \quad M S_{e}=\frac{S_{e}}{f_{e}} MSA=fASA,MSe=feSe
然后将两者相除,就可以判断多组样本的均值是否相等
data = np.genfromtxt('./data/altman_910.txt', delimiter=',')
group1 = data[data[:,1]==1,0]
group2 = data[data[:,1]==2,0]
group3 = data[data[:,1]==3,0]
group1
from typing import Tuple
def anova_oneway() -> Tuple[float, float]:
pVal=pd.Series(dtype='float64')
# 先做方差齐性检验
_,pVal['levene'] = stats.levene(group1, group2, group3)
if pVal['levene']<0.05: #这里假设显著性水平为0.05
print('警告: 方差齐性检验的p值小于0.05: p={},方差分析结果在小样本下可能不准确'.format(pVal['levene']))
print('-------------------------------')
# 单因素方差分析-假设样本服从正态分布
_, pVal['anova_oneway_normal'] = stats.f_oneway(group1, group2, group3) # 在这里输入待分析的数据
print('若样本服从正态分布,单因素方差分析的p值为{}'.format(pVal['anova_oneway_normal']))
if pVal['anova_oneway_normal'] < 0.05:
print('检验在0.05的显著性水平下显著,多组样本中至少存在一组样本均值与其它样本的均值不相等。')
print('---------------------------------')
# 单因素方差分析-假设样本不服从正态分布
_, pVal['anova_oneway_notnormal'] = stats.mstats.kruskalwallis(group1, group2, group3) # 在这里输入待分析的数据
print('若样本不服从正态分布,单因素方差分析的p值为{}'.format(pVal['anova_oneway_notnormal']))
if pVal['anova_oneway_notnormal'] < 0.05:
print('检验在0.05的显著性水平下显著,多组样本中至少存在一组样本均值与其它样本的均值不相等。')
return pVal
anova_oneway()
现在有3个婴儿的头围需要测量,我们想知道他们的头围有无显著的区别。注意,如果我们只探究这个问题,则此时就是单因素方差分析,探究的是(因素:婴儿fetus)对头围的是否有显著性影响
人工测量总是有误差的。为了保证客观性,我们请了四位观察者observer,让他们对每个婴儿的头围重复测量重复测量三次,并记录每一次测量的结果,此时,我们总共会得到36个样品。我们将(因素:observer)也纳入考量,同时考量两个因素以及他们的交互项的显著性
如果观察者因素没有显著差别,就说明观察者观察的结果很一致;反之,他们观察的差距非常大,可能要另外寻找其他的观察者
代码示例
# statsmodel包分析的对象更多的是dataframe,前面的分析对象多为数组,这一点需要注意。
data = np.genfromtxt('./data/altman_12_6.txt', delimiter=',')
df = pd.DataFrame(data, columns=['hs', 'fetus', 'observer'])
import statsmodels.api as sm
from statsmodels.stats.anova import anova_lm
lm = sm.formula.ols('hs~C(fetus)+C(observer)+C(fetus):C(observer)',data=df).fit()
# 将待分析的因变量放在~左侧,因素放在~右侧
# 因素用C()括起来,交互项使用:将两者联系在一起。
anovaResults = anova_lm(lm) # 方差分析
print(anovaResults)# 3. 多元数值向量的假设检验
df sum_sq mean_sq F PR(>F)
C(fetus) 2.0 324.008889 162.004444 2113.101449 1.051039e-27
C(observer) 3.0 1.198611 0.399537 5.211353 6.497055e-03
C(fetus):C(observer) 6.0 0.562222 0.093704 1.222222 3.295509e-01
Residual 24.0 1.840000 0.076667 NaN NaN