技术人生,  机器学习

数据可视化与Z-Test假设检验方法

本文使用Python数学绘图库matplotlib针对“2011年英格兰各地区的人口普查结果”数据集进行可视化探索。依据绘制出的图表设定合理的假设,并采用Z检验方法对假设进行检验论证。

数据集简介

本次实验采用的数据集为2011年英格兰各地区的人口普查年龄样本数据。数据集已整理为csv格式,第一列为数据索引,共有1000行数据,之后的每一列分别包含一个地区的年龄数据。其中,每一个数据点代表截至调查时间某个人最后一个生日时的年龄(0代表尚未满1岁的婴儿,35代表一个已满35岁的人)。特例:100代表年龄位于100到114之间,且数据集中不包含年龄大于114的数据。

数据来源:英国国家统计局,2011年人口普查汇总数据。DOI: http://dx.doi.org/10.5257/census/aggregate-2011-2

直接取自:http://infuse2011.ukdataservice.ac.uk/

数据读取

导入接下来会用到的相关库。使用Pandas读取数据集文件,并显示前10行,结果如下表。

import pandas as pd
import matplotlib.pyplot as plt
import random
import numpy as np
from scipy import stats
df = pd.read_csv("census11sample.csv", index_col=0)
df.head(10)
North East North West Yorkshire and The Humber East Midlands West Midlands East of England London South East South West
0 27 32 65 60 44 79 43 8 9
1 10 36 40 19 52 46 58 87 40
2 82 53 60 34 2 29 18 46 57
3 33 63 25 60 9 26 30 52 74
4 55 98 4 47 58 37 18 67 19
5 27 26 66 70 33 41 38 78 63
6 42 44 49 59 40 65 13 8 52
7 62 44 41 76 8 74 54 40 72
8 34 17 33 71 45 73 1 77 75
9 15 46 37 3 13 41 19 47 69

数据集划分

由于之后我们将根据可视化图表设定假设用以验证测试,为了确保获得无偏测试结果,我们需要将原数据集划分为两部分,一部分用于任意探索并绘制可视化图表,另一部分用于假设的检验。接下来,我们分别称这两部分数据集为sample_exploresample_test

数据集的划分仍然基于之前读取的DataFrame,我们计划将原数据集按照1:1进行划分,先用df.sample方法随机取样,然后使用布尔掩码筛选出另一部分,最后不要忘了重建索引。

# divide the original dataset into 1:1 with no overlapping
sample_explore = df.sample(frac=0.5)
sample_test = df[~df.index.isin(sample_explore.index)]
sample_explore = sample_explore.reset_index(drop=True)
sample_test = sample_test.reset_index(drop=True)
sample_explore
North East North West Yorkshire and The Humber East Midlands West Midlands East of England London South East South West
0 20 78 24 26 3 2 24 74 21
1 43 53 30 60 66 57 12 48 37
2 70 49 80 23 42 71 51 2 22
3 90 69 62 59 24 77 46 47 67
4 52 60 66 5 18 46 65 44 55
495 12 4 74 45 33 43 44 66 42
496 43 19 12 47 29 55 51 66 55
497 73 87 41 50 57 2 34 20 78
498 26 19 4 40 76 37 10 58 27
499 7 34 16 49 44 31 51 11 26

500 rows × 9 columns

数据探索和挖掘

下面我们将尽可能充分挖掘sample_explore样本集,并希望从中发现一些客观规律以形成可被证伪的客观假设。

首先计算数据集中各个地区的整体描述类统计信息,包含最值、中值、均值、标准误差、方差等等。

# calculate descriptive statistics
sample1d = sample_explore.stack().reset_index(drop=True)
statistics = []
for column in ['Total'] + list(sample_explore.columns):
    c_data = sample1d if column == 'Total' else sample_explore[column]
    statistics.append([c_data.min(), c_data.max(), c_data.median(), ', '.join(str(x) for x in c_data.mode()), c_data.quantile(0.25), c_data.quantile(0.75), c_data.quantile(0.75) - c_data.quantile(0.25), c_data.mean(), c_data.sem(), c_data.var(), c_data.std()])
desc_stats = pd.DataFrame(statistics, index=['Total (England)']+list(sample_explore.columns), columns=['Min','Max','Median', 'Mode', 'Q1','Q3','IQR','Mean','Standard Error','Variance','Standard Deviation'])
print("  ↓ Descriptive statistics of the whole England & different regions of the 'sample_explore' dataset")
desc_stats

↓ Descriptive statistics of the whole England & different regions of the ‘sample_explore’ dataset

Min Max Median Mode Q1 Q3 IQR Mean Standard Error Variance Standard Deviation
Total (England) 0 100 39.0 47 20.0 58.00 38.00 39.535556 0.350215 551.926497 23.493116
North East 0 90 39.5 49 20.0 57.25 37.25 39.442000 1.051182 552.491619 23.505140
North West 0 98 39.0 18 18.0 58.00 40.00 39.058000 1.084249 587.798232 24.244551
Yorkshire and The Humber 0 100 39.0 47 20.0 57.00 37.00 39.568000 1.052629 554.013403 23.537489
East Midlands 0 90 38.0 37, 43 20.0 57.00 37.00 39.392000 1.014396 514.499335 22.682578
West Midlands 0 96 39.0 27, 58 20.0 58.00 38.00 39.942000 1.050327 551.593824 23.486035
East of England 0 97 40.0 48 21.0 59.25 38.25 40.322000 1.050430 551.701719 23.488332
London 0 94 34.0 38 17.0 49.00 32.00 34.908000 0.982324 482.480497 21.965439
South East 0 95 39.0 20 20.0 57.00 37.00 38.828000 1.030495 530.960337 23.042577
South West 0 98 45.0 47 23.0 64.00 41.00 44.360000 1.099384 604.323046 24.582983

开始进行数据可视化,首先绘制探索样本集整体的数据分布直方图。

# style of plots
plt.style.use("seaborn-darkgrid")

# histogram
plt.figure(figsize=(18,10))
plt.title("Histogram of ages in the whole England")
plt.hist(sample1d, bins=50, edgecolor='white', color='darkorange')
plt.xlabel("Age")
plt.ylabel("Number")
plt.show()
2021-04-16T16:45:28.983415 image/svg+xml Matplotlib v3.3.4, https://matplotlib.org/

绘制各个地区的年龄数据散点图。设置x方向一个小幅度的随机偏移令数据点稍微错开。

# scatter plot
plt.figure(figsize=(18,10))
plt.title("Scatter plot of ages in different regions of England")
for i,column in enumerate(sample_explore.columns):
    plt.scatter([i+random.random()*0.6-0.3 for x in range(len(sample_explore))], sample_explore[column], s=2)
plt.xticks(list(range(len(sample_explore.columns))), sample_explore.columns)
plt.xlabel("Regions")
plt.ylabel("Age")
plt.show()
2021-04-16T16:45:29.310413 image/svg+xml Matplotlib v3.3.4, https://matplotlib.org/

绘制各个地区含误差条(标准均值误差)的均值分布图。

# means with errorbars
plt.figure(figsize=(18,10))
plt.title("Means and standard errors of ages in different regions of England")
plt.errorbar(list(range(1, 10)), sample_explore.mean(), yerr=sample_explore.sem(), fmt='o', capsize=10, color="darkviolet")
plt.xticks(list(range(1, 10)), sample_explore.columns)
plt.xlabel("Regions")
plt.ylabel("Age")
plt.show()
2021-04-16T16:45:29.767412 image/svg+xml Matplotlib v3.3.4, https://matplotlib.org/

绘制各个地区年龄数据的箱型图。箱型图可以展示一组数据的中位数、上下四分位数和上下边缘(上下四分位数±1.5*IQR)。

# boxplot
plt.figure(figsize=(18,10))
plt.title("Boxplot of ages in different regions of England")
plt.boxplot(sample_explore, labels=list(sample_explore.columns), notch=True, medianprops={'color':'darkorange', 'linewidth':2})
plt.xlabel("Regions")
plt.ylabel("Age")
plt.show()
2021-04-16T16:45:30.058413 image/svg+xml Matplotlib v3.3.4, https://matplotlib.org/

最后,再加上一张展示各个地区不同年龄段层次比例的柱状图。

# grouped bar plot to show age structures
age_structs = [[len(sample_explore[(sample_explore[column] >= 0) & (sample_explore[column] <= 20)]),
                len(sample_explore[(sample_explore[column] >= 21) & (sample_explore[column] <= 45)]),
                len(sample_explore[(sample_explore[column] >= 46) & (sample_explore[column] <= 70)]),
                len(sample_explore[(sample_explore[column] >= 71) & (sample_explore[column] <= 100)])]
                for column in sample_explore.columns]
age_nums = []
for s in age_structs:
    age_nums += s
plt.figure(figsize=(18,10))
plt.title("Age structures of dirrerent regions in England")
plt.bar(list(range(0, 45, 5)), [x[0] for x in age_structs], width=0.9, color='dodgerblue', label="0 - 20")
plt.bar(list(range(1, 45, 5)), [x[1] for x in age_structs], width=0.9, color='darkorange', label="21 - 45")
plt.bar(list(range(2, 45, 5)), [x[2] for x in age_structs], width=0.9, color='limegreen', label="46 - 70")
plt.bar(list(range(3, 45, 5)), [x[3] for x in age_structs], width=0.9, color='darkorchid', label="71 - 100")
for a,b in zip([x for x in range(45) if x % 5 != 4], age_nums):
    plt.text(a, b+1, '{:.1f}'.format((b/len(sample_explore))*100)+'%', ha='center', va= 'bottom', fontsize=9)
plt.xlabel("Regions")
plt.ylabel("Number")
plt.xticks([x+1.5 for x in range(0, 41, 5)], sample_explore.columns)
plt.legend()
plt.show()
2021-04-16T16:45:30.544415 image/svg+xml Matplotlib v3.3.4, https://matplotlib.org/

设定假设

  • 假设:伦敦人口的平均年龄小于英格兰东南部人口的平均年龄。
  • 证据:伦敦的平均年龄误差条明显低于东南部的平均年龄误差线,且大致相差两段误差线。

检验方案

根据如上总体数据分布直方图,可以发现英格兰的人口年龄分布大致遵循正态分布规律。因此,计划采用Z检验来比较假设中两个地区的人口年龄均值,以进一步验证我的假设。

  • 原假设 (Null Hypothesis):伦敦人口的平均年龄等于英格兰东南部人口的平均年龄。
  • 备择假设 (Alternative Hypothesis):伦敦人口的平均年龄小于英格兰东南部人口的平均年龄。
  • 置信度 (Probability level):95% (α=0.05)

接下来将在sample_test测试数据集上进行假设检验,尝试通过拒绝原假设验证备择假设。根据备择假设做出的描述,此处应进行左尾检验 (Left Tail Test)。

具体的,我们将把检验测试结果与临界值 (critical z value) 进行比较,若测试结果小于临界值 (p-value < α),则可以拒绝原假设并支持备择假设,即意味着我设定的原始假设通过了检验。

Z检验 (Z-Test)

Z检验又称U检验,是一种常用于样本均值对比或样本均值与常量对比的假设检验方法。其适用于服从或近似服从正态分布的随机变量,可以通过给定样本判断数学期望差异的显著性。

当进行两个样本间的均值对比时,Z分数的计算公式如下:

$$Z = \frac{\bar{x}_1 – \bar{x}_2}{\sqrt{\frac{s_1^2}{n_1} + \frac{s_2^2}{n_2}}}$$

其中 \(\bar{x}_1\) 和 \(\bar{x}_2\) 表示两个样本集数据的均值,\(s_1^2\) 和 \(s_2^2\) 为样本方差,\(n_1\) 和 \(n_2\) 为样本总数。即两样本均值差与总体标准误差的比值。

$$\frac{\hat{m}_1 – \hat{m}_2}{\sqrt{SEM_1^2 + SEM_2^2}}$$

Z分数的计算结果满足标准正态分布,因此临界值等于标准正态分布的累计分布函数取 \(\alpha\) 值时对应的 \(x\) 值,即 \(ppf(\alpha)\)。

需要注意的是,Z检验仅适用于样本容量较大的情况,一般要求样本容量 \(\ge 30\),否则应采用学生T检验

完成假设检验

根据设定的检验方案,完成检验流程,并判断输出结果。

# get test data from sample_test dataset
londonSample = sample_test['London']
southEastSample = sample_test['South East']

# set alpha value and calculate critical value
alpha = 0.05
critical_value = stats.norm.ppf(alpha)
critical_value

# calculate the z test statistic by formula
meanLondon = londonSample.mean()
meanSouthEast = southEastSample.mean()
meanDiff = meanLondon - meanSouthEast

varLondon = londonSample.var()
varSouthEast = southEastSample.var()

combined_sem = np.sqrt(varLondon/len(londonSample) + varSouthEast/len(southEastSample))

z_stat = meanDiff / combined_sem

# compare z_stat and critical value to decide the result
print("alpha value is", alpha)
print("critical value is", critical_value)
print("z statistic is", z_stat)
print("result of left tail test:")
if (z_stat < critical_value):
    print("reject Null hypothesis")
else:
    print("failed to reject Null hypothesis")
alpha value is 0.05
critical value is -1.6448536269514729
z statistic is -2.967612239842122
result of left tail test:
reject Null hypothesis

结论

使用经典假设检验方法——Z检验,在95%的置信度下,最终成功拒绝原假设。

A WindRunner. VoyagingOne

留言

您的邮箱地址不会被公开。 必填项已用 * 标注