各种假设检验Python实现 -- 潘登同学的数理统计笔记
数据集说明
本文全程采用ATTEND
数据集, 说的是不同班级出勤率与成绩关系的一个数据集
变量说明
- attend:出勤人数(32人为全勤)
- termGPA:这个学期的GPA
- priGPA:上个学期的GPA
- ACT:美国高考的分数
- final:期末考试分数
- atndrte:课程参加了百分比
- hwrte:交作业百分比
- frosh:是否是大一(是大一=1)
- soph:是否是大二(是大二=1)
- skipped:number of classes skipped(0_0不会翻译)
- stndfnl:(final - mean)/sd 期末成绩偏离均值的程度
t检验
单总体检验
- 任务
判断final的总体均值为25这一说法的正确性
- 原假设
$H_0$: 总体均值为$25$, 即$\mu = 25$
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
#%%单样本t检验
import pandas as pd
from scipy import stats as ss
#检验sepal_width的均值是否为3
path = '统计编程/attend.xls'
data = pd.read_excel(path)
data.columns
data['final'].plot(kind = 'hist')
ss.ttest_1samp(data.final,25)
#得出p值为1e-6 < 0.05 故拒绝原假设
ss.ttest_1samp(data.final,26)
#p接近于0.547 > 0.05,故有95%的把握认为均值为26
- 结果分析
结果中的statistic
表示t统计量的大小, pvalue为p值, p值越小越拒绝原假设, 通常用p值与显著性水平$\alpha$进行比较, $p<\alpha$就拒绝原假设;
双总体检验
- 原假设
$H_0$: 出勤人数对期末成绩有没有显著影响, 将数据集按照attend的中位数分为两部分, 两部分的均值相同
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
# %% 双总体t检验
# 分析出勤人数对期末成绩有没有显著影响
data.describe()
# 将数据集划分成两部分
attend_min = data[data['attend'] <= 28]
attend_max = data[data['attend'] > 28]
#先检验方差齐性
ss.levene(attend_min.final,attend_max.final)
#得出p值小于0.05,说明方差不齐
# 双总体检验
ss.ttest_ind(attend_min.final,attend_max.final,equal_var=False)
#得出p值小于0.05,拒绝原假设, 说明两总体的均值不同
- 结果分析
我们这里先做了方差齐性的检验, 得到的p-value小于0.05, 说明方差不相等
于是我们在做双总体t检验时, 一个超参数equal_var
选择了False, 得到的p-value小于0.05, 所以拒绝原假设, 说明两总体的均值不同
相关系数检验
- 原假设
$H_0$: 高考成绩对GPA没有影响, 即ACT与GPA的相关系数为0
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
from scipy.stats import pearsonr
r,p = pearsonr(data.ACT, data.termGPA)
print('相关系数为:', r, '\np-value:', p)
- 结果分析
pearsonr能直接返回p-value, 这里的p值小于0.05, 故我们拒绝原假设,认为高考成绩对GPA有影响
F检验
方差齐性检验
我们碰巧在前面做了一个方差检验, 这里还是再来一个新的例子吧
- 原假设
$H_0$: 大一大二的同学的出勤方差不变(这个问题就是说:卷的不会更卷, 懒的也不会更懒)
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
# %% 方差齐性检验
# 大一大二的同学的出勤方差不变
# 将数据集划分成两部分
frosh = data[data['frosh'] == 1] # 大一的
soph = data[data['soph'] == 1] # 大二的
# 检验方差齐性
ss.levene(frosh.attend,soph.attend)
# 得出p值为0.44 > 0.05,说明方差相等
- 结果分析
得出p值为0.44 > 0.05,不拒绝原假设, 说明方差相等 也就是:卷的不会更卷, 懒的也不会更懒
单因素方差分析
- 原假设
$H_0$: 不同水平的skipped对期末成绩没有影响
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
# %% 单因素方差分析
# 对skipped进行离散化, 分为3个区间
data['skipped'].plot(kind = 'hist')
bins = [0, 10, 20 ,31]
labels = ['skipped_A', 'skipped_B', 'skipped_C']
data['skipped_1'] = pd.cut(data.skipped, bins, right=False, labels=labels)
skipped_A = data[data['skipped_1']=='skipped_A']
skipped_B = data[data['skipped_1']=='skipped_B']
skipped_C = data[data['skipped_1']=='skipped_C']
#检验方差齐性
ss.levene(skipped_A.final, skipped_B.final, skipped_C.final)
# 得出p值为0.11 > 0.05,说明方差相等
# 方差相等, 再做单因素方差分析
ss.f_oneway(skipped_A.final, skipped_B.final, skipped_C.final)
# 得出p值为0.01 < 0.05,拒绝原假设, 说明不同水平的skipped对期末成绩有影响
- 结果分析
得出p值为0.01 < 0.05,拒绝原假设, 说明不同水平的skipped对期末成绩有影响
注意
单因素方差分析的样本要求: 每一水平的总体服从均值为0的正态分布且各总体方差相等 所以先做了方差齐性的检验
线性回归方程整体的显著性检验
- 任务
用除final、stndfnl之外的所有变量去对final进行回归
- 原假设
$H_0$: 线性回归方程整体不显著, 即回归系数均为0
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
# %% 线性回归方程整体的显著性检验
import statsmodels.api as sm
def deal_with(x):
if x == '.':
return None
else:
return float(x)
data['hwrte'] = data['hwrte'].apply(deal_with) # 转换数据类型
data['hwrte'].fillna(method='backfill', inplace=True) # 填充缺失值
y = data['final']
X=data.drop(columns = ['final', 'stndfnl', 'skipped_1'])
X=sm.add_constant(X) #添加常数项
model=sm.OLS(y,X)
results=model.fit()
y_pred=pd.DataFrame(model.predict(results.params,X),
columns=['pred'])
print(results.summary())
- 结果分析
我们只需看P值那一列就行, 可以看到priGPA、hwrte、frosh
这三个变量是不显著的, 对应回实际中的作用就是可以把这三个变量从回归方程中去掉;
卡方检验(非参数检验)
- 原假设
$H_0$: 大一大二的高考成绩相同(这是显然的, 因为高考成绩与你在读大一还是大二没有关系)
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
# %% 卡方检验
# 对ACT进行离散化
data['new_ACT'] = pd.cut(data.ACT,bins = 4)
# 卡方的列联表
t = pd.crosstab(data['new_ACT'],data['frosh'])
#期望频数--> 指的是如果原假设正确, 那么连列联表应该是这样,
# 现在就是去检验到底期望与实际的差距是由误差导致的还是 ACT会收到年级的影响
ss.contingency.expected_freq(t)
ss.contingency.chi2_contingency(t,False)
# False表示不做卡方统计量的修正
# 若为真,则*和*自由度为1,应用耶茨修正,对于连续性。
# 调整的效果就是调整每一个观测值与相应的期望值相差0.5。
# 得出p值为0.005 < 0.05,拒绝原假设, 说明不同年级的学生的ACT不相等
- 结果分析
得出p值为0.005 < 0.05,拒绝原假设, 说明不同年级的学生的ACT不相等, 这显然违反常识, 我们只能这样解释他, 要么就是题目难易程度影响了学生的成绩, 要么就是今年招到了更好的学生。
K-S检验
单样本K-S
- 原假设
$H_0$: 期末考试成绩服从正态分布
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
#%% K-S检验
# 检验期末考试成绩是否服从正态分布
# 绘图查看
data['final'].plot(kind = 'hist')
ss.kstest(data['final'], 'norm')
# 得出p值为0.00< 0.05,拒绝原假设, 说明期末考试成绩不服从正态分布
- 结果分析
结果就没啥好分析的, 唯一要说的就是可能画图画出来的看上去是正态分布, 实际是不一定服从正态分布
两独立样本K-S检验
- 原假设
$H_0$: 大一大二的同学的期末考试成绩服从相同分布
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
# 两独立样本K-S检验
# 检验大一的与大二的同学的期末考试成绩是否同分布
ss.ks_2samp(frosh.final,soph.final)
# 得出p值为0.046< 0.05,拒绝原假设,大一的与大二的同学的期末考试成绩不同分布
- 结果分析
也没啥好分析的, 要看显著性水平怎么选, 要是像我们选择了0.05, 那么就会拒绝原假设, 但实际上可能大一大二的同学的期末考试成绩服从相同分布, 所以假设检验的显著性选择也很重要
游程检验
因为我们的数据是面板数据, 显然不是做游程检验的对象, 我们随机生成一个随机的变量序列, 对其做游程检验
- 原假设
$H_0$: 该序列变量值出现是随机的
- 确定显著性水平
$$\alpha = 0.05$$
- Python实现
from statsmodels.sandbox.stats.runs import runstest_1samp
import random
random.seed(45)
# 生成随机变量序列
x = []
for i in range(100):
if random.random() < 0.4:
x.append(1)
else:
x.append(0)
# 做游程检验
runstest_1samp(x)
# 得出p值为0.33 > 0.05,不拒绝原假设,该序列变量值出现是随机的
- 结果分析
得出p值为0.33 > 0.05,不拒绝原假设,说明该序列变量值出现是随机的;这与我们的构造变量的时候就是随机构造的结果一致;
我的GitHub仓库
本文的数据集和代码都会上传到我的GitHub仓库