Py学习  »  Python

3万字长文!手把手教你学会用Python实现统计学

经管之家 • 1 年前 • 884 次点击  

本文来自:山有木兮水有鱼(ID:symx-syy)——发现和追求有意思和有意义的事情



1.统计学简介

听说你已经被统计学劝退,被Python唬住……先别着急划走,看完这篇再说!

先说结论,大多数情况下的学不会都不是知识本身难,而是被知识的传播者劝退的

比如大佬们授课,虽逻辑严谨、思维缜密,但你只能望其项背,因为大佬们往往无法体会菜鸟的痛苦。再比如一些照本宣科的老师,他们没有深入研究这些知识,无法用通俗的语言帮你解释,只能貌似努力地帮你认真地读完所有PPT……

究其本质而言,这种情况多半是按 “是什么、有什么用,怎么用” 的方式在学,而对在大多数人而言,第一步就学懂“是什么”,或许难度有点大,因为得从定义出发,了解性质,推导出原理,一套流程下来直接劝退了,反而最关心的有什么用、怎么用的问题没有解决。

所以接下来的内容我将用“MVP(最小可行化产品)” 的思路来筛选重点内容,帮你厘清哪些内容是不可或缺及必须要学的。然后以 “有什么用,怎么用,是什么” 的顺序展开,快速提升当你急需Get某个技能时候的学习效率。

另外教程的标题既然含有“极简入门”,那么至少有2个原则:

  • 尽量不废话
  • 尽量说人话

说“尽量”是因为有些时候,不得不说些废话才能引起你的注意,比如以上内容…

好,我们正式开始!首先来看第一个问题:

1. 数据的种类

我们都知道,一般数据可以分为两类,即定性数据(类别型数据)定量数据(数值型数据)

(1). 定性数据,表示研究对象的类别。很好理解,这里的表示类别用的数字既没有大小之分,也没有先后顺序之分,更不能进行算术四则运算。

定性数据可以分为:

① 定类数据
表现为类别,但不区分顺序,是由定类尺度计量形成的。一般可以从非数值型数据中编码转换而来,数值本身没有意义,只是为了区分类别做出的数值型标识

例如性别用1代表男性,用2代表女性;血型用1,2,3,4来表示A、B、AB及O四种;

② 定序数据
表现为类别,但有顺序,是由定序尺度计量形成的。运算符也没有意义,

例如受教育程度用 文盲 = 1,半文盲 = 2,小学 = 3,初中 =4,高中 = 5,大专 = 6,本科 = 7,(研究生)硕士 = 8,(研究生)博士= 9表示。

(2). 定量数据,表示的是研究对象的数量特征,如人群中人的身高、体重等。

定量数据可以分为以下几种:

① 定距数据
表现为数值,可进行加、减运算,是由定距尺度计量形成的。定距数据的特征是没有绝对的零点,例如温度,不能说10摄氏度的一倍是20摄氏度。因此乘、除法对于定距数据来说也是没有意义的。

② 定比数据
表现为数值,可进行加、减、乘、除运算,是由定比尺度计量形成的。定比数据存在绝对的零点。例如价格,100元的2倍就是200元。

2. 什么是统计学

先看一个例子,这里有一组数据2,23,4,17,12,12,13,16,请思考你要怎么描述它?

你可能会说他们的平均数是12.375,中位数是12.5,最大值是23,最小值是2,等等。

没错,这里其实你已经在用平均数、中位数、最大值、最小值的来描述这组数据。

那么用几个数来描述一堆数就是统计学的基本概念:统计学是一门将数据汇总为统计量或图表的学问

Tips:通俗来说就是,数据太多记不住且不好描述,需要简化为更少的数字或图表,于是有了统计学和统计图表

知道了统计学的定义再接着看:

3. 统计学的知识体系是什么样的?

通常我们把统计学分为两大方向,通过计算出来的统计量来概括已有数据叫做描述统计学,通过样本获取总体特征的叫做推断统计学

Tips:“算”出来的统计量,比如中位数、平均值、众数这些;“猜”出来的叫推断统计学,比如通过样本数据来推断总体的数字特征。

下面这张图展示了统计学两大分支:描述统计与推断统计。其中推断统计又分两大学派,频率学派与贝叶斯学派。这些内容大家先知道就行,后面再展开。

下期预告:《Python统计学极简入门》第2节 描述性统计

2. 描述性统计

上一篇介绍了数据的分类、统计学是什么、以及统计学知识的大分类,本篇我们重点学习描述性统计学。

我们描述一组数据的时候,通常分三个方面描述:集中趋势、离散趋势、分布形状。通俗来说,集中趋势是描述数据集中在什么位置,离散趋势描述的是数据分散的程度,分布形状描述的是数据形状

首先,来看描述数据的集中趋势,使用的三个常见的统计量:

平均数

  • 算术平均数算术平均数是n个数求和后除以n得到的结果。广泛应用于各个领域,用于描述和分析数据的平均水平和集中趋势

Excel求算术平均数的函数=AVERAGE(A1:A8)

PS:聪明的你肯定知道把上面8个数据2,23,4,17,12,12,13,16,用左手复制到你Excel中的A1:A8单元格(记得竖着放!)

用Python求算术平均数

## 使用 numpy 库里的 mean 函数
import numpy as np
data =  [2,23,4,17,12,12,13,16]
print(np.mean(data))
# 12.375
  • 几何平均数几何平均数就是n个数乘积的n次方根。在金融财务、投资和银行业的问题中,几何平均数的应用尤为常见。当你任何时候想确定过去几个连续时期的平均变化率时,都能应用几何平均数。其他通常的应用包括物种总体、农作物产量、污染水平以及出生率和死亡率的变化。(在第8节案例8.1中会举例说明)。公式如下:

Excel求几何平均数的函数=GEOMEAN(A1:A8)

用Python求几何平均数

# 使用 scipy 库里的 gmean 函数求几何平均数
from scipy import stats as sts
data = [2,23,4,17,12,12,13,16]
print(sts.gmean(data))
# 9.918855683110795
  • 调和平均数

n个数的倒数的算术平均数的倒数

Excel求调和平均数的函数=HARMEAN(A1:A8)

Python求调和平均数

# 使用 scipy 库里的 hmean 函数求调和平均数
from scipy import stats as sts
data = [2,23,4,17,12,12,13,16]
print(sts.hmean(data))
# 6.906127821278071

还没看晕吧?我们小结一下,算数平均值 ≥ 几何平均值 ≥ 调和平均值数值类数据的均值一般用算数平均值,比例型数据的均值一般用几何平均值,平均速度一般用调和平均数

中位数

中位数是一组按大小顺序排列在一起的数据中位于中间位置的数。

Excel求中位数的函数=MEDIAN(A1:A8)

Python求中位数

# 使用 numpy 库里的 median 函数求中位数
import numpy as np
data =  [2,23,4,17,12,12,13,16]
print(np.median(data))
# 12.5

众数

众数是一组数据中出现次数最多的变量值。

Excel求众数的函数=MODE(A1:A8)

Python求众数

# 使用 scipy 库里的 mode 函数求众数
from scipy import stats as sts
data =  [2,23,4,17,12,12,13,16]
print(sts.mode(data))
# ModeResult(mode=array([12]), count=array([2]))

以上便是描述数据集中趋势的几个统计量,接下来我们来看描述数据离散趋势的统计量:

分位数

四分位数用3个分位数,将数据等分成4个部分。这3个四分位数,分别位于这组数据排序后的25%、50%和75%的位置上。另外,75%分位数与25%分位数的差叫做四分位距。

Excel求分位数的函数=QUARTILE(A1:A8,1),括号里面的参数:0代表最小值,1代表25%分位数,2代表50%分位数,3代表75%分位数,4代表最大值,

Python求该组数据的下四分位数与上四分位数

from scipy import stats as sts  
data =  [2,23,4,17,12,12,13,16]
print(sts.scoreatpercentile(data,25)) #25分位数
print(sts.scoreatpercentile(data,75)) #75分位数
10.0
16.25

补充一点,关于描述性统计部分的图表可视化,本系列教程不做展开,唯一值得一提的是箱线图,不论是描述数据、还是判断异常都是你应该掌握的数据分析利器(在第8节案例8.2中会详细举例说明)这里先简单举例如下

用四分位数绘制的箱线图

import seaborn as sns
data = [2,23,4,17,12,12,13,16]
# 使用sns.boxplot()函数绘制箱线图
sns.boxplot(data=data)

箱线图可以很直观地看到:数据的最大值、最小值、以及大部分数据集中在什么区间。

具体来说就是:异常值、上边缘Q3+1.5(Q3-Q1)、上四分位数Q3、中位数Q2下四分位数Q1、下边缘Q1-1.5(Q3-Q1)

  • 极差

极差又称范围误差或全距,是指一组数据中最大值与最小值的差

Excel求极差的函数=MAX(A1:A8) - MIN(A1:A8)

Python 求极差

import numpy as np
data =  [2,23,4,17,12,12,13,16]
print(np.ptp(data))
# 21
  • 四分位距

四分位距是上四分位数与下四分位数之差,一般用表示

Excel求分位数的函数=QUARTILE(A1:A8,3)-QUARTILE(A1:A8,1)Python 求四分位距

from scipy import stats as sts
data =  [2,23,4,17,12,12,13,16]
print(sts.scoreatpercentile(data,75)-sts.scoreatpercentile(data,25))
# 6.25

方差

方差是一组数据中的各数据值与该组数据算术平均数之差的平方的算术平均数。

Excel求方差的函数=VAR(A1:A8)

Python求方差

from scipy import stats 


    
as sts
data =  [2,23,4,17,12,12,13,16]
print(sts.tvar(data,ddof = 1))# ddof=1时,分母为n-1;ddof=0时,分母为n
#46.55357142857143

标准差

标准差为方差的开方。总体标准差常用σ表示,样本标准差常用S表示。Excel求方差的函数=STDEV(A1:A8)Python求标准差:

from scipy import stats as sts
data = [2,23,4,17,12,12,13,16]
print(sts.tstd(data,ddof = 1))# ddof=1时,分母为n-1;ddof=0时,分母为n
# 6.823017765517794

变异系数

对不同变量或不同数组的离散程度进行比较时,如果它们的平均水平和计量单位都相同,才能利用上述指标进行分析,否则需利用变异系数来比较它们的离散程度。

变异系数又称为离散系数,是一组数据中的极差、四分位差或标准差等离散指标与算术平均数的比率。

Excel求变异系数的函数=STDEV(A1:A8)/AVERAGE(A1:A8)

Python求标准差变异系数:

from scipy import stats as sts
data = [2,23,4,17,12,12,13,16]
print(sts.tstd(data)/sts.tmean(data))
# 0.5513549709509329

看完了描述数据离散程度的几个统计量,我们接着看描述数据分布形状的偏度和峰度:

偏度

偏度系数是对分布偏斜程度的测度,通常用SK表示。偏度衡量随机变量概率分布的不对称性,是相对于平均值不对称程度的度量。

当偏度系数为正值时,表示正偏离差数值较大,可以判断为正偏态或右偏态;反之,当偏度系数为负值时,表示负偏离差数值较大,可以判断为负偏态或左偏态。偏度系数的绝对值越大,表示偏斜的程度就越大。

Excel求偏度的函数=SKEW(A1:A8)

Python如何求偏度:

from scipy import stats as sts
data = [2,23,4,17,12,12,13,16]
print(sts.skew(data,bias=False)) # bias=False 代表计算的是总体偏度,bias=True 代表计算的是样本偏度
# -0.21470003988916822

峰度

峰度描述的是分布集中趋势高峰的形态,通常与标准正态分布相比较。在归化到同一方差时,若分布的形状比标准正态分布更“瘦”、更“高”,则称为尖峰分布;若比标准正态分布更“矮”、更“胖”,则称为平峰分布。

峰度系数是对分布峰度的测度,通常用K表示:

由于标准正态分布的峰度系数为0,所以当峰度系数大于0时为尖峰分布,当峰度系数小于0时为平峰分布。

Excel求峰度的函数=KURT(A1:A8)

Python如何求峰度:

from scipy import stats as sts
data = [2,23,4,17,12,12,13,16]
print(sts.kurtosis(data,bias=False)) # bias=False 代表计算的是总体峰度,bias=True 代表计算的是样本峰度
# -0.17282884047242897

下期预告:《Python统计学极简入门》第3节 数据分布


3. 数据分布

t分布、F分布和卡方分布是统计学中常用的三种概率分布,它们分别用于样本均值的推断、方差的比较和数据的拟合优度检验。

总之这3个分布很有用,首次接触你可能理解不了,但没关系你知道很重要就行了,接着往下看,我们在介绍三大分布之前,先看一下正态分布和标准正态分布:

正态分布(Normal Distribution)

正态分布也被称为高斯分布,是统计学中最常见的概率分布之一。

正态分布具有钟形曲线的特征,均值和标准差是其两个重要的参数。

import numpy as np
import seaborn as sns

mean = 3  # 均值
std = 4  # 标准差
size = 1000  # 生成1000个随机数

data = np.random.normal(mean, std, size=size)
sns.histplot(data, kde=True)

标准正态分布(Standard Normal Distribution)

标准正态分布是一种特殊的正态分布,其均值为0,标准差为1。在统计学中,标准正态分布经常用于标准化数据或进行假设检验。

import numpy as np
import seaborn as sns

size = 1000  # 生成1000个随机数

data = np.random.standard_normal(size=size)
sns.histplot(data, kde=True)

t分布(t Distribution)

t分布是一种概率分布,用于小样本情况下对总体均值的推断。当样本容量较小或总体方差未知时,使用T分布进行推断更准确。T分布的形状类似于正态分布,但尾部较宽。T分布的自由度(degreesof freedom)决定了其形状。

import numpy as np
import seaborn as sns

df = 10  # 自由度
size = 1000  # 生成1000个随机数

data = np.random.standard_t(df, size=size)
sns.histplot(data, kde=True)

F分布(F Distribution)

F分布是一种概率分布,用于比较两个样本方差的差异。F分布常用于方差分析和回归分析中。F分布的形状取决于两个自由度参数,分子自由度和分母自由度。

import numpy as np
import seaborn as sns

dfn = 5  # 分子自由度
dfd = 10  # 分母自由度
size = 1000  # 生成1000个随机数

data = np.random.f(dfn, dfd, size=size)
sns.histplot(data, kde=True)

卡方分布(Chi-Square Distribution)

卡方分布是一种概率分布,用于检验观察值与理论值之间的拟合优度。卡方分布常用于拟合优度检验、独立性检验和方差分析中。卡方分布的自由度参数决定了其形状。

import numpy as np
import seaborn as sns

df = 5  # 自由度
size = 1000  # 生成1000个随机数

data = np.random.chisquare(df, size)
sns.histplot(data, kde=True)

番外篇:三大分布互相推导

注:本节作为延伸阅读,初学者简单了解即可

十九世纪中叶至二十世纪初,有三位统计学届杰出代表:皮尔逊( Pearson) 、戈塞特( Gosset) 、费希尔( Fisher)表,他们是统计学三大分布的始创者。

  • 皮尔逊(Pearson)在创立拟合优度理论的过程中发现了分布;

  • 戈塞特( Gosset)发现分布的过程正是小样本理论创立的过程;

  • 费希尔( Fisher)在创立方差分析理论的过程中发现了分布。

这便是著名的三大抽样分布包括:分布、分布和 分布

分布是由个相互独立的标准正态分布的平方和确定的分布,记作~,即

分布的分子是一个,分母是自由度为分布与自由度的比值再开方确定的分布,记作~,即

分布是由两个分布与其自由度比值的比值确定的分布 ,记 作~,即

三大分布的推导

三大分布的推导例题

下期预告:《Python统计学极简入门》第4节 区间估计


4. 区间估计

还以为你被上节课的内容唬住了~终于等到你,还好没放弃!

本节我们将说明两个问题:总体均值的区间估计总体比例的区间估计

区间估计经常用于质量控制领域来检测生产过程是否正常运行或者在“控制之中” ,也可以用来监控互联网领域各类数据指标是否在正常区间。

一个总体均值的区间估计

  • 大样本的情况下

    • 已知,

    • 未知,

  • 小样本的情况下

    • 已知,
    • 未知,

另外补充一个公式,样本量这个了解就好,大部分情况下是不缺数据的,尽可能选数据量稍大些的数据。

把以上过程编写成Python的自定义函数:

import numpy as np
import scipy.stats
from scipy import stats as sts


def mean_interval(mean=None, sigma=None,std=None,n=None,confidence_coef=0.95):
    """
    mean:样本均值
    sigma: 总体标准差
    std: 样本标准差
    n:   样本量
    confidence_coefficient:置信系数
    confidence_level:置信水平 置信度
    alpha:显著性水平
    功能:构建总体均值的置信区间
    """

    alpha = 1 - confidence_coef
    z_score = scipy.stats.norm.isf(alpha / 2)            # z分布临界值
    t_score = scipy.stats.t.isf(alpha / 2, df = (n-1) )  # t分布临界值
   
    if n >= 30
        if sigma != None:
            me = z_score * sigma / np.sqrt(n)
            print("大样本,总体 sigma 已知:z_score:",z_score)
        elif sigma == None:
            me = z_score * std / np.sqrt(n)
            print("大样本,总体 sigma 未知 z_score",z_score)
        lower_limit = mean - me
        upper_limit = mean + me
    if n 30 :
        if sigma != None:
            me = z_score * sigma / np.sqrt(n)
            print("小样本,总体 sigma 已知 z_score * sigma / np.sqrt(n) \n z_score = ",z_score)
        elif sigma == None:
            me = t_score * std / np.sqrt(n)
            print("小样本,总体 sigma 未知 t_score * std / np.sqrt(n) \n t_score = ",t_score)
            
        print("t_score:",t_score)
        lower_limit = mean - me
        upper_limit = mean + me
    
    return (round(lower_limit, 1), round(upper_limit, 1))
应用:网站流量UV区间估计:

某网站流量UV数据如下[52,44,55,44,45,59,50,54,62,46,54,42,60,62,43,42,48,55,57,56],我们研究一下该网站的总体流量uv均值:

先把数据放进来

import numpy as np
data = np.array([52,44,55,44,45,59,50,54,62,46,54,42,60,62,43,42,48,55,57,56])

计算一下均值为:

x_bar = data.mean()
x_bar
# 51.5

样本标准差为:



    
x_std = sts.tstd(data,ddof = 1) #  ddof=1时,分母为n-1;ddof=0时,分母为n
x_std
# 6.840283158189472
mean_interval(mean=x_bar, sigma=None,std= x_std,  n=n, confidence_coef=0.95)

输出结果:

小样本,总体 sigma 未知 t_score * std / np.sqrt(n) 
t_score =  2.093024054408263
(48.3, 54.7)

于是我们有95%的把握,该网站的流量uv介于 [48, 55]之间。

值得一提的是,上面这个案例的数据是实际上是公众号山有木兮水有鱼的按天统计阅读量……有人可能要说了,你这数据也太惨了,而且举个案例都是小样本。我想说,小样本的原因是这新号一共发了也没几天,至于数量低,你帮忙动动小手转发转发,这数据也就高了~希望下次举例的时候这个能变成大样本,均值怎么着也得个千儿八百的,感谢感谢!

一个总体比例的区间估计

其中样本量

def proportion_interval(p=None, n=None, confidence_coef =0.95):
    """
    p: 样本比例
    n: 样本量
    confidence_coef: 置信系数
    功能:构建总体比例的置信区间
    """

    alpha = 1 - confidence_coef
    z_score = scipy.stats.norm.isf(alpha / 2)  # z分布临界值
    
    me = z_score * np.sqrt((p * (1 - p)) / n) 
    lower_limit = p - me
    upper_limit = p + me
    
    return (round(lower_limit, 3), round(upper_limit, 3))

下期将为大家带来《Python统计学极简入门》之假设检验


5. 假设检验

久经考场的你肯定对于很多概念类题目里问到的“区别和联系”不陌生,与之类似,在统计领域要研究的是数据之间的区别和联系,也就是差异性分析相关性分析。本节我们重点关注数据的差异性分析

我们知道,比较两个数之间的大小,要么前后两者求差,要么求比。差值大于零说明前者大于后者。比值大于1说明分子大于分母。

那么如何比较两组数据的差异性呢?大道至简,其实和上面原理类似

我们先从简单的看起,先比较一组数和一个给定数的差异,即,单个总体的差异性分析:

单个总体的假设检验

常见的单个总体差异性的假设检验分为3个类型:均值、比例、方差

一个总体均值的假设检验 (指定值和样本均值)

顾名思义,就是检验指定值与样本均值的差异,按是否已知可以分2种情况:

已知的情况:检验

接下来我们用代码举例实现一下你就明白怎么用了:

例5.1 检验一批厂家生产的红糖是否够标重

监督部门称了50包标重500g的红糖,均值是498.35g,少于所标的500g。对于厂家生产的这批红糖平均起来是否够份量,需要统计检验。

分析过程:由于厂家声称每袋500g,因此原假设为总体均值等于500g(被怀疑对象总是放在零假设)。而且由于样本均值少于500g(这是怀疑的根据),把备择假设设定为总体均值少于500g (上面这种备选假设为单向不等式的检验称为单侧检验,而备选假设为不等号“”的称为双侧检验,后面会解释)

于是我们有了原假设和备择假设

:

引入相关库、读取数据如下

from scipy import stats
import scipy.stats
import numpy as np
import pandas  as pd
import statsmodels.stats.weightstats

data = [493.01,498.83,494.16,500.39,497.63,499.72,493.41,498.97,501.94,503.45,497.47,494.19,500.99,495.81,499.63,494.91,498.90,502.43,491.34,497.50,505.95,496.56,501.66,492.02,497.68,493.48,505.40,499.21,505.84,499.41,505.65,500.51,489.53,496.55,492.26,498.91,496.65,496.38,497.16,498.91,490.98,499.97,501.21,502.85,494.35,502.96,506.21,497.66,504.66,492.11]

进行z检验:

z, pval = statsmodels.stats.weightstats.ztest(data, value=500,alternative = 'smaller')

# 'two-sided': 样本均值与给定的总体均值不同
# 'larger' :   样本均值小于给定总体均值
# 'smaller' :  样本均值大于给定总体均值
print(z,pval)
# -2.6961912076362085 0.0035068696715304876

结论:选择显著性水平 0.05 的话,P=0.0035 < 0.05, 故应该拒绝原假设。具体来说就是该结果倾向于支持平均重量小于500g的备则假设。

未知的情况:检验

例5.2 检验汽车实际排放是否低于其声称的排放标准

汽车厂商声称其发动机排放标准的一个指标平均低于20个单位。在抽查了10台发动机之后,得到下面的排放数据:17.0 21.7 17.9 22.9 20.7 22.4 17.3 21.8 24.2 25.4该样本均值为21.13.究竟能否由此认为该指标均值超过20?

分析过程:由于厂家声称指标平均低于20个单位,因此原假设为总体均值等于20个单位(被怀疑对象总是放在零假设)。而且由于样本均值大于20(这是怀疑的根据),把备择假设设定为总体均值大于20个单位

于是我们有了原假设和备择假设

:

读取数据如下

data = [17.021.717.922.920.722.417.3

21.824.225.4]

进行t检验如下:

import scipy.stats
t, pval = scipy.stats.ttest_1samp(a = data, popmean=20,alternative = 'greater')
# 说明  
# a  为给定的样本数据
# popmean 为给定的总体均值
# alternative 定义备择假设。以下选项可用(默认为“two-sided”):
# ‘two-sided’:样本均值与给定的总体均值(popmean)不同
# ‘less’:样本均值小于给定总体均值(popmean)
# ‘greater’:样本均值大于给定总体均值(popmean)

print(t, pval)

# '''
# P= 0.004793 < 5%, 拒绝原假设,接受备择假设样本
# '''

结论:选择显著性水平 0.01 的话,P=0.1243 > 0.05, 故无法拒绝原假设。具体来说就是该结果无法支持指标均值超过20的备则假设。

一个总体比例的假设检验(指定比例和样本比例)

例5.3 检验高尔夫球场女性球员比例是否因促销活动而升高

某高尔夫球场去年打球🏌🏻‍的人当中有20%是女性,为了增加女性球员的比例,该球场推出了一项促销活动来吸引更多的女性参加高尔夫运动,在活动实施了1个月后,球场的研究者想通过统计分析研究确定高尔夫球场的女性球员比例是否上升,收集到了400个随机样本,其中有100是女性

分析过程:由于研究的是女性球员所占的比例是否上升,因此选择上侧检验比较合适,备择假设是比例大于20%

:

方法1:用statsmodels.stats.proportion里面的proportions_ztest函数计算(推荐)

import numpy as np
from statsmodels.stats.proportion import proportions_ztest

count = 100
nobs = 400
p_0 = 0.2


p_bar = count/nobs
p_0 = 0.2
n = 400

# 执行单一样本比例检验 statsmodels.stats.proportion.proportions_ztest
z_statistic, p_value = proportions_ztest(count, nobs, value = p_0,alternative='larger',prop_var = value)
# 注:statsmodels.stats.proportion.proportions_ztest 的函数有几个问题:讲在第八节之后说明,感兴趣的读者请持续关注

# 打印结果
print("z统计量:", z_statistic)
print("p值:", p_value)
#z统计量:2.4999999999999996
#p值: 0.006209665325776138

方法2 用手动方式计算

count = 100
nobs = 400
p_0 = 0.2


p_bar = count/nobs
p_0 = 0.2
n = 400

def calc_z_score(p_bar, p_0, n):
    z = (p_bar - p_0) / (p_0 * (1 - p_0) / n)**0.5
    return z

z = calc_z_score(p_bar, p_0, n)
p = stats.norm.sf(z)


# 打印结果
print("z统计量:", z)
print("p值:", p)
# z统计量:2.4999999999999996
# p值: 0.006209665325776138

结论:选择显著性水平 0.05 的话,P=0.0062 < 0.05, 拒绝原假设。具体来说就是该结果支持特定的促销活动能够提升该球场女性运动员比例的备则假设。

一个总体方差的假设检验(指定方差和样本方差)

import numpy as np
from scipy import stats

def chi2test(sample_var, sample_num,sigma_square,side, alpha=0.05):
    '''
    参数:
    sample_var--样本方差
    sample_num--样本容量
    sigma_square--H0方差
    返回值:
    pval
    '
''
    chi_square =((sample_num-1)*sample_var)/(sigma_square)
    p_value = None
    if side == 'two-sided':
        p = stats.chi2(df=sample_num-1).cdf(chi_square)
        p_value = 2*np.min([p, 1-p])
    elif side == 'less':
        p_value = stats.chi2(df=sample_num-1).cdf(chi_square)
    elif side == 'greater':
        p_value = stats.chi2(df=sample_num-1).sf(chi_square)
    return chi_square,p_value

例5.4 检验公交车到站时间的方差是否比规定标准大

某市中心车站为规范化提升市民对于公交车到站时间的满意度,对于公交车的到站时间管理做了规定,标准是到站时间的方差不超过4。为了检验时间的到站时间的方差是否过大,随机抽取了24辆公交车的到站时间组成一个样本,得到的样本方差是,假设到站时间的总体分布符合正态分布,请分析总体方差是否过大。

分析过程:由于研究的是方差是否过大,因此选择上侧检验比较合适,备择假设是方差大于4

于是我们有了原假设和备择假设

:

chi_square,p_value = chi2test(sample_var = 4.9, sample_num = 24, sigma_square = 4,side='greater')

print("p值:", p_value)
# p值: 0.2092362676676498

结论:选择显著性水平 0.05 的话,P=0.2092 > 0.05, 无法拒绝原假设。具体来说就是该结果不支持方差变大的备则假设。

例5.5 检验某考试中心升级题库后考生分数的方差是否有显著变化

某数据分析师认证考试机构CDA考试中心,历史上的持证人考试分数的方差为,现在升级了题库,该考试中心希望新型考题的方差保持在原有水平上,为了研究该问题,收集到了30份新考题的考分组成的样本,样本方差是,在的显著性水平下进行假设检验。

分析过程:由于目标是希望考试分数的方差保持原有水平,因此选择双侧检验

于是我们有了原假设和备择假设

:

p_value = chi2test(sample_var = 162, sample_num = 30, sigma_square = 100,side='two-sided')

print("p值:", p_value)
# p值: 0.07213100536907469

结论:选择显著性水平 0.05 的话,P=0.0721 > 0.05, 故无法拒绝原假设。具体来说就是不支持方差发生了变化的备则假设。

两个总体的假设检验

常见的两总体差异性的假设检验也分3个类型:均值、比例、方差

两总体均值之差的假设检验(独立样本)

例5.6(数据:drug.txt) 检验某药物在实验组的指标是否低于对照组

为检测某种药物对情绪的影响,对实验组的100名服药者和对照组的150名非服药者进行心理测试,得到相应的某指标。需要检验实验组指标的总体均值是否大于对照组的指标的总体均值。这里假定两个总体独立地服从正态分布。相应的假设检验问题为:

分析过程:由于目标是检验实验组指标的总体均值 是否大于对照组的指标的总体均值,因此选择上侧检验

于是我们有了原假设和备择假设

:

data = pd.read_table("./t-data/drug.txt",sep = ' ')
data.sample(5)
ahid
4.42
6.82
9.62
4.82
13.21
a = data[data['id']==1]['ah']
b = data[data['id']==2]['ah']
'''
H0: 实验组的均值等于对照组
H1: 实验组的均值大于对照组

'''

t, pval = scipy.stats.ttest_ind(a,b,alternative = 'greater')
print(t,pval)
# 0.9109168350628888 0.18161186154576608

结论:选择显著性水平 0.05 的话,p = 0.1816 > 0.05,无法拒绝H0,具体来说就是该结果无法支持实验组均值大于对照组的备则假设。

两总体均值之差的假设检验(配对样本)

例5.7(数据: diet.txt) 检验减肥前后的重量是否有显著性差异(是否有减肥效果)

这里有两列50对减肥数据。其中一列数据(变量名before)是减肥前的重量,另一列(变量名after)是减肥后的重量(单位: 公斤),人们希望比较50个人在减肥前和减肥后的重量。

分析过程:这里不能用前面的独立样本均值差的检验,这是因为两个样本并不独立。每一个人减肥后的重量都和自己减肥前的重量有关,但不同人之间却是独立的,所以应该用配对样本检验。同时,由于研究的是减肥前后的重量变化,期望减肥前的重量大于减肥后的重量,所以备择假设是期望减肥前的重量大于减肥后的重量

于是我们有了原假设和备择假设:

:

data = pd.read_table("./t-data/diet.txt",sep = ' ')
data.sample(5)
beforeafter
5850
7671
6965
6876
8175
a = data['before']
b = data['after']
stats.ttest_rel(a, b,alternative = 'greater')
# Ttest_relResult(statistic=3.3550474801424173, pvalue=0.000769424325484219)

结论选择显著性水平 0.05 的话,p = 0.0007 < 0.05,故应该拒绝原假设。具体来说就是该结果倾向支持减肥前后的重量之差大于零(即减肥前重量大于减肥后,也就是有减肥效果)的备则假设。

两总体比例之差的假设检验

import numpy as np
import scipy.stats as stats

def proportion_test(p1, p2, n1, n2, side='two-sided'):
    """
    参数:
    p1: 样本1的比例
    p2: 样本2的比例
    n1: 样本1的数量
    n2: 样本2的数量
    side: 假设检验的方向,可选'two-sided'(双侧检验,默认), 'greater'(右侧检验), 'less'(左侧检验)

    返回值:
    z_value: Z统计量的值
    p_value: 对应的p值
    "
""
    p = (p1 * n1 + p2 * n2) / (n1 + n2)
    se = np.sqrt(p * (1 - p) * (1 / n1 + 1 / n2))
    z_value = (p1 - p2) / se

    if side == 'two-sided':
        p_value = 2 * (1 - stats.norm.cdf(np.abs(z_value)))
    elif side == 'greater':
        p_value = 1 - stats.norm.cdf(z_value)
    elif side == 'less':
        p_value = stats.norm.cdf(z_value)
    else:
        raise ValueError("Invalid side value. Must be 'two-sided', 'greater', or 'less'.")

    return z_value, p_value

例5.8 检验不同保险客户的索赔率是否存在差异

某保险公司抽取了单身与已婚客户的样本,记录了他们在一段数据内的索赔次数,计算了索赔率,现在需要检验两种保险客户的索赔率是否存在差异

分析过程:由于目标比例是否有差异,因此选择比例之差的双侧检验

于是我们有了原假设和备择假设

:

p1 = 0.14
p2 = 0.09
n1 = 250
n2 = 300

z_value, p_value = proportion_test(p1, p2, n1, n2, side='two-sided')
# 选择双侧检验 alternative = 'two-sided'

print("Z_value:", z_value)
print("p_value:", p_value)
# Z_value: 1.846189280616294
# p_value: 0.0648647268570739

结论选择显著性水平 0.05 的话,p = 0.0648 > 0.05,故应该拒绝原假设。具体来说就是该结果倾向支持两种保险客户的索赔率存在差异的备则假设。

两总体方差之比的假设检验

import numpy as np
from scipy import stats

def f_test_by_s_square(n1, n2, s1_square,s2_square, side ='two-sided'):
    """
    参数
    n1 :样本1的数量
    n2 :样本2的数量
    s1_square:样本1的方差
    s2_square:样本2的方差
    # 
    # F_value :F统计量的值
    # p_value :对应的p值
    "
""
    F_value = s1_square/s2_square
    F = stats.f(dfn = n1-1, dfd = n2-1)
    if side=='two-sided':
        print("two-sided")
        p_value = 2*min(F.cdf(F_value), 1-F.cdf(F_value))
        return F_value,p_value
    elif  side=='greater':
        print("greater")
        p_value = 1-F.cdf(F_value)
        return F_value,p_value

例5.9 检验不同公交公司的校车到达时间的方差是否有差异

某学校的校车合同到期,先需要在A、B两个校车供应公司中选择一个,才有到达时间的方差作为衡量服务质量的标准,较低方差说明服务质量稳定且水平较高,如果方差相等,则会选择价格更低的公司,,如果方差不等,则优先考虑方差更低的公司。现收集到了A公司的26次到达时间组成一个样本,方差68,B公司16次到达时间组成一个样本,方差是30,请检验AB两个公司的到达时间方差。

分析过程:由于目标是希望的方差保持原有水平,因此选择双侧检验。两总体方差之比用F检验,将方差较大的A视为总体1

于是我们有了原假设和备择假设

:

f_statistic , p_value= f_test_by_s_square(n1=26, n2=16,s1_square=78,s2_square=20,side='two-sided')
# 选择双侧检验所以side='two-sided'
# 打印检验结果
print("F statistic:", f_statistic)
print("p-value:", p_value)
#two-sided
#F statistic: 3.9
#p-value: 0.00834904415829052

结论选择显著性水平 0.05 的话,p = 0.0083 < 0.05,故拒绝原假设。结果倾向支持AB两个公司的到达时间方差存在差异的备则假设。

例5.10 检验修完Python课程的学生是否比修完数据库课程的学生考CDA的成绩方差更大

某高校数据科学专业的学生,修完一门数据库课程的41名学生考CDA的方差,修完Python课程的31名学生考CDA的方差是,这些数据是否表明,修完数据库的学生要比修完Python的学生CDA成绩的方差更大?

分析过程:由于目标是希望修完Python的学生CDA成绩的方差更大,因此选择上侧检验。两总体方差之比用F检验,将方差较大的数据库课程的考试成绩视为总体1

于是我们有了原假设和备择假设

:

f_statistic , p_value= f_test_by_s_square(n1=41, n2=31,s1_square=120,s2_square=80,side='greater')# 打印检验结果
# 选择上侧检验所以side='greater'
print("F statistic:", f_statistic)
print("p-value:", p_value)

结论 选择显著性水平 0.05 的话,p = 0.1256 > 0.05,故无法原假设。结果无法支持修完数据库的学生要比修完Python的学生CDA成绩的方差更大的备则假设。

关于知识的学习,你会发现有很多相似的逻辑,抓住问题的本质去理解的话就没那么复杂了,比如概念题里面的区别和联系延伸到数据分析里的差异性和相关性;再比如计算机数据结构里的树、森林、网络到机器学习里面的决策树、随机森林、神经网络;再比如从互联网、区块链到元宇宙,都是想通过技术的手段去刻画客观世界;算法应用里面的图像识别、语音识别,替代人的眼耳鼻舌身意中的前二者去感知世界。抓住了问题的本质不仅可以帮助我们理解知识,还可以将一个领域的知识或模型迁移到另一个领域加以创新和应用。

假设检验背后的故事:统计学史上最著名的女士品茶

下期将为大家带来《统计学极简入门》之方差分析

6. 方差分析

单因素多水平方差分析

例6.1 不同装配方式对生产的过滤系统数量的差异性检验

某城市过滤水系统生产公司,有A、B、C3种方式进行过滤水系统的装配,该公司为了研究三种装配方式生产的过滤系统数量是否有差异,从全体装配工人中抽取了15名工人,然后随机地指派一种装配方式,这样每个装配方式就有5个工人。在指派装配方法和培训工作都完成后,一周内对每名工人的装配过滤系统数量进行统计如下:

方法A方法B方法C
585848
646957
557159
666447
676849

请根据数据判断3种装配方式有无差异

分析过程:由于目标是判断3种装配方式有无差异,多样本的检验用方差分析

于是我们有了原假设和备择假设

:均值不全相等

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

# 数据
A = [58,64,55,66,67]
B = [58,69,71,64,68]
C = [48,57,59,47,49]

data = [A, B, C]
# 方差的齐性检验
w, p = stats.levene(*data)
if p     print('方差齐性假设不成立')
 
 
# 成立之后, 就可以进行单因素方差分析
f_value, p_value = stats.f_oneway(*data)
# 输出结果
print("F_value:", f_value)
print("p_value:", p_value)
F_value: 9.176470588235295
p_value: 0.0038184120755124806

结论选择显著性水平 0.05 的话,p = 0.0038 < 0.05,故拒绝原假设。支持三种装配方式装配数量均值不全相等的备则假设。

例6.2 不同优惠金额对购买转化率的差异性检验

某公司营销中心为了提升销量,针对某产品设计了3种不同金额的优惠,想测试三种优惠方式对于用户的购买转化率是否有显著影响,先收集到了三种不同方式在6个月内的转化率数据

请根据数据判断3种不同优惠金额的转化率有无差异

优惠A优惠B优惠C
0.0430.050.048
0.0470.0480.05
0.0510.0450.047
0.0490.0550.056
0.0450.0480.054
0.04690.04910.0509

分析过程:由于目标是判断3种不同金额的优惠券对于转化率有无差异,多样本的检验用方差分析

于是我们有了原假设和备择假设

:认为这几组之间的购买率不一样

P < 0.05 拒绝原假设,倾向于支持不同优惠金额购买率不一样的备择假设。认为不同优惠金额会对购买率产生影响 P > 0.05无法拒绝原假设。认为不同优惠金额不会对购买率产生影响

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

A = [0.043 , 0.047 , 0.051 , 0.049 , 0.045 , 0.0469]
B = [0.05  , 0.048 , 0.045 , 0.055 , 0.048 , 0.0491]
C = [0.048 , 0.05  , 0.047 , 0.056 , 0.054 , 0.0509]
data = [A, B, C]
# 方差的齐性检验
w, p = stats.levene(*data)
if p     print('方差齐性假设不成立')
 
 
# 成立之后, 就可以进行单因素方差分析
f_value, p_value = stats.f_oneway(*data)
# 输出结果
print("F_value:", f_value)
print("p_value:", p_value)

# F_value: 2.332956563862427
# p_value: 0.13116820340181937

结论选择显著性水平 0.05 的话,p = 0.1311 > 0.05,故无法拒绝原假设。认为不同优惠金额不会对购买率产生影响

双因素方差分析

1.双因素方差分析(等重复实验)

这里的等重复实验,意思就是针对每个组合做大于等于两次的实验,比如下方例子中表里A1和B1的组合里面有2个数字,即说明做了两次实验,如果是3个数字则说明3次实验,依次类推。

例6.3 不同燃料种类和推进器的火箭射程差异性检验

火箭的射程与燃料的种类和推进器的型号有关,现对四种不同的燃料与三种不同型号的推进器进行试验,每种组合各发射火箭两次,测得火箭的射程如表(以海里计)(设显著性水平为0.05)

燃料B1B2B3
A158.2 , 52.656.2 , 41.265.3 , 60.8
A249.1 , 42.854.1 , 50.551.6 , 48.4
A360.1 , 58.370.9 , 73.239.2 , 40.7
A475.8 , 71.558.2 , 51.048.7 , 41.0
import numpy as np
import pandas as pd 

d = np.array([[58.2, 52.6, 56.2, 41.2, 65.3, 60.8],
    [49.1, 42.8, 54.1, 50.5, 51.6, 48.4],
    [60.1, 58.3, 70.9, 73.2, 39.2, 40.7],
    [75.8, 71.5, 58.2, 51.0, 48.7,41.4]
])
data = pd.DataFrame(d)
data.index=pd.Index(['A1','A2','A3','A4'],name='燃料')
data.columns=pd.Index(['B1','B1','B2','B2','B3','B3'],name='推进器')

# pandas宽表转长表
data = data.reset_index().melt(id_vars =['燃料'])
data = data.rename(columns={'value':'射程'})
data.sample(5)
燃料推进器射程
A2B348.4
A3B273.2
A3B339.2
A4B171.5
A2B254.1
import statsmodels.api as sm
from statsmodels.formula.api import ols

# 进行双因素方差分析
model = ols('射程~C(燃料) + C(推进器)+C(燃料):C(推进器)', data =data).fit()
# 打印方差分析表
anova_table = sm.stats.anova_lm(model, typ=2)
anova_table

sum_sqdfFPR(>F)
C(燃料)261.67534.41739 0.025969
C(推进器)370.98129.39390.00350603
C(燃料):C(推进器)1768.69614.92886.15115e-05
Residual236.9512nannan

结论:

对燃料因素来说,其p = 0.0259 < 0.05 所以拒绝,认为燃料对射程影响显著;

对推进器因素来说,其p = 0.0035 < 0.05,所以拒绝,认为推进器对射程影响显著;

对燃料和推进器的交互因素来说,其p = 0.000062< 0.05,所以拒绝,认为交互因素其对射程影响显著。

2.双因素方差分析(无重复实验)

在等重复实验中,我们为了检验实验中两个因素的交互作用,针对每对组合至少要做2次以上实验,才能够将交互作用与误差分离开来,在处理实际问题时候,如果我们一直不存在交互作用,或者交互作用对实验指标影响极小,则可以不考虑交互作用,此时每对组合只做一次实验,类似下方例子中的表中数据:

例6.3 不同时间、不同地点颗粒状物含量差异性检验 无重复实验

下面给出了在5个不同地点、不同时间空气中的颗粒状物(单位:mg/m°)含 量的数据记录于表中,试在显著性水平下检验不同时间、不同地点颗粒状物含量有无显著差异?(假设两者没有交互作用〉

因素B -地点
因素A - 时间





1995年10月7667815651
1996年01月8269965970
1996年05月6859675442
1996年08月6356645837


    
import numpy as np
import pandas as pd 

d = np.array([
    [76,67,81,56,51],
    [82,69,96,59,70],
    [68,59,67,54,42],
    [63,56,64,58,37]])
data = pd.DataFrame(d)
data.index=pd.Index(['1995年10月','1996年01月','1996年05月','1996年08月'],name='时间')
data.columns=pd.Index(['B1','B2','B3','B4','B5'],name='地点')
# pandas宽表转长表
data = data.reset_index().melt(id_vars =['时间'])
data = data.rename(columns={'value':'颗粒状物含量'})
data.sample(5)

随机查看5条转化后的数据:

时间地点颗粒状物含量
1996年05月B454
1995年10月B456
1996年05月B367
1996年01月B269
1996年01月B396
import statsmodels.api as sm
from statsmodels.formula.api import ols

# 进行双因素方差分析
model = ols('颗粒状物含量~C(时间) + C(地点)', data =data).fit()
# 打印方差分析表
anova_table = sm.stats.anova_lm(model, typ=2)
anova_table

sum_sqdfFPR(>F)
C(时间)1182.95310.72240.00103293
C(地点)1947.5413.23930.000234184
Residual441.312nannan

结论:

对时间因素来说,其p = 0.001033 < 0.05 所以拒绝 ,认为时间对颗粒状物含量影响显著;

对地点因素来说,其p = 0.000234 < 0.05,所以拒绝,认为地点对颗粒状物含量影响显著;

致敬:数理统计的大半江山的创造者——费希尔

下期将为大家带来《统计学极简入门》之相关分析

7. 相关性分析

前面的假设检验、方差分析基本上都是围绕差异性分析,不论是单个总体还是两个总体及以上,总之都是属于研究“区别”,从本节开始,我们关注“联系”,变量之间的关系分为函数关系和相关关系。本节这里重点探讨的是不同类型变量之间的相关性,千万记住一点相关性不代表因果性。除表中列出的常用方法外,还有Tetrachoric、相关系数等。

变量类型变量类型相关系数计算方法示例
连续型变量连续型变量Pearson(正态)/Spearman(非正态)商品曝光量和购买转化率
二分类变量(无序)连续型变量Point-biserial性别和疾病指数
无序分类变量连续型变量方差分析不同教育水平的考试成绩
有序分类变量连续型变量连续指标离散化后当做有序分类商品评分与购买转化率
二分类变量二分类变量数学公式:检验 联合 Cramer's V性别和是否吸烟
二分类变量(有序)连续型变量Biserial乐器练习时间与考级是否通过
无序分类变量无序分类变量数学公式:检验 / Fisher检验手机品牌和年龄段
有序分类变量无序分类变量数学公式:检验满意度和手机品牌
有序分类变量有序分类变量Spearman /Kendall Tau相关系数用户等级和活跃程度等级

连续型变量 vs 连续型变量  : Pearson / Spearmanr

Pearson

Pearson相关系数度量了两个连续变量之间的线性相关程度;

import random 
import numpy as np
import pandas as pd

np.random.seed(10)
df = pd.DataFrame({'商品曝光量':[1233,1333,1330,1323,1323,1142,1231,1312,1233,1123],
     '购买转化率':[0.033,0.034,0.035,0.033,0.034,0.029,0.032,0.034,0.033,0.031]})
df
  • Pandas计算Pearson相关系数
pd.Series.corr(df['商品曝光量'], df['购买转化率'],method = 'pearson'# pearson相关系数
# 0.885789300493948
  • scipy计算Pearson相关系数
import scipy.stats as stats

# 假设有两个变量X和Y
X = df['商品曝光量']
Y = df['购买转化率']

# 使用spearmanr函数计算斯皮尔曼相关系数和p值
corr, p_value = stats.pearsonr(X, Y)

print("Pearson相关系数:", corr)
print("p值:", p_value)
# Pearson相关系数: 0.8857893004939478
# p值: 0.0006471519603654732

Spearman等级相关系数

Spearman等级相关系数可以衡量非线性关系变量间的相关系数,是一种非参数的统计方法,可以用于定序变量或不满足正态分布假设的等间隔数据;

import random 
import numpy as np
import pandas as pd

np.random.seed(10)
df = pd.DataFrame({'品牌知名度排位':[9,4,3,6,5,8,1,7,10,2],
     '售后服务质量评价排位':[8,2,5,4,7,9,1,6,10,3]})
df
  • Pandas计算spearman相关系数
pd.Series.corr(df['品牌知名度排位'], df['售后服务质量评价排位'],method = 'spearman'# spearman秩相关
# 0.8787878787878788
  • scipy计算spearman相关系数
import scipy.stats as stats

# 假设有两个变量X和Y
X = df['品牌知名度排位']
Y = df['售后服务质量评价排位']

# 使用spearmanr函数计算斯皮尔曼相关系数和p值
corr, p_value = stats.spearmanr(X, Y)

print("斯皮尔曼相关系数:", corr)
print("p值:", p_value)
# 斯皮尔曼相关系数: 0.8787878787878788
# p值: 0.0008138621117322101

结论:p = 0.0008<0.05,表明两变量之间的正向关系很显著。

二分类变量(自然)vs 连续型变量 :Point-biserial

假设我们想要研究性别对于某种疾病是否存在影响。我们有一个二元变量“性别”(男、女)和一个连续型变量“疾病指数”。我们想要计算性别与疾病指数之间的相关系数,就需要用到Point-biserial相关系数。




    
import scipy.stats as stats

# 创建一个列表来存储数据
gender = [0, 1, 0, 1, 1, 0]
disease_index = [3.2, 4.5, 2.8, 4.0, 3.9, 3.1]

# 使用pointbiserialr函数计算Point-biserial相关系数和p值
corr, p_value = stats.pointbiserialr(gender, disease_index)

print("Point-biserial相关系数:", corr)
print("p值:", p_value)
# Point-biserial相关系数: 0.9278305692406299
# p值: 0.007624695507848026

结论:p = 0.007<0.05,表明两变量之间的正向关系很显著。即性别与疾病指数正相关

无序分类变量 vs 连续型变量 :ANOVA

假设我们想要比较不同教育水平的学生在CDA考试成绩上是否存在显著差异。我们有一个无序分类变量“教育水平”(高中、本科、研究生)和一个连续型变量“考试成绩”。

import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols

# 创建一个DataFrame来存储数据
data = pd.DataFrame({
    '教育水平': ['高中''本科''本科''研究生''高中''本科''研究生'],
    '考试成绩': [80, 90, 85, 95, 75, 88, 92]
})

# 使用ols函数创建一个线性模型
model = ols('考试成绩 ~ C(教育水平)', data=data).fit()

# 使用anova_lm函数进行方差分析
anova_table = sm.stats.anova_lm(model, typ=2)
anova_table

结论:p = 0.0102<0.05,拒绝原假设,表明两变量之间的正向关系很显著。教育水平与考试成绩正相关

有序分类变量 vs 连续型变量

将连续型变量离散化后当做有序分类,然后用 有序分类变量 VS 有序分类变量的方法

二分类变量 vs 二分类变量  :检验 联合 Cramer's V

一项研究调查了不同性别的成年人对在公众场合吸烟的态度,结果如表所示。那么,性别与对待吸烟的态度之间的相关程度

-赞同反对
1510
1026
import numpy as np
from scipy.stats import chi2_contingency

observed = np.array([[15, 10],
                     [10, 26]])
observed

chi2, p, dof, expected = chi2_contingency(observed,correction =False) # correction =False
# 卡方值 
# P值 
# 自由度: 
# 与原数据数组同维度的对应期望值

chi2, p
#(6.3334567901234555, 0.011848116168529757)

结论:p = 0.0118<0.05,拒绝原假设,表明两变量之间的正向关系很显著。

phi = np.sqrt(chi2/n)
print("phi's V:", phi)
# phi's V: 0.3222222222222222

卡方检验时有多种指标可表示效应量,可结合数据类型及交叉表格类型综合选择

  • 第一:如果是2*2表格,建议使用指标
  • 第二:如果是33,或 44表格,建议使用列联系数
  • 第三:如果是n*n(n>4)表格,建议使用校正列联系数
  • 第四:如果是m*n(m不等于n)表格,建议使用Cramer V指标
  • 第五:如果X或Y中有定序数据,建议使用指标

这里只列出指标和  Cramer V指标的计算,其他计算方式请读者自行研究。

# 计算Cramer's V
contingency_table = observed
n = contingency_table.sum().sum()
phi_corr = np.sqrt(chi2 / (n * min(contingency_table.shape) - 1))
v = phi_corr / np.sqrt(min(contingency_table.shape) - 1)

print("Cramer's V:", v)
# Cramer's V: 0.22878509151645754

二分类变量(有序) 连续型变量:Biserial

import numpy as np
from scipy.stats import pearsonr

# 生成随机的二元变量
binary_variable = np.random.choice([0, 1], size=100)

# 生成随机的连续变量
continuous_variable = np.random.normal(loc=0, scale=1, size=100)


# 注:此处的代码未经严格考证,请谨慎使用
def biserial_correlation(binary_variable, continuous_variable):
    binary_variable_bool = binary_variable.astype(bool)
    binary_mean = np.mean(binary_variable_bool)
    binary_std = np.std(binary_variable_bool)
    
    binary_variable_norm = (binary_variable_bool - binary_mean) / binary_std
    
    corr, _ = pearsonr(binary_variable_norm, continuous_variable)
    biserial_corr = corr * (np.std(continuous_variable) / binary_std)
    
    return biserial_corr

# 计算Biserial相关系数
biserial_corr = biserial_correlation(binary_variable, continuous_variable)

print("Biserial相关系数:", biserial_corr)
Biserial相关系数: -0.2061772328681707

无序分类变量 vs 无序分类变量

参考检验

有序分类变量 vs  无序分类变量

参考检验

有序分类变量 vs  有序分类变量

Kendall秩相关系数

Kendall秩相关系数也是一种非参数的等级相关度量,类似于Spearman等级相关系数。

import random 
import numpy as np
import pandas as pd

np.random.seed(10)
df = pd.DataFrame({'品牌知名度排位':[9,4,3,6,5,8,1,7,10,2],
     '售后服务质量评价排位':[8,2,5,4,7,9,1,6,10,3]})
df
pd.Series.corr(df['品牌知名度排位'], df['售后服务质量评价排位'],method = 'kendall'# Kendall Tau相关系数
# 0.7333333333333333
from scipy.stats import kendalltau

# 两个样本数据
x = df['品牌知名度排位']
y = df['售后服务质量评价排位']

# 计算Kendall Tau相关系数
correlation, p_value = kendalltau(x, y)

print("Kendall Tau相关系数:", correlation)
print("p值:", p_value)
# Kendall Tau相关系数: 0.7333333333333333
# p值: 0.002212852733686067

浮生皆纵,恍如一梦,让我们只争朝夕,不负韶华!

下期将为大家带来《统计学极简入门》之 再看t检验、F检验、检验

8. 再看t检验、F检验、检验

前面在假设检验的部分经学过t检验、F检验、检验,之所以再看,是想通过纵向对比这几个检验统计量以加深理解:

t检验

针对不同的场景,主要分为单样本T检验、独立样本T检验、配对样本T检验:

单样本的t检验

主要用于分析一组定量数据指定值的差异,例如检验食盐的实际称重是否不够标重的份量。

单样本T检验需要满足正态分布的假设,若不满足可采用单样本Wilcoxon检验

例5.2 检验汽车实际排放是否低于其声称的排放标准

汽车厂商声称其发动机排放标准的一个指标平均低于20个单位。在抽查了10台发动机之后,得到下面的排放数据:17.0 21.7 17.9 22.9 20.7 22.4 17.3 21.8 24.2 25.4该样本均值为21.13.究竟能否由此认为该指标均值超过20?

分析过程:由于厂家声称指标平均低于20个单位,因此原假设为总体均值等于20个单位(被怀疑对象总是放在零假设)。而且由于样本均值大于20(这是怀疑的根据),把备择假设设定为总体均值大于20个单位

于是我们有了原假设和备择假设

:

读取数据如下

data = [17.021.717.922.920.722.417.321.824.225.4]

分步骤计算过程如下:

步骤一:计算样本均值=(17+21.7+...+25.4)/10=21.13

用Python:

x_bar = np.array(data).mean()
x_bar
# 21.13

步骤二:计算样本标准差

用Python计算:

s = np.sqrt(((data-x_bar)**2).sum()/len(data))
s
# 2.7481084403640255

步骤三:计算统计量

,其中为整体均值20,自由度n-1为9

t = (x_bar - 20)/(s/np.sqrt(len(data)-1))
t
# 1.2335757753252794

步骤四:查表或用软件查询p值与

p_value = scipy.stats.t.sf(t,len(data)-1 )
p_value 
# 0.1243024777589817

结论:选择显著性水平 0.01 的话,P=0.1243 > 0.05, 故无法拒绝原假设。具体来说就是该结果无法支持指标均值超过20的备则假设。说明发动机排放指标是不合格的。

对于以上过程,我们也可以用scipy.stats.ttest_1samp函数,一步到位进行t检验,直接返回的就是t统计量和p值:

import scipy.stats
t, pval = scipy.stats.ttest_1samp(a = data, popmean=20,alternative = 'greater')
# 说明  
# 单一样本的t检验,检验单一样本是否与给定的均值popmean差异显著的函数,第一个参数为给定的样本,第二个函数为给定的均值popmean,可以以列表的形式传输多个单一样本和均值。
# a  为给定的样本数据
# popmean 为给定的总体均值
# alternative 定义备择假设。以下选项可用(默认为“two-sided”):
# ‘two-sided’:样本均值与给定的总体均值(popmean)不同
# ‘less’:样本均值小于给定总体均值(popmean)
# ‘greater’:样本均值大于给定总体均值(popmean)

print(t, pval)

# '''
# P= 0.004793 < 5%, 拒绝原假设,接受备择假设样本
# '''

结论:选择显著性水平 0.01 的话,P=0.1243 > 0.05, 故无法拒绝原假设。具体来说就是该结果无法支持指标均值超过20的备则假设。

独立样本的t检验

主要用于分析定量数据定类数据(2组)的差异。原理是推论差异发生的概率,从而比较两个平均数的差异是否显著。通俗的说就是用样本均数和已知总体均数进行比较,来观察此组样本与总体的差异性。

例如有一个班的学生身高数据,如果学生的身高服从正态分布,想要研究身高和性别的关系,这个时候就相当于是两个独立样本。

独立样本的T检验也需要满足正态分布的假设,如果不满足可采用Wilcoxon检验(也称MannWhitney检验);如果满足但方差不等可采用Welch T检验

计算公式如下:

代表两组数据的均值,

代表样本数,

代表两组数组的方差。

从计算公式能看出来,t越小则两组数据差异性越小。具体多小就根据置信度和自由度查表对比理论统计量的大小得出两组数据差异性是否显著。

例5.6(数据:drug.txt) 检验某药物在实验组的指标是否低于对照组

为检测某种药物对情绪的影响,对实验组的100名服药者和对照组的150名非服药者进行心理测试,得到相应的某指标。需要检验实验组指标的总体均值是否大于对照组的指标的总体均值。这里假定两个总体独立地服从正态分布。相应的假设检验问题为:

分析过程:由于目标是检验实验组指标的总体均值是否大于对照组的指标的总体均值,因此选择上侧检验

于是我们有了原假设和备择假设

:

data = pd.read_table("./t-data/drug.txt",sep = ' ')
data.sample(5)
ahid
4.42
6.82
9.62
4.82
13.21
a = data[data['id']==1]['ah']
b = data[data['id']==2]['ah']
'''
H0: 实验组的均值等于对照组
H1: 实验组的均值大于对照组

'''

t, pval = scipy.stats.ttest_ind(a,b,alternative = 'greater')
# 独立样本的T检验,检验两个样本的均值差异,该检验方法假定了样本的通过了F检验,即两个独立样本的方差相同


# 另一个方法是: 
# stats.ttest_ind_from_stats(mean1, std1, nobs1, mean2, std2, nobs2, equal_var=True)
# 检验两个样本的均值差异(同上),输出的参数两个样本的统计量,包括均值,标准差,和样本大小:直接输入样本的描述统计量(均值,标准差,样本数)即可



print(t,pval)
# 0.9109168350628888 0.18161186154576608

结论:选择显著性水平 0.05 的话,p = 0.1816 > 0.05,无法拒绝H0,具体来说就是该结果无法支持实验组均值大于对照组的备则假设。

配对样本t检验

主要用于分析配对定量数据的差异。

常见的使用场景有:

①同一对象处理前后的对比(同一组人员采用同一种减肥方法前后的效果对比);

②同一对象采用两种方法检验的结果的对比(同一组人员分别服用两种减肥药后的效果对比);

③配对的两个对象分别接受两种处理后的结果对比(两组人员,按照体重进行配对,服用不同的减肥药,对比服药后的两组人员的体重)。

例如,假设一个班上男女生的成绩不存在差异,显著性水平为0.05,可理解为只有5%的概率会出现“男女生成绩差异显著”的情况,若计算出的检验p值若小于0.05,则可以拒绝原假设。反之不能拒绝原假设。

此外,t检验注意事项

①无论哪种t检验、都要数据服从正态或者近似正态分布。正态性的检验方法有:正态图、正态性检验、P-P图/Q-Q图等。

②两个独立样本的t检验,通常需要先进行F检验(方差齐次检验),检验两个独立样本的方差是否相同,若两总体方差相等,则直接用t检验,若不等,可采用t’检验或变量变换或秩和检验等方法。

例5.7(数据: diet.txt) 检验减肥前后的重量是否有显著性差异(是否有减肥效果)

这里有两列50对减肥数据。其中一列数据(变量名before)是减肥前的重量,另一列(变量名after)是减肥后的重量(单位: 公斤),人们希望比较50个人在减肥前和减肥后的重量。

分析过程:这里不能用前面的独立样本均值差的检验,这是因为两个样本并不独立。每一个人减肥后的重量都和自己减肥前的重量有关,但不同人之间却是独立的,所以应该用配对样本检验。同时,由于研究的是减肥前后的重量变化,期望减肥前的重量大于减肥后的重量,所以备择假设是期望减肥前的重量大于减肥后的重量

于是我们有了原假设和备择假设:

:

步骤一、计算两组样本数据差值d,即58-50,76-71,69-65,68-76,81-75

d = data['before'] - data['after']

步骤二、计算差值d的平均值,即(-1+0+1+0)/4=0




    
d_bar = ( d).sum()/len(data)

步骤三、计算差值d的标准差,计算公式为

s_d = np.sqrt(((d -d_bar)**2).sum()/(len(data)-1))
s_d

步骤四、计算统计量t,计算公式为




    
t = (d_bar)/(s_d/np.sqrt(len(data))) # 这里mu是0
t
#

计算p值

p_value = scipy.stats.t.sf(t, len(data)-1)
p_value 
# 0.0007694243254842176

其中为理论总体差值均值0,n为样本数。

结论选择显著性水平 0.05 的话,p = 0.0007 < 0.05,故应该拒绝原假设。具体来说就是该结果倾向支持减肥前后的重量之差大于零(即减肥前重量大于减肥后,也就是有减肥效果)的备则假设。

同样的,我们用现成的函数stats.ttest_rel,一步到位进行t检验,直接返回的就是t统计量和p值:

data = pd.read_table("./t-data/diet.txt",sep = ' ')
data.sample(5)
beforeafter
5850
7671
6965
6876
8175
a = data['before']
b = data['after']
stats.ttest_rel(a, b,alternative = 'greater')
# #配对T检验,检测两个样本的均值差异,输入的参数是样本的向量
# Ttest_relResult(statistic=3.3550474801424173, pvalue=0.000769424325484219)

结论选择显著性水平 0.05 的话,p = 0.0007 < 0.05,故应该拒绝原假设。具体来说就是该结果倾向支持减肥前后的重量之差大于零(即减肥前重量大于减肥后,也就是有减肥效果)的备则假设。

F检验

F检验(F-test),最常用的别名叫做联合假设检验(英语:joint hypotheses test),此外也称方差比率检验、方差齐性检验。

用于:判断两组数据方差是否存在显著差异。

步骤一:分别计算两组样本数据的均值

步骤二:分别计算两组样本数据的标准方差的平方

步骤三:计算两组样本数据标准方差的平方比

,把平方大的作为分子,小的作为分母。

得到F值后根据两组数据的自由度和置信度查表对比,同样的,F值也是越小越说明差异性不显著。

stats模块中虽然没有f检验的函数,但是却有着f分布的生成函数,可以利用其进行f检验:

import numpy as np
from scipy.stats import f_oneway

# 创建两个样本
sample1 = np.array([1, 2, 3, 4, 5])
sample2 = np.array([2, 4, 6, 8, 10])

# 使用 f_oneway 函数进行 F 检验
f_statistic, p_value = f_oneway(sample1, sample2)

# 打印检验结果
print("F statistic:", f_statistic)
print("p-value:", p_value)

#在上述示例中,我们创建了两个样本 sample1 和 sample2,每个样本包含五个观测值。然后,我们使用 f_oneway 函数对这两个样本进行F 检验。
#f_oneway 函数返回两个值,第一个是 F 统计量,第二个是对应的 p 值。我们可以根据 p 值来判断样本方差是否有显著差异。如果 p值小于设定的显著性水平(通常为 0.05),则可以拒绝原假设,认为样本方差存在显著差异。
#在上述示例中,我们可以得到 F 统计量为 4.0,p 值为 0.078。由于 p 值大于 0.05,我们不能拒绝原假设,即无法认为这两个样本的方差存在显著差异。
#需要注意的是,在使用 f_oneway 函数进行 F 检验时,输入的样本应该是一维数组或列表形式。如果有多个样本,可以将它们作为函数的参数传入。

也可以引入sklearn进行f检验

# 例子1
from sklearn.datasets import make_classification
from sklearn.feature_selection import f_classif 

# 生成样本数据集
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=42)

# F检验
F, pval = f_classif(X, y)  

# 输出结果
print(F) 
print(pval)

# 特征排序(如果不做机器学习可以忽略这一步)
indices = np.argsort(F)[-10:] 
print(indices)
#这里我们生成了一个包含10个特征的样本分类数据集,其中5个特征包含区分两类信息,5个特征冗余。
#使用f_classif函数可以计算每个特征的F-score和p值,F-score越高表示该特征越重要。
#最后通过argsort排序,输出最重要的特征索引。
#F检验通过计算每个特征对目标类别的区分能力,来对特征重要性进行评估和排序,是一种常用的过滤式特征选择方法。

# 例子2
import numpy as np
from sklearn.feature_selection import f_regression

# 创建特征矩阵 X 和目标向量 y
X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
y = np.array([1, 2, 3])

# 使用 f_regression 函数进行 F 检验
f_values, p_values = f_regression(X, y)

# 打印 F 统计量和 p 值
print("F values:", f_values)
print("p-values:", p_values)

例5.10(两总体方差之比的假设检验) 检验修完Python课程的学生是否比修完数据库课程的学生考CDA的成绩方差更大

某高校数据科学专业的学生,修完一门数据库课程的41名学生考CDA的方差,修完Python课程的31名学生考CDA的方差是,这些数据是否表明,修完数据库的学生要比修完Python的学生CDA成绩的方差更大?

分析过程:由于目标是希望修完Python的学生CDA成绩的方差更大,因此选择上侧检验。两总体方差之比用F检验,将方差较大的数据库课程的考试成绩视为总体1

于是我们有了原假设和备择假设

:


import numpy as np
from scipy import stats

def f_test_by_s_square(n1, n2, s1_square,s2_square, side ='two-sided'):
    """
    参数
    n1 :样本1的数量
    n2 :样本2的数量
    s1_square:样本1的方差
    s2_square:样本2的方差
    # 
    # F_value :F统计量的值
    # p_value :对应的p值
    "
""
    F_value = s1_square/s2_square
    F = stats.f(dfn = n1-1, dfd = n2-1)
    if side=='two-sided':
        print("two-sided")
        p_value = 2*min(F.cdf(F_value), 1-F.cdf(F_value))
        return F_value,p_value
    elif  side=='greater':
        print("greater")
        p_value = 1-F.cdf(F_value)
        return F_value,p_value
f_statistic , p_value= f_test_by_s_square(n1=41, n2=31,s1_square=120,s2_square=80,side='greater')# 打印检验结果
# 选择上侧检验所以side='greater'
print("F statistic:", f_statistic)
print("p-value:", p_value)

结论 选择显著性水平 0.05 的话,p = 0.1256 > 0.05,故无法原假设。结果无法支持修完数据库的学生要比修完Python的学生CDA成绩的方差更大的备则假设。

检验

卡方检验(chi-square test),也就是χ2检验,是以分布为基础的一种用途广泛的分析定性数据差异性的方法,通过频数进行检验。

之前假设检验一节中,我们知道卡方检验可以做指定方差和样本方差是否有差异

例5.5 检验某考试中心升级题库后考生分数的方差是否有显著变化

某数据分析师认证考试机构CDA考试中心,历史上的持证人考试分数的方差为,现在升级了题库,该考试中心希望新型考题的方差保持在原有水平上,为了研究该问题,收集到了30份新考题的考分组成的样本,样本方差是,在的显著性水平下进行假设检验。

分析过程:由于目标是希望考试分数的方差保持原有水平,因此选择双侧检验

于是我们有了原假设和备择假设

:

import numpy as np
from scipy import stats

def chi2test(sample_var, sample_num,sigma_square,side, alpha=0.05):
    '''
    参数:
    sample_var--样本方差
    sample_num--样本容量
    sigma_square--H0方差
    返回值:
    pval
    '
''
    chi_square =((sample_num-1)*sample_var)/(sigma_square)
    p_value = None
    if side == 'two-sided':
        p = stats.chi2(df=sample_num-1).cdf(chi_square)
        p_value = 2*np.min([p, 1-p])
    elif side == 'less':
        p_value = stats.chi2(df=sample_num-1).cdf(chi_square)
    elif side == 'greater':
        p_value = stats.chi2(df=sample_num-1).sf(chi_square)
    return chi_square,p_value

p_value = chi2test(sample_var = 162, sample_num = 30, sigma_square = 100,side='two-sided')

print("p值:", p_value)
# p值: 0.07213100536907469

结论:选择显著性水平 0.05 的话,P=0.0721 > 0.05, 故无法拒绝原假设。具体来说就是不支持方差发生了变化的备则假设。换句话说新题型的方差依然保持在原有水平上

那么,卡方检验还有什么应用呢?

统计样本的实际观测值与理论推断值之间的偏离程度,实际观测值与理论推断值之间的偏离程度就决定卡方值的大小,如果卡方值越大,二者偏差程度越大;反之,二者偏差越小;若两个值完全相等时,卡方值就为0,表明理论值完全符合。

卡方值计算公式:

①卡方优度检验 对一列数据进行统计检验,分析单个分类变量实际观测的比例与期望的比例是否一致。

②配对卡方 研究实验过程中,用不同方法检测同一批人,看两个方法的效果是否有显著差异。

③ 交叉表卡方

研究两组分类变量的关系:如性别与看不看直播是否有关系。

例7.5(交叉表卡方):性别与对待吸烟的态度之间的相关性一项研究调查了不同性别的成年人对在公众场合吸烟的态度,结果如表所示。那么,性别与对待吸烟的态度之间的相关程度

-赞同反对
1510
1026

python中stats模块,同样有卡方检验的计算函数

from  scipy.stats import chi2_contingency
import numpy as np
data = np.array([[15,10], [10,26]])
chi2, p, dof, expected  = chi2_contingency(data,correction =False)
print(f'卡方值={chi2}, p值={p}, 自由度={dof}')
# 卡方值=6.3334567901234555, p值=0.011848116168529757, 自由度=1

结论:p = 0.0118<0.05,拒绝原假设,表明两变量之间的正向关系很显著。

sklearn中的特征选择中也可以进行卡方检验。

from sklearn.feature_selection import chi2
import numpy as np

# 假设我们有一个包含100个样本和5个特征的数据集
X = np.random.randint(0, 10, (40, 5))
y = np.random.randint(0, 2, 40)
# 使用chi2函数计算特征变量与目标变量之间的卡方统计量和p值
chi2_stats, p_values = chi2(X, y)

# 打印每个特征的卡方统计量和p值
for i in range(len(chi2_stats)):
    print(f"Feature {i+1}: chi2_stat = {chi2_stats[i]}, p_value = {p_values[i]}")

#在上面的例子中,我们生成了一个包含100个样本和5个特征的随机数据集。然后,我们使用chi2函数计算每个特征与目标变量之间的卡方统计量和p值。最后,我们打印出每个特征的卡方统计量和p值。
#根据卡方统计量和p值,我们可以判断每个特征与目标变量之间的关联程度。如果卡方统计量较大且p值较小,则说明特征与目标变量之间存在显著关联,可以考虑选择该特征作为重要的特征进行建模。

一文看懂A/B测试(含Python代码实现流程、假设检验原理及2个案例)

前面学完了统计学的基础内容,以及t检验、F检验、卡方检验等。本节我们尝试再用“MVP”的思路来梳理A/B测试:实施的流程、假设检验原理(z检验、t检验应用场景的区别)及分组的注意事项(设计AA分组对比原分组的分布以确保分组的科学性),最后用2个小案例结合Python代码来演示假设检验的过程。

AB测试是一种常用的实验设计方法,用于 比较两个或多个不同的版本(例如产品、网页设计、广告等)在某个指标上的表现差异。而假设检验是AB测试的统计分析方法,用于判断这些差异是否具有统计学意义。

  1. 假设检验的一般步骤

  • (1) 提出原假设H0和备择假设H1
  • (2) 用均值之差 或者 比例之差作为检验统计量 z检验 或者 t检验,并计算统计量及p值
  • (3) 根据p值与显著性水平判断是否拒绝H0
  • 基于假设检验的AB测试步骤

    • (1) H0假设:A组转化率等于B组转化率 H1假设:A组转化率不等于B组转化率
    • (2) 用均值之差t检验 或者 比例之差z检验; 并计算统计量及p值
    • (3) 判断p值是否小于显著性水平0.05,判断是否拒绝H0

    案例9-1 使用基于均值的假设检验进行AB测试

    # 随机生成两组样本数据
    import numpy as np
    from scipy import stats
    # 假设有两个版本 A 和 B
    # 生成样本数据,这里使用正态分布作为例子
    np.random.seed(0)
    sample_A = np.random.normal(loc=91, scale=4, size= 1000)
    sample_B = np.random.normal(loc=95, scale=4, size= 1000)
    # 计算样本均值和标准差
    mean_A = np.mean(sample_A)
    mean_B = np.mean(sample_B)
    std_A = np.std(sample_A)
    std_B = np.std(sample_B)

    步骤1: 提出原假设H0和备择假设H1

    H0:版本 A 和 B 在统计上存在显著差异

    H2:版本 A 和 B 在统计上没有显著差异

    步骤2: 使用均值之差的t检验,计算出t统计量的值和P值如下

    # 假设零假设为版本 A 和 B 的均值相等
    # 使用独立样本 t 检验进行假设检验
    t_statistic, p_value = stats.ttest_ind(sample_A, sample_B)
    print(t_statistic, p_value)
    -24.206499153286355 9.893622835346878e-114

    步骤3: 进行假设检验

    # 步骤5: 判断结果

    alpha = 0.05  # 设置显著性水平
    if p_value     print("拒绝零假设(无差异),版本 A 和 B 在统计上存在显著差异")
    else:
        print("接受零假设(无差异),版本 A 和 B 在统计上没有显著差异")
    拒绝零假设(无差异),版本 A 和 B 在统计上存在显著差异

    值得注意的是,通常情况下我们在做AB测试前需要做AA测试,也就是从A里面通过不同的抽样方式选定一定样本AA,再与A进行测试

    AA测试(简单随机抽样)

    # 简单随机抽样
    sample_AA1 = np.random.choice(sample_A,int(len(sample_A)*0.2)) 
    # 假设零假设为版本 A 和 B 的均值相等
    # 使用独立样本 t 检验进行假设检验
    t_statistic, p_value = stats.ttest_ind(sample_A, sample_AA1)
    # 步骤5: 判断结果

    alpha = 0.05  # 设置显著性水平
    if p_value     print(f"p = {p_value} )
    else:
        print(f"p = {p_value} > 0.05,接受零假设(无差异),版本 A 和 AA1 在统计上没有显著差异")
    p = 0.8914256267444793 > 0.05,接受零假设(无差异),版本 A 和 AA1 在统计上没有显著差异
    import seaborn as sns
    import matplotlib.pyplot as plt

    # 绘制 sample_A 和 sample_AA, 的分布曲线图
    plt.figure(figsize=(10 6))
    sns.histplot(sample_A, kde=True, color='blue', label='Sample A')
    sns.histplot(sample_AA1, kde=True, color='orange', label='Sample AA1')
    plt.xlabel('Value')
    plt.ylabel('Frequency')
    plt.title('Distribution of Sample A and Sample AA')
    plt.legend()
    plt.show()

    AA测试(分层抽样)

    ## 分层抽样
    layers = np.repeat(['A','B','C','D','E'], 200)

    # 在每个分层中抽样
    s1 = np.random.choice(sample_A[layers=='A'], size=40
    s2 = np.random.choice(sample_A[layers=='B'], size=40
    s3 = np.random.choice(sample_A[layers=='C'], size=40
    s4 = np.random.choice(sample_A[layers=='D'], size=40
    s5 = np.random.choice(sample_A[layers=='E'], size=40


    sample_AA2 =np.concatenate([s1, s2, s3, s4, s5])
    # 假设零假设为版本 A 和 B 的均值相等
    # 使用独立样本 t 检验进行假设检验
    t_statistic, p_value = stats.ttest_ind(sample_A, sample_AA2)
    # 步骤5: 判断结果

    alpha = 0.05  # 设置显著性水平
    if p_value     print(f"p = {p_value} )
    else:
        print(f"p = {p_value} > 0.05,接受零假设(无差异),版本 A 和 AA2 在统计上没有显著差异")
    p = 0.6702407975402769 > 0.05,接受零假设(无差异),版本 A 和 AA2 在统计上没有显著差异
    import seaborn as sns
    import matplotlib.pyplot as plt

    # 绘制 sample_A 和 sample_AA, 的分布曲线图
    plt.figure(figsize=(106))
    sns.histplot(sample_A, kde=True, color='blue', label='Sample A')
    sns.histplot(sample_AA2, kde=True, color='orange', label='Sample AA2')
    plt.xlabel('Value')
    plt.ylabel('Frequency')
    plt.title('Distribution of Sample A and Sample AA2')
    plt.legend()
    plt.show()

    AA测试(系统抽样)

    # 系统抽样
    k = 5 # 每隔3个取一个
    sample_AA3 = sample_A[::k] 
    # 假设零假设为版本 A 和 B 的均值相等
    # 使用独立样本 t 检验进行假设检验
    t_statistic, p_value = stats.ttest_ind(sample_A, sample_AA3)
    # 步骤5: 判断结果

    alpha = 0.05  # 设置显著性水平
    if p_value     print(f"p = {p_value} )
    else:
        print(f"p = {p_value} > 0.05,接受零假设(无差异),版本 A 和 AA3 在统计上没有显著差异")
    p = 0.4730028024029992 > 0.05,接受零假设(无差异),版本 A 和 AA3 在统计上没有显著差异
    import seaborn as sns
    import matplotlib.pyplot as plt

    # 绘制 sample_A 和 sample_AA, 的分布曲线图
    plt.figure(figsize=(106))
    sns.histplot(sample_A, kde=True, color='blue', label='Sample A')
    sns.histplot(sample_AA3, kde=True, color='orange', label='Sample AA3')
    plt.xlabel('Value')
    plt.ylabel('Frequency')
    plt.title('Distribution of Sample A and Sample AA3')
    plt.legend()
    plt.show()

    AB测试

    # 假设零假设为版本 A 和 B 的均值相等
    # 使用独立样本 t 检验进行假设检验
    t_statistic, p_value = stats.ttest_ind(sample_A, sample_B)
    # 步骤5: 判断结果

    alpha = 0.05  # 设置显著性水平
    if p_value     print(f"p = {p_value} )
    else:
        print(f"p = {p_value} > 0.05,接受零假设(无差异),版本 A 和 B 在统计上没有显著差异")
    p = 9.893622835346878e-114 < 0.05,拒绝零假设(无差异),版本 A 和 B 在统计上存在显著差异
    import seaborn as sns
    import matplotlib.pyplot as plt

    # 绘制 sample_A 和 sample_B 的分布曲线图
    plt.figure(figsize=(106))
    sns.histplot(sample_A, kde=True, color='blue', label='Sample A')
    sns.histplot(sample_B, kde=True, color='orange', label='Sample B')
    plt.xlabel('Value')
    plt.ylabel('Frequency')
    plt.title('Distribution of Sample A and Sample B')
    plt.legend()
    plt.show()

    在这个例子中,我们假设有两个版本 A 和 B,通过生成正态分布的样本数据进行比较。然后计算两个样本的均值和标准差,并使用独立样本 t 检验进行假设检验。根据显著性水平alpha 的设定,判断是否拒绝零假设,进而得出结论。


    案例:基于假设检验的支付宝点击率策略提升A-B测试效果分析

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    %matplotlib inline
    #加载数据
    data = pd.read_csv('./data/audience_expansion/effect_tb.csv',header = None)

    data.columns = ["dt","user_id","label","dmp_id"]
    # dmp_id:营销策略编号(源数据文档未做说明,这个根据情况设定为1.对照组,2.营销策略一,3.营销策略二)
    # user_id:支付宝用户id
    # label:用户当天是否点击活动广告(0:未点击,1:点击)

    data = data[["user_id","label","dmp_id"]]
    data = data.drop_duplicates() #删除重复值
    data.head(3)

    user_idlabeldmp_id
    0101
    1100000401
    2100000402

    计算3组营销策略的点击率的平均值

    根据原始数据计算3营销策略的点击率如下:

    df = data.pivot_table(index = "dmp_id",columns = "label",values = "user_id",aggfunc = "count",margins = True)
    df['crt'] = df[1] / df['All']
    df
    label01Allcrt
    dmp_id



    118817452391819056630.012551
    240481162964111070.015315
    330792382823162050.026192
    All25944793849626329750.014621

    我们可以得到:

    • 对照组 (dmp_id=1)的点击率0.012551 ,
    • 策略1组(dmp_id=2)的点击率0.015315 ,
    • 策略2组(dmp_id=2)的点击率0.026192 ,
    df['crt'][1:3]-0.012551 # 2
    dmp_id
    2 0.002764
    3 0.013641
    Name: crt, dtype: float64

    从点击率来看,策略一和策略二在对照组的基础上都有一定的提升。

    其中策略一提高了0.2个百分点,策略二提高了1.3个百分点,只有策略二满足了我们对点击率提升最小值1个百分点的要求

    接下来需要进行假设验证,来看看策略二的点击率提升是否显著

    假设检验进行判断

    记对照组点击率为p1,策略二点击率为p2,则:

    • (1) H0假设: p1>=p2 策略2组点击率大于等于对照组点击率 H1假设:p1< p2 策略2组点击率小于对照组点击率
    • (2) 计算A组和B组样本的转化率
    • (3) 用转化率之差作为检验统计量 z检验
    • (4) 计算p值
    • (5) 判断p值是否小于显著性水平0.05,判断是否拒绝H0
    import numpy as np
    import scipy.stats as stats

    def proportion_test(p1, p2, n1, n2, side='two-sided'):
        """
        参数:
        p1: 样本1的比例
        p2: 样本2的比例
        n1: 样本1的数量
        n2: 样本2的数量
        side: 假设检验的方向,可选'two-sided'(双侧检验,默认), 'greater'(右侧检验), 'less'(左侧检验)

        返回值:
        z_value: Z统计量的值
        p_value: 对应的p值
        """

        p = (p1 * n1 + p2 * n2) / (n1 + n2)
        se = np.sqrt(p * (1 - p) * (1 / n1 + 1 / n2))
        z_value = (p1 - p2) / se

        if side == 'two-sided':
            p_value = 2 * (1 - stats.norm.cdf(np.abs(z_value)))
        elif side == 'greater':
            p_value = 1 - stats.norm.cdf(z_value)
        elif side == 'less':
            p_value = stats.norm.cdf(z_value)
        else:
            raise ValueError("Invalid side value. Must be 'two-sided', 'greater', or 'less'.")

        return z_value, p_value

    p1 = df.loc[1,'crt']
    p2 = df.loc[3,'crt']
    n1 = df.loc[1,'All']
    n2 = df.loc[3,'All']

    z_value, p_value = proportion_test(p1, p2, n1, n2, side='less')
    # 选择双侧检验 alternative = 'two-sided'

    print("Z_value:", z_value)
    print("p_value:", p_value)
    Z_value: -59.44168632985996
    p_value: 0.0
    # 直接用 sp.proportions_ztest 函数也可以,注意带入的是人数而非比例

    import statsmodels.stats.proportion as sp

    p1 = df.loc[1,1]
    p2 = df.loc[3,1]
    n1 = df.loc[1,'All']
    n2 = df.loc[3,'All']

    z_value, p_value = sp.proportions_ztest([p1, p2],[n1, n2], alternative = "smaller")
    # 选择双侧检验 alternative = 'two-sided'

    print("Z_value:", z_value)
    print("p_value:", p_value)
    Z_value: -59.44168632985996
    p_value: 0.0

    可以看到,p约等于0 < 0.05。所以拒绝原假设,认为策略2点击率的提升在统计上是显著的。两种营销策略中,策略二对广告点击率有显著提升效果,因而在两组营销策略中应选择第二组进行推广


    至此,统计学的描述性统计、推断统计基本告一段落,剩下的贝叶斯、线性回归、逻辑回归请读者自行查阅资料进行学习,我们下个系列见!

    (PS:可以在评价中写下你想学的系列,包括不限于SQL、Pandas、Julia、机器学习、数学建模、数据治理)

    致谢

    《统计学极简入门》图文系列教程的写作过程中参考了诸多经典书籍,包括:

    人大统计学教授吴喜之老师的 《统计学:从数据到结论》;

    浙大盛骤教授的 《概率论与数理统计》;

    辛辛那提大学 David R. Anderson的 《商务经济与统计》;

    北海道大学的马场真哉的 《用Python动手学统计学》 ;

    千叶大学研究院教授栗原伸一的《统计学图鉴》;

    前阿里巴巴产品专家徐小磊的知乎:磊叔-数据化运营;

    知乎旧梦的文章T检验、F检验、卡方检验详细分析及应用场景总结;

    csdn文章T检验、卡方检验、F检验;

    以及CDA认证考试中心  提供的部分案例数据集

    在此一并感谢以上内容的作者!

    一死生为虚诞,齐彭殇为妄作。各位加油!


    关注山有木兮水有鱼(ID:symx-syy)公众号后,在公众号对话框回复“统计学”,可以获取本文所涉及的ipnb代码。


    Python社区是高质量的Python/Django开发社区
    本文地址:http://www.python88.com/topic/161994
     
    884 次点击