假設檢驗的Python實現


結合假設檢驗的理論知識,本文使用Python對實際數據進行假設檢驗。

導入測試數據

從線上下載測試數據文件,數據鏈接:https://pan.baidu.com/s/1t4SKF6U2yyjT365FaE692A*
數據字段說明:

gender:性別,1為男性,2為女性
Temperature:體溫
HeartRate:心率

下載后,使用pandasread_csv函數導入數據。

import numpy as np
import pandas as pd
from scipy import stats

test_df = pd.read_csv('test.csv')

問題清單

針對此測試數據,提出以下問題:

  1. 人體體溫的總體均值是否為98.6華氏度?
  2. 人體的溫度是否服從正態分布?
  3. 人體體溫中存在的異常數據是哪些?
  4. 男女體溫是否存在明顯差異?
  5. 體溫與心率間的相關性(強?弱?中等?)

解題步驟

1. 體溫的總體均值是否為98.6華氏度?

解法1:
此問題可以轉化為假設檢驗問題,可以假設:
$H_0: \mu = 96.8 \( \)H_1: \mu \neq 96.8 $

這是一個雙側檢測問題,所以只要\(\mu>\mu_0\)\(\mu<\mu_0\)二者之中有一個成立,就可以拒絕原假設。

根據人大《統計學》第7版p163:

在樣本量大的條件下,如果總體為正態分布,則樣本統計量服從正態分布;如果總體為非正態分布,則樣本統計量漸近服從正態分布。在這些情況下,我們都可以把樣本統計量視為正態分布,這時可以使用\(z\)統計量。

本題中,總體標准差\(\sigma\)未知,可以用樣本標准差\(s\)代替。

## 計算Z統計量
mu = 96.8
temp = test_df['Temperature']
# 樣本均值
sample_mean = np.mean(temp)

# 樣本方差
sample_std = np.std(temp, ddof=1)
# 樣本個數
sample_size = temp.size

z = (sample_mean-mu)/(sample_std/np.sqrt(sample_size))
print(z)
22.537033076347175

按照雙側檢驗的原理,在顯著性水平\(\alpha=0.05\)情況下
\(z_{\frac{\alpha}{2}}=\pm 1.96\)
因為\(|z|>|z_{\frac{\alpha}{2}}|\),所以拒絕原假設,人體體溫的總體均值不是98.6華氏度。

解法2:
可以直接使用statsmodels包的statsmodels.stats.weightstats.ztest函數直接執行計算,http://www.statsmodels.org/stable/generated/statsmodels.stats.weightstats.ztest.html
用法如下:

statsmodels.stats.weightstats.ztest(x1, x2=None, value=0, alternative='two-sided', usevar='pooled', ddof=1.0)[source]

2. 人體的溫度是否服從正態分布?

解法:

參考Python驗證數據的抽樣分布類型,先畫出分布的直方圖,然后使用scipy.stat.kstest函數進行判斷。

%matplotlib inline
import seaborn as sns
sns.distplot(temp, color='b', bins=10, kde=True)

簡單從圖形看,大於99.3之后的數據分布極少。初步認為不符合正態分布。

然后使用kstest驗證。

In: stats.kstest(temp, 'norm')
Out: KstestResult(statistic=1.0, pvalue=0.0)

可以發現pvalue<0.05,即認為體溫不符合正態分布。

判斷是否服從t分布:

In: np.random.seed(1)
    ks = stats.t.fit(temp)
    df = ks[0]
    loc = ks[1]
    scale = ks[2]
    t_estm = stats.t.rvs(df=df, loc=loc, scale=scale, size=sample_size)
    stats.ks_2samp(temp, t_estm)
Out: Ks_2sampResult(statistic=0.11538461538461536, pvalue=0.33281734591562734)

此處的思路是先用t分布擬合區域收入均值,然后使用ks_2samp函數比較區域收入均值和t分布的隨機變量。因為pvalue大於0.05,認為該數據集服從t分布。

判斷是否服從卡方分布:

In: np.random.seed(1)
    chi_square = stats.chi2.fit(temp)
    df = chi_square[0]
    loc = chi_square[1]
    scale = chi_square[2]
    chi_estm = stats.chi2.rvs(df=df, loc=loc, scale=scale, size=sample_size)
    stats.ks_2samp(temp, chi_estm)
Out: Ks_2sampResult(statistic=0.07692307692307687, pvalue=0.8215795712396048)

pvalue為0.82,說明體溫數據更服從卡方分布。可以使用以下方式,畫出擬合的卡方分布和測試數據的對比圖。

from matplotlib import pyplot as plt
plt.figure()
temp.plot(kind = 'kde')
chi2_distribution = stats.chi2(chi_square[0], chi_square[1],chi_square[2])
x = np.linspace(chi2_distribution.ppf(0.01), chi2_distribution.ppf(0.99), 100)
plt.plot(x, chi2_distribution.pdf(x), c='orange')
plt.xlabel('Human temperature')
plt.title('temperature on chi_square', size=20)
plt.legend(['test_data', 'chi_square'])

3. 人體體溫中存在的異常數據是哪些?

解法:

已知體溫數據服從卡方分布的情況下,可以直接使用Python計算出P=0.025和P=0.925時的分布值,在分布值兩側的數據屬於小概率,認為是異常值。

In: chi2_distribution.ppf(0.025)
Out:97.0690523831819
In: chi2_distribution.ppf(0.925)
Out:99.332801136025
In: temp[temp<97.069]
Out:0     96.3
    1     96.7
    2     96.9
    3     97.0
    65    96.4
    66    96.7
    67    96.8
    Name: Temperature, dtype: float64
In: temp[temp>99.332]
    63      99.4
    64      99.5
    126     99.4
    127     99.9
    128    100.0
    129    100.8
    Name: Temperature, dtype: float64

4. 男女體溫是否存在明顯差異?

解法:

此題是一道兩個總體均值之差的假設檢驗問題,因為是否存在差別並不涉及方向,所以是雙側檢驗。建立原假設和備擇假設如下:

\(H_0: \mu_1 - \mu_2 = 0\) 沒有顯著差別

\(H_1: \mu_1 - \mu_2 \ne 0\) 有顯著差別

由於\(\sigma_1^2\),\(\sigma_2^2\)未知,也無法斷定\(\sigma_1^2=\sigma_2^2\)是否成立,且\(n_1\),\(n_2\)的數量為65。

In: test_df.groupby(['Gender']).size()
Out:Gender
    1    65
    2    65
    dtype: int64

在此樣本量情況,抽樣分布近似服從自由度為f的t分布,其中f為:

\[f=\frac{(\frac{s_1^2}{n_1}+\frac{s_2^2}{n_2})^2}{\frac{(\frac{s_1^2}{n_1})^2}{n_1-1}+\frac{(\frac{s_2^2}{n_2})^2}{n_2-1}} \]

檢驗統計量t的計算公式為:

\[t = \frac{(\bar{x_1}-\bar{x_2})-(\mu_1-\mu_2)}{\sqrt{\frac{s_1^2}{n_1}+\frac{s_2^2}{n2}}} \]

male_df = test_df.loc[test_df['Gender'] == 1]
female_df = test_df.loc[test_df['Gender'] == 2]
  • 方法一:構建統計量計算函數
def cal_f(a, b):
     n_1 = len(a)
     n_2 = len(b)
     mean_1 = a.mean()
     mean_2 = b.mean()
     std_1 = a.std()
     std_2 = b.std()

     s_1 = std_1**2/n_1
     s_2 = std_2**2/n_2
     f = (s_1 + s_2)**2 / (s_1**2/(n_1 - 1) + s_2**2/(n_2 -1))
     print('degree of freedom=%.3f', % f)

     t = (mean_1 - mean_2)/np.sqrt(s_1 + s_2)
     
     # 計算邊界值,設置顯著性水平為0.05,雙側檢驗,取邊界值為0.025
     v = stats.t.ppf(0.025, f)

     print('stat=%.3f, boudary=%.3f' % (t, v))

     if abs(t)>abs(v):
       print("拒絕原假設,男女體溫存在明顯差異。")
     else:
       print("不能拒絕原假設,男女體溫無明顯差異。")

調用自定義函數

In: cal_f(male_df['Temperature'],female_df['Temperature'])
Out:degree of freedom=127.510
    stat=-2.285, boudary=-1.979
    拒絕原假設,男女體溫存在明顯差異。
  • 方法2,使用ttest_ind函數1
stats.t.ppf(0.025, 127.51)
stat, p = stats.ttest_ind(male_df['Temperature'],female_df['Temperature'])
print('stat=%.3f, p=%.3f' % (stat, p))
if p > 0.05:
	print('不能拒絕原假設,男女體溫無明顯差異。')
else:
	print('拒絕原假設,男女體溫存在明顯差異。')
    
Out:
stat=-2.285, p=0.024
拒絕原假設,男女體溫存在明顯差異。

注意:此函數計算得出的p值為雙側概率的累加,所以直接與顯著性水平0.05進行比較。可以用以下方式證明是雙側概率:

# 將方法1中的統計值代入t分布的概率分布
In: stats.t.cdf(stat, 127.51)
Out:0.011969134059074056

上述結果正好是雙側概率的一半。

5. 體溫與心率間的相關性

可以使用皮爾遜相關性系數2檢驗兩組數據之間的關系。在處理前,可以先用散點圖展示下兩者在二維空間上的分布。

heartrate_s = test_df['HeartRate']
temperature_s = test_df['Temperature']
from matplotlib import pyplot as plt
plt.scatter(heartrate_s, temperature_s)

計算皮爾遜相關系數3

In: stat, p = stats.pearsonr(heartrate_s, temperature_s)
    print('stat=%.3f, p=%.3f' % (stat, p))
Out:stat=0.254, p=0.004

已知皮爾遜相關系數為0,數據無相關性,而大於0表示有正相關性,當為1時完全正相關。因為結果為0.004,體溫和心率之間可以認為基本無相關性。從圖形上也可以發現四散分布,缺乏相關性。

參考資料

歡迎掃描二維碼進行關注
fishdata


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM