以下有一份1973年,美国疾病控制和预防中心(CDC)进行全国家庭增长调查的数据报告,收集“与家庭生活、婚姻情况、妊娠情况、生育情况、避孕情况,以及两性健康相关的信息。此项调查的结果用于进行健康服务和健康教育项目的规划,以及对家庭、生育及健康情况进行统计研究”。
其中“prglngth”表示孕妇每胎妊娠的时长(周数),我们现在要验证一个结论:
孕妇第一胎妊娠的时长(周数)不同于其他妊娠的时长(周数)。
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
live,firsts,others=first.MakeFrames()
firsts['prglngth'].mean()
others['prglngth'].mean()
abs(firsts['prglngth'].mean()-others['prglngth'].mean())
输出:
4413
4735
38.60095173351461
38.52291446673706
0.07803726677754952
第一胎样本量为4413个,第二胎样本量为4735个。从数据上看,第一胎的妊娠时长均值(38.60095173351461)大于其他的妊娠时长均值(38.52291446673706),差值的绝对值为0.07803726677754952。
接下来我们需要去检验第一胎的妊娠时长大于其他胎的妊娠时长是否显著。
1、首先我们假设两个样本的分布(均值)相同。
2、在假设均值相等的基础之上,我们将两组数据混合在一起,再随机分组(按照原始的样本量之比),重复这样的操作1000次,我们会得到1000个不同的组合,分别求其均值之差,得到1000个均值的差值,按照均值差值的绝对值降序如下:
import collections
#将两个样本中的元素混合打散。
def RunModel(group1,group2):
m,n=len(group1),len(group2)
pool=np.hstack((group1,group2)) #将两个Series前后拼接成一组
np.random.shuffle(pool) #将拼接后的数组中的元素打散,重新排列。
data=pool[:m],pool[m:] #将数组重新划分成两个数组
return data
#计算每次重新划分之后的均值之差。
def TestStatistic(data):
group1,group2=data
di=abs(group1.mean()-group2.mean())
return di
a=[TestStatistic(RunModel(firsts['prglngth'],others['prglngth'])) for _ in range(1000)]
sorted(a,reverse=True)
输出:
3、计算这1000次当中,有多少次是当前情况及更差情况,除以1000,我们可以得到其概率值。
b=sum(1 for x in a if x>=abs(firsts['prglngth'].mean()-others['prglngth'].mean())) #计算比当前情况乃至更差情况出现的次数
b/1000
输出:
0.147
即在原假设(均值相等)成立的情况下,我们预计有0.147的可能性出现妊娠时间差为当前情况所观测到的差值,可能性较大(>0.05)。故我们认为差异不显著。
下图展示绘制结果。
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
plt.figure(figsize=(6,4.5)) #表示绘制图形的画板尺寸为6*4.5;
x=sorted(a,reverse=False)
y=np.cumsum([1 for _ in range(len(a))])/len(a)
plt.plot(x,y)
plt.vlines(abs(firsts['prglngth'].mean()-others['prglngth'].mean()),ymin=0,ymax=1) #垂直于X轴的辅助线
plt.xlim(0,0.25)
plt.ylim(0,1)
plt.xlabel('均值差的绝对值')
plt.ylabel('累积概率密度')
plt.show()
输出:
从图中可以看出,累积概率密度与观察到的差值在0.85(即p值0.147的补)处相交。
如果我们是检验“孕妇第一胎妊娠的时长(周数)大于其他妊娠的时长(周数)”。这个时候我们只需要检验差值分布的一侧,称为单侧检验。
对比上面的检验,我们只需要将如下代码中的绝对值去掉。
import collections
#将两个样本中的元素混合打散。
def RunModel(group1,group2):
m,n=len(group1),len(group2)
pool=np.hstack((group1,group2)) #将两个Series前后拼接成一组
np.random.shuffle(pool) #将拼接后的数组中的元素打散,重新排列。
data=pool[:m],pool[m:] #将数组重新划分成两个数组
return data
#计算每次重新划分之后的均值之差。
def TestStatistic(data):
group1,group2=data
di=group1.mean()-group2.mean()
return di
a=[TestStatistic(RunModel(firsts['prglngth'],others['prglngth'])) for _ in range(1000)]
sorted(a,reverse=True)
输出:
3、计算这1000次当中,有多少次是当前情况及更差情况,除以1000,我们可以得到其概率值。
b=sum(1 for x in a if x>=firsts['prglngth'].mean()-others['prglngth'].mean()) #计算比当前情况乃至更差情况出现的次数
b/1000
输出:
0.094
即在原假设成立的情况下,我们预计有0.094的可能性出现妊娠时间差为当前情况所观测到的差值,可能性较大(>0.05)。故我们认为差异不显著。
下图展示绘制结果。
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
plt.figure(figsize=(6,4.5)) #表示绘制图形的画板尺寸为6*4.5;
x=sorted(a,reverse=False)
y=np.cumsum([1 for _ in range(len(a))])/len(a)
plt.plot(x,y)
plt.vlines(firsts['prglngth'].mean()-others['prglngth'].mean(),ymin=0,ymax=1) #垂直于X轴的辅助线
plt.ylim(0,1)
plt.xlabel('均值差的绝对值')
plt.ylabel('累积概率密度')
plt.show()
输出:
从图中可以看出,累积概率密度与观察到的差值在0.906(即p值0.094的补)处相交。
如果我们是检验“孕妇第一胎妊娠的时长(周数)相较于其他妊娠的时长(周数),更容易提前或者推迟出生,较少准时”。这个时候我们可以假设第一胎妊娠时长的标准差更高,同样用到单侧检验。
import collections
#将两个样本中的元素混合打散。
def RunModel(group1,group2):
m,n=len(group1),len(group2)
pool=np.hstack((group1,group2)) #将两个Series前后拼接成一组
np.random.shuffle(pool) #将拼接后的数组中的元素打散,重新排列。
data=pool[:m],pool[m:] #将数组重新划分成两个数组
return data
#计算每次重新划分之后的均值之差。
def TestStatistic(data):
group1,group2=data
di=group1.std()-group2.std()
return di
a=[TestStatistic(RunModel(firsts['prglngth'],others['prglngth'])) for _ in range(1000)]
sorted(a,reverse=True)
输出:
3、计算这1000次当中,有多少次是当前情况及更差情况,除以1000,我们可以得到其概率值。
b=sum(1 for x in a if x>=firsts['prglngth'].std()-others['prglngth'].std()) #计算比当前情况乃至更差情况出现的次数
b/1000
输出:
0.088
即在原假设成立的情况下,我们预计有0.088的可能性出现妊娠时间差为当前情况所观测到的差值,可能性较大(>0.05)。故我们认为差异不显著。
下图展示绘制结果。
from matplotlib import pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
plt.figure(figsize=(6,4.5)) #表示绘制图形的画板尺寸为6*4.5;
x=sorted(a,reverse=False)
y=np.cumsum([1 for _ in range(len(a))])/len(a)
plt.plot(x,y)
plt.vlines(firsts['prglngth'].std()-others['prglngth'].std(),ymin=0,ymax=1) #垂直于X轴的辅助线
plt.ylim(0,1)
plt.xlabel('标准差的差值')
plt.ylabel('累积概率密度')
plt.show()
输出:
从图中可以看出,累积概率密度与观察到的差值在0.912(即p值0.088的补)处相交。
前面我们研究了第一胎和其他胎的妊娠时间,认为两组的样本均值和标准差的直观差异不是显著的,但是在以下我们发现,在35~43周范围内的差异尤为明显,要判断这些差异是否统计显著,我们可以使用基于卡方统计量的检验。
原假设:两个样本来自同一分布。
import collections
#导入数据
live,firsts,others=first.MakeFrames()
#根据频数列表、返回频率(字典)
def Pmf(counter):
d={}
for x,freq in counter.items():
d[x]=freq/sum(counter.values())
return d
#将两个样本中的元素混合打散,并取该区间段的频率,作为理论频率
def MakeModel(group1,group2,values=None):
pool=np.hstack((group1,group2)) #将两个Series前后拼接成一组
counter=collections.Counter(pool) #统计出生时间的分布频数
pmf=Pmf(counter) #将出生时间分布频数转换为频率
expexted_prob=np.array([pmf.get(x,0) for x in values]) #选出35到44区段的概率分布
return expexted_prob
def Hist(group,values=None):
counter=collections.Counter(group) #统计出生时间的分布频数
observed=np.array([counter.get(x,0) for x in values]) #观擦值在某区间内的分布
return observed
def RunModel(group1,group2,values=None): #重新划分样本
m,n=len(group1),len(group2)
pool=np.hstack((group1,group2)) #将两个Series前后拼接成一组
np.random.shuffle(pool) #将拼接后的数组中的元素打散,重新排列。
data=Hist(pool[:m],values),Hist(pool[m:],values) #将数组按照比例重新划分,并计算区间频数
return data
#根据实际频数和理论频数,计算卡方值。
def ChiSquared(observed,expected):
stat_a=(observed-expected)**2/expected
stat_a[np.isnan(stat_a)] = 0 #注意expected中有可能为0,不能作为分母,需要处理。
stat=sum(stat_a)
return stat
#计算每次重新划分之后的均值之差。
def TestStatistic(firsts,others,values=None):
#生成理论频数
expected_firsts=MakeModel(firsts,others,values)*len(firsts)
expected_others=MakeModel(firsts,others,values)*len(others)
#打散之后统计实际频数
observed_firsts,observed_others=RunModel(firsts,others,values)
chi_firsts=ChiSquared(observed_firsts,expected_firsts)
chi_others=ChiSquared(observed_others,expected_others)
stat=chi_firsts+chi_others #计算两个样本的卡方值之和
return stat
根据原始数据计算观察到的卡方统计量
#观察到的卡方统计量
values=np.arange(35,44)
expected_firsts=MakeModel(firsts['prglngth'],others['prglngth'],values)*len(firsts['prglngth'])
expected_others=MakeModel(firsts['prglngth'],others['prglngth'],values)*len(others['prglngth'])
#(Hist(firsts,values)-expected_firsts)**2/expected_firsts
a=ChiSquared(Hist(firsts['prglngth'],values),expected_firsts)+ChiSquared(Hist(others['prglngth'],values),expected_others)
a
输出:
101.50141482893264
基于原假设(两样本来自同一总体),将数据混合,重新分,反复1000次,计算卡方值,最大值为26.51。
values=np.arange(35,44)
b=[TestStatistic(firsts['prglngth'],others['prglngth'],values) for _ in range(1000)]
sorted(b,reverse=True)
输出:
c=sum(1 for x in b if x>=a) #计算比当前情况乃至更差情况出现的次数
c/1000
输出:
0
即在原假设成立的情况下,最大的卡方和为26.51,但是当前情况的卡方值为101.5,远大于26.51,故我们认为在原假设成立的情况下,发生当前情况(及更极端情况)的概率基本为0,故我们认为第一胎和其他胎在35~43周内差异是显著的。
这个示例说明了卡方检验存在一个局限:卡方检验可以证明两个群组之间存在差异,但不能揭示这个差异是什么。