import numpy as np
import pandas as pd
import time
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
plt.rcParams['figure.figsize'] = (18.0, 10.0)
pd.set_option('display.float_format', lambda x: '%.4f' % x)
pd.set_option('display.max_rows', 200)
pd.set_option('display.max_columns', 200)
1. 使用 Python 驗證數據集中的體溫是否符合正態分佈。
- 數據集地址:http://jse.amstat.org/datasets/normtemp.dat.txt
- 數據集描述:總共只有三列:體溫、性別、心率
- 數據集詳細描述:Journal of Statistics Education, V4N2:Shoemaker
加載數據
# filename = 'http://jse.amstat.org/datasets/normtemp.dat.txt'
filename = 'normtemp.dat.txt'
feature_name_list = [
'體溫',
'性別',
'心率',
]
df = pd.read_csv(filename, sep='\s+', header=None, skiprows=1, names=feature_name_list)
print('df shape={}'.format(df.shape))
print('df info={}'.format(df.info()))
print('df describe={}'.format(df.describe()))
display(df.head())
df shape=(130, 3)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 130 entries, 0 to 129
Data columns (total 3 columns):
體溫 130 non-null float64
性別 130 non-null int64
心率 130 non-null int64
dtypes: float64(1), int64(2)
memory usage: 3.1 KB
df info=None
df describe= 體溫 性別 心率
count 130.0000 130.0000 130.0000
mean 98.2492 1.5000 73.7615
std 0.7332 0.5019 7.0621
min 96.3000 1.0000 57.0000
25% 97.8000 1.0000 69.0000
50% 98.3000 1.5000 74.0000
75% 98.7000 2.0000 79.0000
max 100.8000 2.0000 89.0000
體溫 | 性別 | 心率 | |
---|---|---|---|
0 | 96.3000 | 1 | 70 |
1 | 96.7000 | 1 | 71 |
2 | 96.9000 | 1 | 74 |
3 | 97.0000 | 1 | 80 |
4 | 97.1000 | 1 | 73 |
df['體溫'].hist(bins=130)
<matplotlib.axes._subplots.AxesSubplot at 0x122fdd5f8>
import seaborn as sns
sns.set_palette("hls")
sns.distplot(df['體溫'], bins=130, kde=True)
plt.show()
/Users/jliang/anaconda3/envs/py36/lib/python3.6/site-packages/scipy/stats/stats.py:1633: FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.
return np.add.reduce(sorted[indexer] * weights, axis=axis) / sumval
檢驗溫度數據是否符合正態分佈
- 頻數分佈有正態分佈和偏態分佈之分。正態分佈是指多數頻數集中在中央位置,兩端的頻數分佈大致對稱。
- 偏態分佈是指頻數分佈不對稱,集中位置偏向一側。若集中位置偏向數值小的一側,稱爲正偏態分佈;集中位置偏向數值大的一側,稱爲負偏態分佈。
- 如果頻數分佈的高峯向左偏移,長尾向右側延伸稱爲正偏態分佈,也稱右偏態分佈;同樣的,如果頻數分佈的高峯向右偏移,長尾向左延伸則成爲負偏態分佈,也稱左偏態分佈。
方法一:根據峯度係數和偏度係數判斷
- 峯度係數是用於判定分佈是不是太尖或太平;
- 偏度係數用於判定偏左還是偏右;
利用變量的偏度和峯度進行正態性檢驗時,可以分別計算偏度和峯度的Z評分(Z-score)。
偏度Z-score = 偏度值 / 偏度值的標準差
峯度Z-score = 峯度值 / 峯度值的標準差
在 a=0.05的檢驗水平下,偏度Z-score和峯度Z-score是否滿足假設條件下所限制的變量範圍(Z-score在±1.96之間),若都滿足則可認爲服從正態分佈,若一個不滿足則認爲不服從正態分佈。
# 偏態係數
skew = df['體溫'].skew()
# 峯態係數
kurt = df['體溫'].kurt()
print('偏態係數={}, 峯態係數={}'.format(skew, kurt))
偏態係數=-0.004419131169113917, 峯態係數=0.7804573950337397
- 偏態係數小於0且接近0,曲線呈微左偏,大致呈對稱分佈。
- 峯態係數大於0且接近0,說明曲線微高聳。
什麼是“偏度值的標準差”、“峯度值的標準差”?偏態係數和峯態係數不是一個具體的值嗎?怎麼會有標準差可計算?
樣本的增加會減小偏度值和峯度值的標準差,相應的Z-score會變大,最終會拒絕條件假設,會給正確判斷樣本數據的正態性情況造成一定的干擾。因此,當樣本量小於100時,用偏度和峯度來判斷樣本的正態分佈性比較合理。
由於這裏的樣本量大於100個,因此不太適合使用此方法判斷是否滿足正態分佈。
好了,反正不適合用這種方法判斷,那我就不弄這個了。有知道的朋友請告訴我!
方法二:通過Kolmogorov-Smirnov檢驗(K-S檢驗)判斷
- statistic:D值;
- pvalue:P值。
當P>0.05時,可以認爲數據是呈正態分佈的,小於爲非正態性。
from scipy import stats
u = df['體溫'].mean() # 計算均值
std = df['體溫'].std() # 計算標準差
ks_test = stats.kstest(df['體溫'], 'norm',(u,std))
print(ks_test)
KstestResult(statistic=0.06472685044047233, pvalue=0.6450307317438662)
結果顯示p-value大於顯著性水平0.05,所以不能拒絕零假設:樣本來自正態分佈。
方法三:通過Shapiro- Wilk (W 檢驗)判斷
-
W的值越接近1就越表明數據和正態分佈擬合得越好;
-
P值>指定水平, 不拒絕原假設,可以認爲樣本數據服從正態分佈;
-
不適合樣本>5000的情況;
-
W:統計數;
-
p-value:p值。
當P>0.05時,可以認爲數據是呈正態分佈的,小於爲非正態性。
import scipy.stats as stats
w, p = stats.shapiro(df['體溫'])
print(w, p)
0.9865770936012268 0.233174666762352
結果顯示p-value大於顯著性水平0.05,所以不能拒絕零假設:樣本來自正態分佈。