Python数据分析

55次阅读
没有评论

共计 8005 个字符,预计需要花费 21 分钟才能阅读完成。

方法论

OSEMN(Obtain, Scrub, Explore, Model, iNterpret)是一种常用的数据分析方法论,包括数据获取、数据清洗、数据探索、建模和解释等步骤。OSEMN 方法论强调了数据分析过程中数据质量和数据探索的重要性。

数理统计

数理统计分为如下两类:

  • 描述统计
  • 推断统计

描述性统计分析

描述性统计所提取统计的信息,称为 统计量,主要包括以下几个方面:

  • 频数与频率
  • 集中趋势分析

    • 均值
    • 中位数
    • 众数
    • 分位数
  • 离散程度分析

    • 极差
    • 方差
    • 标准差
  • 分布形状

    • 偏度
    • 峰度

频数与频率

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
import warnings

## 设置 seaborn 绘图样式与字体规模
sns.set(style="darkgrid", font_scale=1.2)
plt.rcParams["font.family"] = "SimHei"  # 设置中文字体
plt.rcParams["axes.unicode_minus"] = False  # 是否使用 Unicode 字符集中的负号
warnings.filterwarnings("ignore")  # 忽略警告信息

iris = load_iris()  # 加载鸢尾花数据集
print(iris.data.shape)  # NumPy 数组 (150, 4)
print(iris.target.shape)  # NumPy 数组 (150,) 每朵鸢尾花对应的类别,取值为 0,1,2
print(iris.feature_names)  # 特征列的名称
print(iris.target_names)  # 鸢尾花类别的名称,即数值 0,1,2 分别对应的类别

## 合并数据
data = np.concatenate([iris.data, iris.target.reshape(-1, 1)], axis=1)  # 横向拼接,- 1 的作用是自动计算行
data = pd.DataFrame(data, columns=["sepal_length", "sepal_width", "petal_length", "petal_width", "type"])
data.sample(10)  # 随机抽样

frequency = data["type"].value_counts()  # 计算类别出现的频数
print(frequency)
percentage = frequency * 100 / len(data)  # 计算类别出现的频率
print(percentage)

sns.countplot(x="type", data=data)  # 绘制 type 列不同取值出现的次数
plt.show()

集中趋势

mean = data["sepal_length"].mean()  # 花萼长度的均值
median = data["sepal_length"].median()  # 中位数
s = data["sepal_length"].mode()  # mode 方法返回 Series 类型
mode = s.iloc[0]  # 众数
print(mean, median, mode)

sns.distplot(data["sepal_length"])  # 绘制数据的发布,直方图 + 密度图
## 绘制垂直线
plt.axvline(mean, ls="-", color="r", label=" 均值 ")
plt.axvline(median, ls="-", color="g", label=" 中值 ")
plt.axvline(mode, ls="-", color="indigo", label=" 众数 ")
plt.legend()

x = [1, 3, 10, 15, 18, 20, 23, 40]
print(np.quantile(x, q=[0.25, 0.5, 0.75]))  # 计算分位数
print(np.percentile(x, q=[25, 50, 75]))

s = pd.Series(x)
print(s.describe())  # pandas 中计算分位数
print(s.describe(percentiles=[0.25, 0.9]))

离散程度

sub = np.ptp(data["sepal_length"])  # 极差
var = data["sepal_length"].var()  # 方差
std = data["sepal_length"].std()  # 标准差
print(sub, var, std)

plt.figure(figsize=(15, 4))  # 设置图形大小,宽 * 高,单位为英寸
plt.ylim(-0.5, 1.5)
plt.plot(data["petal_length"], np.zeros(len(data)), linestyle="", marker="o", markersize=10, color="g", label=" 花瓣长度 ")
plt.plot(data["petal_width"], np.ones(len(data)), ls="", marker="o", ms=10, color="r", label=" 花瓣宽度 ")
plt.axvline(data["petal_length"].mean(), ls="--", color="g", label=" 花瓣长度均值 ")
plt.axvline(data["petal_width"].mean(), ls="--", color="r", label=" 花瓣宽度均值 ")
plt.legend()

分布形状

## 构造左偏分布数据
t1 = np.random.randint(1, 11, size=100)
t2 = np.random.randint(11, 21, size=500)
t3 = np.concatenate([t1, t2])
left_skew = pd.Series(t3)
## 构造右偏分布数据
t1 = np.random.randint(1, 11, size=500)
t2 = np.random.randint(11, 21, size=100)
t3 = np.concatenate([t1, t2])
right_skew = pd.Series(t3)

print(left_skew.skew(), right_skew.skew())
## 绘制核密度图
sns.kdeplot(left_skew, shade=True, label=" 左偏 ")
sns.kdeplot(right_skew, shade=True, label=" 右偏 ")
plt.legend()

## 标准正态分布
standard_normal = pd.Series(np.random.normal(0, 1, size=10000))
print(" 标准正态分布峰度:", standard_normal.kurt(), " 标准差:", standard_normal.std())

sns.kdeplot(standard_normal, label=" 标准正态分布 ")
sns.kdeplot(data["sepal_width"], label=" 花萼宽度 ")
sns.kdeplot(data["petal_length"], label=" 花瓣长度 ")
plt.legend()

推断统计分析

总体的参数往往是未知的,为了获取总体的参数,就需要通过样本统计量来估计总体参数。

参数估计

点估计

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
import warnings

sns.set(style="darkgrid", font_scale=1.2)
plt.rcParams["font.family"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
warnings.filterwarnings("ignore")

iris = load_iris()
data = np.concatenate([iris.data, iris.target.reshape(-1, 1)], axis=1)
data = pd.DataFrame(data, columns=["sepal_length", "sepal_width", "petal_length", "petal_width", "type"])
print(data["petal_length"].mean())

点估计容易受到随机抽样的影响,可能无法保证结论的准确性。但是,样本来自于总体,样本还是能够体现出总体的一些特征的。

区间估计

区间估计使用一个置信区间与置信度,表示总体参数有多少可能(置信度)会在该范围(置信区间)内。

样本均值分布的标准差,称为标准误差,简称标准误。

根据中心极限定理,如果多次抽样 (总体的均值为 μ,方差为σ*σ),则样本均值构成的正态分布,满足样本均值~N(μ, σ*σ/n),如果只对总体进行一次抽样,则该样本的均值有 95% 的概率会在(μ-2σ, μ+2σ) 范围内,仅有 5% 的概率会在 (μ-2σ, μ+2σ) 范围外。根据小概率事件 (很小的概率在一次抽样中基本不会发生),如果抽样的样本均值在(μ-2σ, μ+2σ) 之外,就可以认为,本次抽样来自的总体,该总体的均值并非是我们所期望的均值。

通常,以 2 倍标准差作为判依据,即以样本均值为中心,正负 2 倍标准差构成的区间,就是置信区间。而 2 倍标准差包含了 95% 的数据,因此,此时的置信度为 95%。换言之,有 95% 的信心认为总体的均值会在置信区间之内。

a = np.array([0.497, 0.506, 0.518, 0.524, 0.498, 0.511, 0.520, 0.515, 0.512])
mean, std = 0.5, 0.015  # 总体均值与标准差
sample_mean = a.mean()  # 样本均值
se = std / np.sqrt(len(a))  # 标准误
left, right = sample_mean - 1.96 * se, sample_mean + 1.96 * se
print(f" 置信区间:({left:.3f}, {right:.3f})")

plt.plot(mean, 0, marker="*", color="orange", ms=15, label=" 总体均值 ")
plt.plot(sample_mean, 0, marker="o", color="r", label=" 样本均值 ")
plt.hlines(0, xmin=left, xmax=right, color="b", label=" 置信区间 ")
plt.axvline(left, 0.4, 0.6, color="r", ls="--", label=" 左边界 ")
plt.axvline(right, 0.4, 0.6, color="g", ls="--", label=" 右边界 ")
plt.legend()

假设检验

  1. 设置原假设与备择假设

    • 原假设:μ=μ0=0.5kg(机器运作正常)
    • 备择假设:μ≠μ0≠0.5kg(机器运作不正常)
  2. 设置显著性水平,设置 α =0.05
  3. 根据问题选择假设检验的方式。袋装糖的净重呈正态分布,总体标准差已知,选择 Z 检验
  4. 计算统计量,并通过统计量获取 P 值。根据选择的假设检验,进行计算

Z 检验

Z 检验用于正态总体均值的检验,即用来检验总体的均值 (末知) 是否与假设的均值相同。

from scipy import stats

a = np.array([0.497, 0.506, 0.518, 0.524, 0.498, 0.511, 0.520, 0.515, 0.512])
mean, std = 0.5, 0.015  # 总体均值与标准差
sample_mean = a.mean()  # 样本均值
se = std / np.sqrt(len(a))  # 标准误
Z = (sample_mean - mean) / se  # Z 统计量
print(" 统计量 Z:", Z)
## 计算 P 值
## sf 函数,根据参数指定的统计量,返回该统计量与右侧图像围成的面积值
## 实际上,P 值就是通过概率密度函数计算出来的面积,即概率值
P = 2 * stats.norm.sf(abs(Z))
print("P-Value 值:", P)

由结果可知,Z 值偏离超过了 2 倍的标准差,P 值也就小于 0.05。因此,在显著性水平 α =0.05 下,P<α,故拒绝原假设,接受备择假设,即认为机器运作是不正常的。

右边假设检验

右边假设检验的拒绝域在正态分布的右侧区域。

  • 原假设:μ≤μ0=0.5kg(机器运作正常)
  • 备择假设:μ>μ0≠0.5kg(机器运作不正常)
## Z 统计量与之前计算的方式相同
P = stats.norm.sf(Z)
print("P-Value 值:", P)

P<α,拒绝原假设,认为机器运作是不正常的。

左边假设检验

左边假设检验的拒绝域在正态分布的左侧区域。

  • 原假设:μ≥μ0=0.5kg(机器运作正常)
  • 备择假设:μ<μ0≠0.5kg(机器运作不正常)
## cdf 函数,返回该统计量与左侧图像围成的面积值
P = stats.norm.cdf(Z)
print("P-Value 值:", P)

P>α,维持原假设,认为机器运作是正常的。

假设检验的两类错误

接受 H0 拒绝 H0
H0 为真 正确 I 类错误(α 错误,去真错误)
H0 为假 II 类错误(β 错误,取伪错误) 正确

t 检验

与 Z 检验的应用场景类似,t 检验也用于正态总体均值的检验。只不过,t 检验的正态总体,标准差是未知的。

t 检验分为三种类型:

  1. 单样本 t 检验
  2. 两独立样本 t 检验
  3. 两配对样本 t 检验
  • t 检验原假设为等值假设,备择假设为不等值假设(双边检验)
  • t 检验通过构造 t 统计量来实现
  • t 统计量服从 t 分布
  • 随着样本容量的增大,t 分布逐渐接近于标准正态分布

单样本 t 检验

单样本 t 检验 前提条件为:

  1. 总体服从正态分布
  2. 总体方差 (标准差) 末知

    mean = data["petal_length"].mean()
    std = data["petal_length"].std()
    t = (mean - 3.5) / (std / np.sqrt(len(data)))  # t 统计量
    print("t 统计量:", t)
    P = 2 * stats.t.sf(abs(t), df=len(data["petal_length"]) - 1)
    print("P-Value 值:", P)
    
    ## 此外,也可通过 scipy 提供的相关方法来进行 t 检验的计算,无需自行计算
    stats.ttest_1samp(data["petal_length"], 3.5)

两独立样本 t 检验

两独立样本 t 检验 用于检验两个正态总体的均值是否相同,其前提条件为:

  • 1. 两个样本是独立的
  • 2. 两个样本所在的总体服从正态分布
  • 3. 总体方差未知

由于方差是否齐性(相同),会影响 t 统计量的计算。因此,在进行两独立样本 t 检验时,首先需要进行方差齐性检验。可以通过 Levene 检验来判断两个正态总体的方差是否相同。

## A,B 两个学校的学生数据
school_A = np.array([169.7, 163.1, 179.0, 163.4, 180.2, 160.9, 177.3, 160.7, 169.9, 163.4])
school_B = np.array([163.6, 180.0, 171.8, 174.7, 164.3, 173.9, 167.0, 162.9, 164.2, 171.6])
print(school_A.mean(), school_B.mean())
## 使用 levene 检验判断两个样本背后的总体,其方差是否一致
stats.levene(school_A, school_B)

P>0.05,因此可以认为两个总体的方差是相同的。然后,就可以使用两独立样本 t 检验,来验证两个正态总体的均值是否相同。

stats.ttest_ind(school_A, school_B, equal_var=False)

P>0.05,因此维持原假设,认为两个正态总体的均值是相同的,即 A 与 B 两个学校的平均身高相同。

两配对样本 t 检验

两配对样本 t 检验,是同一组受试者产生两组数据,然后,验证两组数据是否存在显著性差异的方式。两组数据可以是:

  1. 受试者在接受处理前与处理后的数据
  2. 受试者在接受两种不同的处理方式,分别产生的数据

两配对样本 t 检验的前提条件为 :

  1. 配对样本差值独立
  2. 配对样本差值服从正态分布
  3. 总体标准差末知
## 培训前体重数据
train_before = np.array([98.8, 92.0, 94.9, 101.2, 99.3, 85.1, 94.8, 89.2, 89.5, 92.1])
## 培训后体重数据
train_after = np.array([88.4, 92.4, 90.3, 88.4, 89.3, 89.0, 92.5, 87.4, 88.9, 85.4])
## 两配对样本 t 检验(双边检验)stats.ttest_rel(train_before, train_after)

P<0.05,拒绝原假设,认为体重在培训前跟培训后是不同的。

如果没有充足的证据证明减肥有效,则应该按照减肥无效处理。则:

  • 原假设:D≤0(减肥无效)
  • 备择假设:D>0(减肥有效)
sta = stats.ttest_rel(train_before, train_after).statistic  # 获取统计量
P = stats.t.sf(sta, df=len(train_before) - 1)
print(P)

P<0.05,拒绝原假设,认为减肥有效。

卡方检验

卡方检验用于检测变量的理论频数与实际频数是否一致。

卡方检验可以分为两个类别 :

  1. 单个变量
  2. 两个变量

关于卡方检验,说明如下 :

  1. 卡方检验原假设为理论频数与实际频数一致,备择假设为理论频数与实际频数不一致(双边假设)
  2. 卡方检验为非参数检验,不对总体进行任何假设,因此也不涉及关于总体分布的参数
  3. 卡方检验通过卡方统计量来实现
  4. 卡方统计量服从卡方分布

单个变量

卡方检验 (单个变量) 的前提条件为 :

  1. 观测值之间相互独立
  2. 变量每个取值的理论频数与实际频数≥5

举例:某手机在一段时间内,销量为 200,各种颜色销量如下表所示:

红色 绿色 黄色 紫色 白色
43 50 24 31 52

请问该款手机的颜色对销售是否会造成影响?

o = [43, 50, 24, 31, 52]
e = [40, 40, 40, 40, 40]
stats.chisquare(f_obs=o, f_exp=e)

P<0.05,拒绝原假设,认为理论频数与实际频数不一致,得出结论:手机颜色对销售确实造成了影响。

两个变量

举例:某手机在一段时间内,每种颜色与性别的销量如下表所示:

红色 绿色 黄色 紫色 白色
43 50 24 31 52
38 42 30 29 45

请问该款手机的购买用户中,性别与手机颜色是否具有关联?

根据随机变量独立性的定义,如果两个随机变量 A 与 B 是独立的,则有:

P(AB) = P(A)*P(B)

因此,可以根据该定义来计算在性别与颜色两个变量独立的情况下,每个变量取值的期望概率(理论概率。例如,计算性别为男,颜色为红色为例。

P(A= 男, B= 红色) = P(A= 男) * P(B= 红色)
=(43 + 50 + 24 + 31 + 52) / 384 * (43 + 38) / 384
=0.52083 * 0.2109
=0.10986328125

有了期望概率,就可以很容易计算每个取值的期望频数:
Expected(A= 男, B= 红色) = count * P(A= 男, B= 红色)
=384 *0.10986328125
=42.1875

observed = [[43, 50, 24, 31, 52], [38, 42, 30, 29, 45]]
stats.chi2_contingency(observed)

P>0.05,维持原假设,认为理论频数与实际频数一致(意味着两个变量是独立的),得出结论:性别对手机颜色没有影响,他们之间没有关系。

正文完
 0
三毛笔记
版权声明:本站原创文章,由 三毛笔记 于2023-08-04发表,共计8005字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)