UCB Data100:数据科学的原理和技巧:第六章到第十章(2)https://developer.aliyun.com/article/1427169
7.10 评估直方图
直方图允许我们通过它们的形状来评估分布。我们可以分析直方图的一些属性:
- 偏斜和尾部
- 左偏 vs 右偏
- 左尾 vs 右尾
- 异常值
- 使用百分位数
- 模式
- 最常出现的数据
7.10.1 偏斜和尾部
直方图的偏斜描述了其“尾巴”延伸的方向。- 具有较长右尾的分布是右偏的(例如人均国民总收入
)。在右偏分布中,少数大的异常值将均值“拉”到中位数的右侧。- 具有较长左尾的分布是左偏的(例如改善的饮水来源
)。在左偏分布中,少数小的异常值将均值“拉”到中位数的左侧。
在分布的右尾和左尾大小相等的情况下,它是对称的。均值大约等于中位数。将均值视为分布的平衡点。
sns.histplot(data = wb, x = 'Gross national income per capita, Atlas method: $: 2016', stat = 'density'); plt.title('Distribution with a long right tail')
Text(0.5, 1.0, 'Distribution with a long right tail')
sns.histplot(data = wb, x = 'Access to an improved water source: % of population: 2015', stat = 'density'); plt.title('Distribution with a long left tail')
Text(0.5, 1.0, 'Distribution with a long left tail')
7.10.2 异常值
粗略地说,异常值被定义为与其他值相比距离异常大的数据点。让我们更具体地描述一下。正如您可能在之前的箱线图信息图表中观察到的,我们将异常值定义为超出触须的数据点。具体来说,小于[第 1 四分位数 − ( 1.5 × 第 1 四分位数 - (1.5 \times第1四分位数−(1.5× IQR)]或大于[第 3 四分位数 + ( 1.5 × 第 3 四分位数 + (1.5 \times第3四分位数+(1.5× IQR)]的值。
7.10.3 模式
在 Data 100 中,我们将直方图的“模式”描述为分布中的峰值。然而,通常很难确定什么算作自己的“峰值”。例如,HIV 率在不同国家之间的分布的峰值数量取决于我们绘制的直方图箱数。
如果我们将箱数设置为 5,则分布呈单峰分布。
# Rename the very long column name for convenience wb = wb.rename(columns={'Antiretroviral therapy coverage: % of people living with HIV: 2015':"HIV rate"}) # With 5 bins, it seems that there is only one peak sns.histplot(data=wb, x="HIV rate", stat="density", bins=5) plt.title("5 histogram bins");
# With 10 bins, there seem to be two peaks sns.histplot(data=wb, x="HIV rate", stat="density", bins=10) plt.title("10 histogram bins");
# And with 20 bins, it becomes hard to say what counts as a "peak"! sns.histplot(data=wb, x ="HIV rate", stat="density", bins=20) plt.title("20 histogram bins");
在某种程度上,正是这些模糊性促使我们考虑使用核密度估计(KDE),我们将在下一讲中更多地探讨。
八、可视化 II
译者:飞龙
学习成果
- 了解用于绘制分布和估计密度曲线的 KDE。
- 使用转换分析两个变量之间的关系。
- 根据可视化理论概念评估可视化的质量。
8.0.1 核密度估计
8.0.1.1 KDE 理论
现在让我们深入研究核密度估计。**核密度估计(KDE)**是一种平滑的连续函数,它近似表示一条曲线。它们允许我们表示分布的一般趋势,而不专注于细节,这对于分析数据集的广泛结构是有用的。
更正式地说,KDE 试图近似我们的数据集抽取的潜在概率分布。您可能在其他课程中遇到过概率分布的概念;如果没有,我们将在下一讲中详细讨论。现在,您可以将概率分布视为描述我们在数据集中抽取特定值的可能性有多大。
KDE 曲线估计随机变量的概率密度函数。考虑下面的例子,我们使用sns.displot
绘制了直方图(包含我们实际收集的数据点)和 KDE 曲线(代表近似概率分布,从中抽取了这些数据)使用我们之前使用过的世界银行数据集(wb
)。
代码
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns wb = pd.read_csv("data/world_bank.csv", index_col=0) wb = wb.rename(columns={'Antiretroviral therapy coverage: % of people living with HIV: 2015':"HIV rate", 'Gross national income per capita, Atlas method: $: 2016':'gni'}) wb.head()
Continent | Country | Primary completion rate: Male: % of relevant age group: 2015 | Primary completion rate: Female: % of relevant age group: 2015 | Lower secondary completion rate: Male: % of relevant age group: 2015 | Lower secondary completion rate: Female: % of relevant age group: 2015 | Youth literacy rate: Male: % of ages 15-24: 2005-14 | Youth literacy rate: Female: % of ages 15-24: 2005-14 | Adult literacy rate: Male: % ages 15 and older: 2005-14 | Adult literacy rate: Female: % ages 15 and older: 2005-14 | … | Access to improved sanitation facilities: % of population: 1990 | Access to improved sanitation facilities: % of population: 2015 | Child immunization rate: Measles: % of children ages 12-23 months: 2015 | Child immunization rate: DTP3: % of children ages 12-23 months: 2015 | Children with acute respiratory infection taken to health provider: % of children under age 5 with ARI: 2009-2016 | Children with diarrhea who received oral rehydration and continuous feeding: % of children under age 5 with diarrhea: 2009-2016 | Children sleeping under treated bed nets: % of children under age 5: 2009-2016 | Children with fever receiving antimalarial drugs: % of children under age 5 with fever: 2009-2016 | Tuberculosis: Treatment success rate: % of new cases: 2014 | Tuberculosis: Cases detection rate: % of new estimated cases: 2015 | |
0 | Africa | Algeria | 106.0 | 105.0 | 68.0 | 85.0 | 96.0 | 92.0 | 83.0 | 68.0 | … | 80.0 | 88.0 | 95.0 | 95.0 | 66.0 | 42.0 | NaN | NaN | 88.0 | 80.0 |
1 | Africa | Angola | NaN | NaN | NaN | NaN | 79.0 | 67.0 | 82.0 | 60.0 | … | 22.0 | 52.0 | 55.0 | 64.0 | NaN | NaN | 25.9 | 28.3 | 34.0 | 64.0 |
2 | Africa | Benin | 83.0 | 73.0 | 50.0 | 37.0 | 55.0 | 31.0 | 41.0 | 18.0 | … | 7.0 | 20.0 | 75.0 | 79.0 | 23.0 | 33.0 | 72.7 | 25.9 | 89.0 | 61.0 |
3 | Africa | Botswana | 98.0 | 101.0 | 86.0 | 87.0 | 96.0 | 99.0 | 87.0 | 89.0 | … | 39.0 | 63.0 | 97.0 | 95.0 | NaN | NaN | NaN | NaN | 77.0 | 62.0 |
5 | Africa | Burundi | 58.0 | 66.0 | 35.0 | 30.0 | 90.0 | 88.0 | 89.0 | 85.0 | … | 42.0 | 48.0 | 93.0 | 94.0 | 55.0 | 43.0 | 53.8 | 25.4 | 91.0 | 51.0 |
5 行×47 列
import seaborn as sns import matplotlib.pyplot as plt sns.displot(data = wb, x = 'HIV rate', \ kde = True, stat = "density") plt.title("Distribution of HIV rates");
/Users/Ishani/micromamba/lib/python3.9/site-packages/seaborn/axisgrid.py:118: UserWarning: The figure layout has changed to tight
注意,平滑的 KDE 曲线在直方图箱更高时更高。你可以将 KDE 曲线的高度看作代表我们随机抽样具有相应值的数据点的“可能性”有多大。这在直观上是有意义的 - 如果我们已经收集了更多具有特定值的数据点(导致一个高的直方图箱),那么如果我们随机抽样另一个数据点,我们更有可能抽样到一个具有类似值的数据点(导致高的 KDE 曲线)。
概率密度函数下的面积应该始终积分为 1,表示分布的总概率应始终总和为 100%。因此,KDE 曲线下始终有一个面积为 1。
8.0.1.2 构建 KDE
我们使用三个步骤进行核密度估计。
- 在每个数据点放置一个核。
- 将核函数归一化,使其总面积为 1(跨所有核函数)。
- 对归一化的核求和。
我们马上会解释“核”是什么。
为了简化,让我们为一个由 5 个数据点构成的小型人工生成的数据集[ 2.2 , 2.8 , 3.7 , 5.3 , 5.7 ] [2.2, 2.8, 3.7, 5.3, 5.7][2.2,2.8,3.7,5.3,5.7]构建一个 KDE。在下面的图中,每个垂直条代表一个数据点。
代码
data = [2.2, 2.8, 3.7, 5.3, 5.7] sns.rugplot(data, height=0.3) plt.xlabel("Data") plt.ylabel("Density") plt.xlim(-3, 10) plt.ylim(0, 0.5);
我们的目标是创建以下由sns.kdeplot
自动生成的 KDE 曲线。
代码
sns.kdeplot(data) plt.xlabel("Data") plt.xlim(-3, 10) plt.ylim(0, 0.5);
8.0.1.2.1 步骤 1:在每个数据点上放置一个核
要开始生成密度曲线,我们需要选择一个核和带宽值(α \alphaα)。这些究竟是什么?
核是一个密度曲线。它是试图捕捉我们采样数据中每个数据点的随机性的数学函数。为了解释这意味着什么,考虑我们数据集中的一个数据点:2.2 2.22.2。我们通过随机抽样得到了这个数据点(你可以想象2.2 2.22.2代表实验中进行的单次测量,例如)。如果我们抽样一个新的数据点,可能会得到一个略有不同的值。它可能高于2.2 2.22.2;也可能低于2.2 2.22.2。我们假设任何未来抽样的数据点可能与我们已经绘制的数据值相似。这意味着我们的核 - 我们对随机抽样任何新值的概率的描述 - 在我们已经绘制的数据点处最大,但在其上下仍具有非零概率。任何核下的面积应该积分为 1,表示抽取新数据点的总概率。
带宽值通常用 α \alphaα 表示,表示核的宽度。 α \alphaα 的值越大,核函数就会变得宽而短,而值越小,核函数就会变得窄而高。
下面,我们在数据点2.2 2.22.2上放置了一个高斯核,用橙色绘制。高斯核简单地是正态分布,在 Data 8 中可能称为钟形曲线。
代码
def gaussian_kernel(x, z, a): # We'll discuss where this mathematical formulation came from later return (1/np.sqrt(2*np.pi*a**2)) * np.exp((-(x - z)**2 / (2 * a**2))) # Plot our datapoint sns.rugplot([2.2], height=0.3) # Plot the kernel x = np.linspace(-3, 10, 1000) plt.plot(x, gaussian_kernel(x, 2.2, 1)) plt.xlabel("Data") plt.ylabel("Density") plt.xlim(-3, 10) plt.ylim(0, 0.5);
要开始创建我们的 KDE,我们在我们的数据集中的每个数据点上放置一个核。对于我们的 5 个数据点的数据集,我们将有 5 个核。
代码
# You will work with the functions below in Lab 4 def create_kde(kernel, pts, a): # Takes in a kernel, set of points, and alpha # Returns the KDE as a function def f(x): output = 0 for pt in pts: output += kernel(x, pt, a) return output / len(pts) # Normalization factor return f def plot_kde(kernel, pts, a): # Calls create_kde and plots the corresponding KDE f = create_kde(kernel, pts, a) x = np.linspace(min(pts) - 5, max(pts) + 5, 1000) y = [f(xi) for xi in x] plt.plot(x, y); def plot_separate_kernels(kernel, pts, a, norm=False): # Plots individual kernels, which are then summed to create the KDE x = np.linspace(min(pts) - 5, max(pts) + 5, 1000) for pt in pts: y = kernel(x, pt, a) if norm: y /= len(pts) plt.plot(x, y) plt.show(); plt.xlim(-3, 10) plt.ylim(0, 0.5) plt.xlabel("Data") plt.ylabel("Density") plot_separate_kernels(gaussian_kernel, data, a = 1)
8.0.1.2.2 步骤 2:将核归一化为总面积为 1
前面我们说每个核的面积为 1。早些时候,我们还说我们的目标是使用这些核构建一个总面积为 1 的 KDE 曲线。如果我们直接将核求和,我们将得到一个积分面积为(5 个核)× \times×(每个 1 的面积)= 5。为了避免这种情况,我们将归一化我们的每个核。这涉及将每个核乘以1 / ( # 数据点 ) 1/(\#\:\text{数据点})1/(#数据点)。
在下面的单元格中,我们将我们的 5 个核心中的每一个乘以1 5 \frac{1}{5}51来应用归一化。
代码
plt.xlim(-3, 10) plt.ylim(0, 0.5) plt.xlabel("Data") plt.ylabel("Density") # The `norm` argument specifies whether or not to normalize the kernels plot_separate_kernels(gaussian_kernel, data, a = 1, norm = True)
8.0.1.2.3 步骤 3:求和归一化核心
我们的 KDE 曲线是归一化核心的总和。请注意,最终曲线与我们之前看到的sns.kdeplot
生成的图相同!
代码
plt.xlim(-3, 10) plt.ylim(0, 0.5) plt.xlabel("Data") plt.ylabel("Density") plot_kde(gaussian_kernel, data, a = 1)
8.0.1.3 核函数和带宽
一个核心(对我们来说)是一个有效的密度函数。这意味着它:
- 对于所有输入,必须为非负。
- 必须积分为 1。
上面给出了一个一般的“KDE 公式”函数。
- K α ( x , x i ) K_{\alpha}(x, x_i)Kα(x,xi)是以观察
i
为中心的核心。
- 每个核心单独的面积为 1。
- x 代表数轴上的任何数字。它是我们函数的输入。
- n nn是我们观察到的数据点的数量。
- 我们乘以1 n \frac{1}{n}n1,以便 KDE 的总面积仍然为 1。
- 每个x i ∈ { x 1 , x 2 , … , x n } x_i \in \{x_1, x_2, \dots, x_n\}xi∈{x1,x2,…,xn}代表一个观察到的数据点。
- 这些是我们用来通过对这些点进行多次移位的核心来创建我们的 KDE 的。
α \alphaα(alpha)是带宽或平滑参数。
8.0.1.3.1 高斯核
最常见的核心是高斯核。高斯核等同于以观察值为中心,标准差为(这被称为带宽参数)的高斯概率密度函数。
K a ( x , x i ) = 1 2 π α 2 e − ( x − x i ) 2 2 α 2 K_a(x, x_i) = \frac{1}{\sqrt{2\pi\alpha^{2}}}e^{-\frac{(x-x_i)^{2}}{2\alpha^{2}}}Ka(x,xi)=2πα21e−2α2(x−xi)2
在这个公式中:
- x xx(无下标)代表我们绘图的 x 轴上的值
- x i x_ixi代表我们数据集中的第i ii个数据点。这是我们在数据采样过程中实际收集到的值之一。在我们之前的例子中,x i = 2.2 x_i=2.2xi=2.2。那些上过概率课的人可能会认出x i x_ixi是正态分布的均值。
- α \alphaα是带宽参数,代表我们核心的宽度。更正式地说,α \alphaα是高斯曲线的标准差。
这个(令人生畏的)公式的细节不如理解它在核密度估计中的作用重要-这个方程给了我们每个核心的形状。
较大的α \alphaα值会产生一个更宽更短的核心-当这些核心被求和时,这会导致更平滑的 KDE。相反,较小的α \alphaα值会产生一个更窄更高的核心,以及一个更嘈杂的 KDE。
高斯核,α \alphaα = 0.1
高斯核,α \alphaα = 1
高斯核,α \alphaα = 2
高斯核,α \alphaα = 10
8.0.1.4 矩箱核
核心的另一个例子是矩箱核。矩箱核为观察点内的点分配均匀密度,其他地方的密度为 0。下面的方程是以x i x_ixi为中心,带宽为α \alphaα的矩箱核。
K a ( x , x i ) = { 1 α , ∣ x − x i ∣ ≤ α 2 0 , else K_a(x, x_i) =
{1α,|x−xi|≤α20,else
\begin{cases} \frac{1}{\alpha}, & |x - x_i| \le \frac{\alpha}{2}\\ 0, & \text{else } \end{cases} K a ( x , x i ) = { α 1 , 0 , ∣ x − x i ∣ ≤ 2 α else矩箱核在实践中很少使用-我们在这里包括它是为了演示核函数可以采用任何您喜欢的形式,只要它积分为 1 并且不输出负值。
代码
def boxcar_kernel(alpha, x, z): return (((x-z)>=-alpha/2)&((x-z)<=alpha/2))/alpha xs = np.linspace(-5, 5, 200) alpha=1 kde_curve = [boxcar_kernel(alpha, x, 0) for x in xs] plt.plot(xs, kde_curve);
以带宽α \alphaα = 1 为中心的矩箱核。
右侧的图表显示了我们的 5 个数据点数据集使用 Boxcar 核和带宽α \alphaα = 1 时的密度曲线。
UCB Data100:数据科学的原理和技巧:第六章到第十章(4)https://developer.aliyun.com/article/1427171