本文将通过kaggle的一个实例来,来展示如何通过统计分析的方法来分析自己的数据集。当我们处理一个数据集,分析里面特征的时候。通常分成三个步骤:
- 探索性数据分析
- 统计分析
- 预测建模
这三个步骤也可以看成是从数据集中一层层提取出有用信息的方式。
下面将主要对统计分析这部分做详细展开。
python统计分析方法
- 1.探索性分析
- 1.1 第一步:观察数据信息,拆分数据类型
- 1.2 第二步:处理当中的缺失值和重复值
- 1.3 数据集数据值处理
- 2.统计分析
- 2.1 假设检验
- 2.2 P显著性
- 2.3 统计分析处理
- 分类变量独立性测试
- 定量变量正态检验
- 目测观察正态性
- 统计正态性假设检验
- 定量变量相关检验
- Spearman's Rank Correlation
- 参数统计显著性检验
- 非参数统计显著性检验
1.探索性分析
探索分析的主要目的,是通过数据汇总,数据展示成图,数据透视等方式,来提取出一些有用的信息。这其中也包含前期,对数据的大致信息查看,和数据处理工作。
以kaggle的数据集为例下载地址https://www.kaggle.com/datasets/uciml/autompg-dataset
该数据记录了汽车的技术规格信息
实例数:398
特征数量:9个
各字段信息:
- mpg:英里数
- cylinders:气缸
- displacement:排量
- horsepower:马力
- weight:车身重量
- acceleration:加速度
- model year:年代
- origin:生产来源
- car name:车的名称
1.1 第一步:观察数据信息,拆分数据类型
观察数据的一些大致信息,以及思考每列的数据类型,如果它是object类型,那么它是类别的,否则是数值的,对于是数值类型的数据,还要去重得唯一,看下数量(如果去唯一后数据量少),也可分成object类
import pandas as pd
df = pd.read_csv('mpg.csv')
# 1.查看数据信息
print(f'shape:{df.shape}')
print(f'columns:{list(df.columns)}')
print(f'info:{df.info}')
# 2.对数据类型,进行分类
categorical = list(df.select_dtypes(include=['object']).columns)
numerical = list(df.select_dtypes(exclude=['object']).columns)
print(f'categorical variables: {categorical}')
print(f'numerical variables: {numerical}')
categorical variables: ['origin', 'name']
numerical variables: ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'model_year']
# 3.统计各特征下的唯一值的个数,对于唯一值个数少的且数据类型不为object的类型,转为object
print(df.nunique(axis=0))
'''
out:
mpg 129
cylinders 5
displacement 82
horsepower 93
weight 351
acceleration 95
model_year 13
origin 3
name 305
dtype: int64
'''
#从上面数量看,以及对应到之前的数据类型,发现cylinders和moderl year需要转化
categorical.extend(['cylinders', 'model_year'])
numerical.remove('cylinders')
numerical.remove('model_year')
1.2 第二步:处理当中的缺失值和重复值
缺失值
df.isna().sum()
'''
mpg 0
cylinders 0
displacement 0
horsepower 6
weight 0
acceleration 0
model_year 0
origin 0
name 0
dtype: int64
'''
#查看缺失部分的数据,看下缺失数据的占比
df[df.isnull().any(axis=1)]
print(f'null rate:{6 / len(df)}')
这里考虑到占比较少,因此对缺失的处理方式就删除(注意删除后,数据集的索引要重新排列),真实的缺失值要结合不同场景下去判断。
df = df[~df.isnull().any(axis=1)]
df.reset_index(inplace=True)
df.drop('index', inplace=True, axis=1)
重复值
# 查找全部重复条目并删除它们(如果有)
print(f'total duplicate rows: {df.duplicated().sum()}')
# drop duplicate rows if any
df = df[~df.duplicated()]
1.3 数据集数据值处理
去除掉数值信息中的空格
for col in ['origin', 'name']:
df[col] = df[col].apply(lambda x: ' '.join(x.split()))
将英里数mpg划分成object
df['mpg_level'] = df['mpg'].apply(lambda x: 'low' if x<17 else 'high' if x>29 else 'medium')
categorical.append('mpg_level')
合并categorical和numerical
df = pd.concat((df[cats], df[nums]), axis=1)
到这里应该可以发现,我们初步数据探索,数据处理,其实是从数据宏观的角度开始逐渐往下处理(先看整体情况,然后把数据补齐,最后在做数据上值的信息处理),具体的数据如何处理
2.统计分析
接下来最重要的部分就是统计分析,我们需要借助假设检验的知识,来判断各特征属性之间的关系,各特征下,不同因子之间的关系。通过假设检验可以做到分析中得出:
- 对于有因变量的,可以得出那些特征是影响因变量的关键因素
- 对于没有因变量的,可以分析出不同组的数据,之间的好坏
再开始分析之前介绍一下假设检验中各种检验的使用,是在怎么样的条件下选择的
2.1 假设检验
假设检验分为:参数检验和非参数检验
- 参数检验,是在总体分布已知的情况下,对总体分布的参数如均值、方差等进行推断的方法
参数检验的方法有,T检验、Z检验、F检验、二项分布总体的假设检验等,这些检验都是假设样本来自于正态分布的总体,将总体的数字特征看做未知的参数,通过样本的数据特征对其总体进行统计推断
- 非参数检验,是在总体方差未知或知道甚少的情况下,利用样本数据对总体分布形态等进行推断的方法
常用的非参数检验的方法有,两个独立样本的K-S检验、W-W检验、U检验等,多个独立样本的H检验、中位数检验等,卡方检验,二项分布检验、拟合优度检验等
从解释上来看,都是基于样本去对总体参数做估计,那我们的数据不就是总体的吗,难道要抽样吗?。
我理解的,我们通常做这类型假设检验拿到的数据总体其实就是一个样本,用我们的数据去对总体提出假设检验,但是这个总体其实是不存在的,
常规的数据如果数据量越来越大的话,这个总体分布,一定是呈现正态分布(中心极限定理)。
中心极限定理,是指概率论中讨论随机变量序列部分和分布渐近于正态分布的一类定理
所以我们分析,对比数据的显著性差异,其实是构造了一个虚拟的总体,通过总体的参数来分析结论。
2.2 P显著性
关于假设检验的原假设和备用假设,大致记住我们的原假设一定是等于什么什么:例如:
- 组1均值 = 组2均值
- df[‘mpg’] = 正态分布
2.3 统计分析处理
统计分析处理也是存在一个固定的流程的
根据之前我们划分的categorical和numerical,我们分别对categorical的特征做独立性测试,对numerical的特征做正态性测试和相关性测试
统计分析
catgorical
独立性测test
numerical
正态test
相关性test
具体如何选择那种检验方法可以对应到下面这条思维导图
定量数据:numerical
分类数据:categorical
分类数据,其实就是对不同类别的数据,进行频数统计,类似于excel里数据透视图中求值的count。
接下来就按照假设检验的方法开始分析我们的数据了
分类变量独立性测试
categorical variables: ['origin', 'name', 'cylinders', 'model_year','mpg_level'']
numerical variables: ['mpg', 'displacement', 'horsepower', 'weight', 'acceleration']
先对origin、model_year做独立检验,从上图选择那种方法需要看样本数和频数pandas中pd.crosstab函数可以很好的应用到这里。
pd.crosstab(df.origin, df.model_year)
可以看到origin和model_year不能实用于这里,接下来对origin和mpg_level分析
observed_values = pd.crosstab(df.origin, df.mpg_level).value
'''
out:
array([[ 22, 2, 44],
[ 49, 0, 30],
[ 24, 90, 131]])
'''
符合配对卡方检验
chi2, p, dof, expected_values = stats.chi2_contingency(observed_values)
chi2, p, dof, expected_values
'''
(123.76491109767298,
8.381476294026467e-26,
4,
array([[ 16.47959184, 15.95918367, 35.56122449],
[ 19.14540816, 18.54081633, 41.31377551],
[ 59.375 , 57.5 , 128.125 ]]))
'''
pvalue值<0.05 拒绝原假设,所以origin和mpg_level没有相关性。
定量变量正态检验
定量变量正态性检验分成二个部分:
- 目测观察正态性
- 统计正态性假设检验
有三种统计检验用于检查数据的正态性:
- Shapiro-Wilk Test (仅适用于总体上是正态分布)
- D’Agostino’s K 2 Test (仅适用于总体上是正态分布)
- Anderson-Darling Test
通常我们使用Shapiro Wilk检验足以。
目测观察正态性
通过对每一个numerical变量画出直方图观看是否为正态分布,
对数正态分布是对数呈正态分布的随机变量的分布。因此,如果随机变量X是对数正态分布,那么Y=ln(X)具有正态分布。
fig = pyplot.figure(1, (10, 4))
ax = pyplot.subplot(1,2,1)
sns.distplot(np.log2(df.mpg))
pyplot.tight_layout()
ax = pyplot.subplot(1,2,2)
sns.distplot(np.log2(df.weight))
pyplot.tight_layout()
pyplot.show()
由图发现weight不是对数正态分布,但mpg在视觉上看起来像对数正态。
感兴趣也可以增加一个QQplots来做检验
统计正态性假设检验
使用shapiro wilk检验对数值属性的正态性进行假设检验
原假设为等于,正态分布
def shapiro_wilk_test(df: pd.DataFrame, cols: list, alpha=0.05):
for col in cols:
_,p = stats.shapiro(df[col])
if p <= alpha:
print(f'{col}拒绝原假设,无显著性,不是正态分布')
else:
print(f'{col}接受原假设,有显著性,是正态分布')
shapiro_wilk_test(df, nums)
'''
mpg 拒绝原假设,无显著性,不是正态分布
displacement 拒绝原假设,无显著性,不是正态分布
horsepower 拒绝原假设,无显著性,不是正态分布
weight 拒绝原假设,无显著性,不是正态分布
acceleration 拒绝原假设,无显著性,不是正态分布
'''
像现在这种情况,我们想让accleration为正态分布,但是检测出来不是,就需要对数据进行转换处理,让它转换为正态分布
from sklearn.preprocessing import PowerTransformer
df_tfnum = pd.DataFrame(PowerTransformer().fit_transform(df[numerical]), columns=numerical)
df_tfnum.head()
删除数据中nan的部分,再做一次正态检测(目测、在用shapiro_wilk_test检验一次),得到acceleration为正态分布。
当数据不为正态分布的时候,可以对数据做转换transform标准化
定量变量相关检验
分析二个变量的相关性可以用皮尔逊相关系数
皮尔逊相关系数只是两个变量之间的标准化协方差,它可以用来总结两个数据样本之间线性关系的强度。计算中使用平均值和标准偏差表明,两个数据样本需要具有高斯或类高斯分布,因此它是一个参数统计。
皮尔逊相关性前提假设:
- 两个变量都应具有高斯或类高斯分布。
- 变量之间的关系应为线性。
- 同方差性,即所有随机变量具有相同有限方差的随机变量序列。
考虑到两个变量之间可能存在非线性关系,因此在变量的分布中,这种关系更强或更弱。在这些情况下,即使变量之间有很强的相关性,皮尔逊的相关性也很低。此外,所考虑的两个变量可能具有非高斯分布。为了正确识别具有非线性关系的变量之间的关联,我们可以使用基于秩的相关方法。
顺序关联(秩相关)
秩相关性是指使用值之间的顺序关系而非特定值量化变量之间关联的方法。
我们首先按升序对数据进行排序,然后给它们分配整数秩,然后使用它来查找相关b/w变量。由于假设值没有分布,秩相关方法被称为无分布相关或非参数相关。
四种秩相关方法如下:
- Spearman’s Rank Correlation
- Kendall’s Rank Correlation
- Kruskal’s Rank Correlation
- Somers’ Rank Correlation
Spearman’s Rank Correlation
斯皮尔曼相关是一种非参数秩相关
在这种情况下,不是使用样本本身的协方差和标准差计算系数,而是通过将原始数据转换为秩数据(因此是非参数的)来计算这些统计数据。这是非参数统计中常用的方法。
作为统计假设检验,该方法假设样本不相关,原假设不相关的
现在用mpg与其他numerical分析其相关性
for num in nums:
if num == 'mpg':
continue
corr, p = stats.spearmanr(df.mpg, df[num])
print(f'\n* `mpg` & `{num}`\n')
print(f'corr: {round(corr, 4)} \t p: {p}')
if p <= ALPHA:
print(f'拒绝原假设, mpg & {num}是相关的')
else:
print(f'接受原假设,mpg & {num}是不相关的')
参数统计显著性检验
T检验——它测试两个独立的正态分布样本是否具有相同的均值。
方差分析检验(ANOVA)——检验两个或多个独立正态分布样本的平均值是否相同。
非参数统计显著性检验
Mann-Whitney U检验-非参数等效的t检验。
Kruskal Wallis H-ANOVA的非参数等效值(用于中值)。
我们将根据样本进行适当的测试,即,如果样本是正态分布的,则进行参数测试,否则进行非参数测试。
首先,我们检查origin为日本和美国的加速度是否正态分布,然后应用适用的测试。
shapiro_wilk_test(df[df.origin=='japan'], ['acceleration'])
shapiro_wilk_test(df[df.origin=='usa'], ['acceleration'])
'''
两个都为正态分布,可以应用参数检验
'''
T检验
ALPHA = 0.05
_, p = stats.ttest_ind(df[df.origin=='japan'].acceleration, df[df.origin=='usa'].acceleration, equal_var=False)
if p <= ALPHA:
print(f'拒绝原假设,二个数据分布不同,有显著')
else:
print(f'接受原假设,二个数据分布相同,无显著')
out:拒绝原假设,二个数据分布不同,有显著
也可以用方差分析
_, p = stats.f_oneway(df[df.origin=='japan'].acceleration, df[df.origin=='usa'].acceleration, df[df.origin=='europe'].acceleration)
if p <= ALPHA:
print(f'拒绝原假设,二个数据分布不同,有显著')
else:
print(f'接受原假设,二个数据分布相同,无显著')
out:拒绝原假设,二个数据分布不同,有显著
做参数与非参数检验,先看样本是不是正态分布,我们常分析的就是对比几组数据之间的谁好谁坏,看下差异,以二组数据对比为例,可以用T检验或着方差分析(statsmodel也可以做),如果是正态分布的数据,对比就用均值就可以了