假设检验是统计推断学的一个分支,它允许利用样本数据对总体参数进行推断。通过假设检验,可以比较总体参数与特定值的差异、比较两个总体,或者检验总体是否遵循某种概率分布等。假设检验的主题相当详细,不可能在一篇短文中全面介绍,但将尝试通过一个端到端的案例研究来获得对假设检验过程的总体看法。本文更侧重于实际应用,必要的基本理论将根据需要进行强调。
假设检验的过程包括以下几个步骤:首先,提出一个关于总体参数的陈述,称为零假设(Ho),并提出一个与零假设相反的备择假设(Ha)。然后,收集数据并进行测试,根据分析结果中的证据,选择拒绝或不拒绝零假设。使用样本数据来推断总体,这个决策过程中存在风险。可以根据用例选择风险水平(愿意接受多少风险?)。通常选择的置信水平为99%、95%和90%,与之相关的术语是显著性水平,分别为0.01、0.05或0.1。错误分为第一类错误(当零假设为真时拒绝它)和第二类错误(当应该拒绝零假设时未能拒绝它)。根据测试得到检验统计量和p值,这些值构成了拒绝或不拒绝零假设的依据。p值告诉在假设零假设为真的情况下,样本结果的可能性。p值也称为观察到的显著性水平,如果发现它小于选择的显著性水平(称为α),拒绝零假设,否则不拒绝零假设。
在假设检验中,还会注意到独立样本和依赖样本的区别。如果两个样本之间没有关系,则它们可能是独立的;如果一个样本可以用来估计另一个样本,则它们可能是依赖的。假设检验有多种方法,取决于处理依赖样本还是独立样本,或者取决于可用样本的数量(z检验或t检验等)。一个依赖样本假设检验的例子可能是分析一个群体在减肥计划前后的体重,或者一个玉米片制造商想要测试制造的包装的平均重量是否等于指定的值,比如说500克。
在端到端案例研究中,将采用独立样本来研究假设检验的方法。将使用Python代码来简化生活,而不是通过经典统计过程进行测试部分(请随时追溯方法以加强理解)。
在与朋友闲聊时,他提到电子商务网站A上的iPhone平均成本与电子商务网站B不同。作为数据科学家,相信数据提供的证据,并开始科学地分析这个问题。让定义零假设和备择假设:
H0: µ1 = µ2(两个总体均值相等)
HA: µ1 ≠ µ2(两个总体均值不相等)
当然,需要数据来测试上述假设,选择0.05作为测试显著性水平。在案例研究中,选择了印度的两个电子商务网站amazon.co.in和flipkart.com(只是一个随机选择)。为了保持代码简单,收集了这两个网站上第一搜索页面上显示的iPhone价格进行测试。这种数据收集在数据收集方面有局限性,但让尽量保持事情简单。让深入代码,
from bs4 import BeautifulSoup
import requests
import pandas as pd
import numpy as np
HEADERS = ({'User-Agent':
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36',
'Accept-Language': 'en-US, en;q=0.5'})
def get_data(URL,tag,attrs):
''' 这个函数接受一个URL、标签和属性,用于从网站抓取数据 '''
webpage = requests.get(URL, headers=HEADERS)
soup = BeautifulSoup(webpage.content, "html.parser")
price = soup.find_all(tag, attrs=attrs)
price_list =[]
for p in price:
price_list.append(p.text)
return price_list
利用requests和BeautifulSoup库从网站抓取数据,使用Pandas来展示数据,并使用scipy和numpy进行假设检验。许多网站都有阻止机器人访问的协议。为了提取数据,需要创建一个用户代理,它携带关于发送请求的主机的信息。创建了一个函数来从网站检索价格数据。网站URL传递给requests库的‘get方法’,响应传递给BeautifulSoup以及HTML解析器以构建soup对象。其中一个困难的任务是识别存储价格信息的HTML标签和ID。可以访问网站页面,将鼠标悬停在价格上,右键单击以检查网页并检索HTML标签。
可以看到,亚马逊搜索页面上的iPhone价格信息位于标签和class='a-price-whole'中。使用find_all()方法获取页面上的所有价格数据。同样,也可以获取flipkart.com上的类似信息(URL、标签和属性将不同,通过检查网页获得)。
#从亚马逊获取iPhone价格
amazon_url = "https://www.amazon.in/s?k=iphones&ref=nb_sb_noss_2"
amz_attrs={'class':'a-price-whole'}
amz_tag = "span"
amazon_price = get_data(amazon_url,amz_tag,amz_attrs)
函数的调用返回一个包含亚马逊网站价格的列表。可以使用相同的函数从Flipkart检索价格信息。
#获取Flipkart价格
flipkart_url = "https://www.flipkart.com/search?q=iphones&otracker=search&otracker1=search&marketplace=FLIPKART&as-show=on&as=off"
flip_attrs={'class':'_30jeq3 _1_WHN1'}
flip_tag = "div"
flipkart_price = get_data(flipkart_url,flip_tag,flip_attrs)
可以创建一个数据框架来漂亮地展示数据。由于从亚马逊和Flipkart获得的价格长度不同,需要在代码中进行一些调整。使用pd.DataFrame_from_dict()函数从字典创建一个数据框架,并进行转置以构建数据框架,然后用值0填充NaN值(命令已经链式连接,如果喜欢,可以随意拆分)。
#使用这些列创建数据框架
my_dict = {'amazon_rs':amazon_price,'flipkart_rs':flipkart_price}
data = pd.DataFrame.from_dict(my_dict,orient='index').T.fillna(0,axis=1)
print(data.head())
数据科学家的工作包括清理数据,以便可以进行进一步的处理。需要注意的是,从网站抓取的数据是对象类型,需要在进行假设检验之前进行格式化。使用fillna()方法填充了数据不可用的网站的行。上述代码将处理预处理,并为假设检验做好准备。
#数据预处理
data['amazon_rs'] = data['amazon_rs'].str.replace(',','').astype(float)
data['flipkart_rs'] = data['flipkart_rs'].str.strip('₹')
data['flipkart_rs'] = data['flipkart_rs'].str.replace(',','').astype(float)
上述代码显示了数据的后处理,两个列都包含浮点数据类型。需要检查两个样本是否遵循正态分布,以了解要对数据执行的假设检验类型。如果样本是正态的,或者样本量相当大(n>30),可以使用scipy.ttest_ind()进行t检验。如果样本是正态的,需要进一步测试样本是否具有相等的方差,通常作为经验法则,如果方差比率小于4:1,可以假设相等的方差,否则必须使用equal_variance=False进行测试。在案例中,注意到将使用pandas切片来处理较短的数据(行数较少的数据)。将使用Shapiro-Wilks测试来测试正态性。
import scipy.stats as stats
print(stats.shapiro(data['amazon_rs'].iloc[:18]))
print(stats.shapiro(data['flipkart_rs']))
#执行双尾测试。可以使用'greater'或'less'进行单尾测试
stats.Mann-Whitney U(x=data['amazon_rs'].iloc[:18], y=data['flipkart_rs'], alternative = 'two-sided')