作为一名数据科学家,当你收到一组新的、不熟悉的数据时,你会采取什么第一步?熟悉数据。
本文着重回答了这个问题,通过一次只分析一个变量的方式,这称为单变量分析。当我们面对一个不熟悉的数据集时,可以利用单变量分析来熟悉数据。它描述和总结数据,以发现不仅仅通过查看整体数据就可以轻松观察到的模式。
执行单变量分析有各种方法,在本文中,我们将介绍其中一些最常见的方法,包括频率分析、数值和视觉总结(例如直方图和箱线图)以及数据透视表。
与我的其他文章类似,学习将通过练习题和答案来实现。在需要时,我将在问题中包含提示和解释,以使学习过程更轻松。最后,我用来创建这个练习的笔记本也链接在文章底部,你可以下载、运行并跟随练习。
让我们开始吧!
为了练习单变量分析,我们将使用UCI机器学习仓库中关于各种葡萄酒的化学分析的数据集,该数据集基于“数据探索、分类和相关性的可扩展包”(Forina, M. et al, 1998),可以从此链接(CC BY 4.0)下载。
让我们首先导入今天要使用的库,然后将数据集读入数据框,并查看数据框的前5行,以熟悉数据。
# Import libraries
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
# Read the data
df = pd.read_csv('wine.csv')
# Return top 5 rows of the dataframe
df.head()
结果:
如上所示,这些是各种葡萄酒的化学分析。我们将主要使用一些列,我将简要解释如下:
既然我们熟悉了将要使用的列,让我们开始分析。
频率分析是描述性分析中的一个基本概念,用于研究事件发生次数。例如,如果我们掷骰子12次,得到以下结果:
[1, 3, 6, 6, 4, 5, 2, 3, 3, 6, 5, 1]
然后1的发生频率是2,因为1在掷骰子中出现了两次。现在让我们看看如何在Python中实现这个概念。我们将使用“value_counts”方法来查看数据框中每个不同变量值发生的次数。但由于“value_counts”不包括空值,让我们首先看看是否有任何空值。
问题1:
数据框中存在多少个空值,以及在哪些列中?
答案:
# Return null values
df.isnull().sum()
结果:
根据结果,没有任何列包含空值,因此我们可以继续使用“value_counts”。让我们继续进行频率分析。
问题2:
数据集包括来自三种不同培育品种的葡萄酒信息,如列“class”中所示。数据集中每个类别有多少行?
答案:
# Apply value_counts to the df['class'] column
df['class'].value_counts()
结果:
如我们所见,有三个类别(如问题中所述),来自培育品种2的实例有71个,来自培育品种1的实例有59个,来自培育品种3的实例有48个。
问题3:
创建一个名为“class_verbose”的新列,将“class”列中的值替换为下表中定义的值。然后确定每个新类别存在多少实例,这应该与问题2的结果相匹配。
答案:
# Replace according to the mapping table provided above
df['class_verbose'] = df['class'].replace({1 : 'cultivar_a', 2 : 'cultivar_b', 3 : 'cultivar_c'})
# Compare results
df.class_verbose.value_counts()
结果:
如预期的那样,每个类别的实例数与问题2的结果保持一致。
在本节中,我们将更多地关注定量变量,并探讨总结此类列的方法。一种简单的方法是使用“describe”方法。让我们在下一个示例中看看它是如何工作的。
问题4:
使用“describe”方法创建数据集的“alcohol”列的数值总结。
答案:
# Use describe method
df['alcohol'].describe()
正如你所看到的,这是一个非常方便的方法,用于概述数据的分布,而不是手动生成这些值。让我们在下一个问题中手动生成一些值以进行练习。
问题5:
返回数据集的“alcohol”列的以下值:均值、标准差、最小值、第25、50和75百分位数以及最大值。
答案:
这些值可以使用Pandas和/或NumPy(等等)来计算。我在这里提供了两种方法供参考。
# Approach 1 - Using Pandas
print(f"Using Pandas:")
print(f"mean: {df.alcohol.mean()}")
print(f"standard_deviation: {df.alcohol.std()}")
print(f"minimum: {df.alcohol.min()}")
print(f"25th_percentile: {df.alcohol.quantile(0.25)}")
print(f"50th_percentile: {df.alcohol.quantile(0.50)}")
print(f"75th_percentile: {df.alcohol.quantile(0.75)}")
print(f"maximum: {df.alcohol.max()}\n")
# Approach 2 - Using NumPy
print(f"Using NumPy:")
print(f"mean: {np.mean(df.alcohol)}")
print(f"standard_deviation: {np.std(df.alcohol, ddof = 1)}")
print(f"minimum: {np.min(df.alcohol)}")
print(f"25th_percentile: {np.percentile(df.alcohol, 25)}")
print(f"50th_percentile: {np.percentile(df.alcohol, 50)}")
print(f"75th_percentile: {np.percentile(df.alcohol, 75)}")
print(f"maximum: {np.max(df.alcohol)}\n")
结果:
问题6:
酒精含量小于1.5的葡萄酒的平均酒精含量与酒精含量大于或等于1.5的葡萄酒的平均酒精含量相比如何?
答案:
lower_bound = np.mean(df['alcohol'][df.malic_acid < 1.5])
upper_bound = np.mean(df['alcohol'][df.malic_acid >= 1.5])
print(f"lower: {lower_bound}")
print(f"upper: {upper_bound}")
结果:
在本节中,我们将查看可视化定量变量的方法。我们将使用直方图和箱线图,我将在开始问题之前介绍它们。
直方图是一种可视化工具,通过计算每个箱中的实例(或观察)数量来表示一个或多个变量的分布。在本文中,我们将专注于单变量直方图,使用seaborn的“histplot”类。让我们看一个例子。
问题7:
创建一个关于数据集中酒精含量的直方图。
答案:
# Create the histogram
sns.histplot(df.alcohol)
plt.show()
结果:
这显示了每个酒精含量区间内有多少实例。例如,看起来包含13.5酒精含量的区间有最多的实例。
箱线图展示了定量数据的分布。箱子显示了数据的四分位数(即第25百分位数或Q1、第50百分位数或中位数和第75百分位数或Q3),而须(whiskers)显示了分布的其余部分,除了被确定为离群值的部分,离群值被定义为超出Q1或Q3以下1.5倍四分位距(IQR)或Q3以上。IQR是Q1和Q3之间的距离,如下所示。
让我们看看一些示例。
问题8:
创建一个箱线图,比较三个培育品种之间的酒精分布。
答案:
# Assign a figure size
plt.figure(figsize = (15, 5))
# Create the box plots
sns.boxplot(data = df, x = 'class_verbose', y = 'alcohol')
plt.show()
结果:
在数据中查找模式的一种方法是将其分解成较小的子集或分层,然后分别分析这些分层。每个分层可能会有新的发现。为了演示这种技术,我们将查看一些示例。
问题9:
创建一个名为“malic_acid_level”的新列,将“malic_acid”列的值分解为以下三个段落:
然后在每个分层的酒精分布中创建一组箱线图。你能看到任何新的模式吗?
答案:
首先,让我们在将“malic_acid”分解为问题中描述的分层之前,为酒精含量创建一个箱线图。然后,我们将应用分层并在视觉上进行比较。
# Assign a figure size
plt.figure(figsize = (5, 5))
# Create the box plots
sns.boxplot(data = df, y = 'alcohol')
plt.show()
结果:
如上所示,Q1、中位数和Q3分别约为12.4、13和13.7。现在让我们看看这些值在“malic_acid”分层中如何变化。
# Calculate the cut levels
minimum = np.min(df.malic_acid)
p33 = np.percentile(df.malic_acid, 33)
p66 = np.percentile(df.malic_acid, 66)
maximum = np.max(df.malic_acid)
# Create the new column
df['malic_acid_level'] = pd.cut(df.malic_acid, [minimum, p33, p66, maximum])
# Assign a figure size
plt.figure(figsize = (15, 5))
# Create the box plots
sns.boxplot(data = df, x = 'malic_acid_level', y = 'alcohol')
plt.show()
结果:
这非常有趣。回想一下,中位数酒精含量约为13?现在我们看到了中位数在“malic_acid”水平之间有一些变化。例如,我们看到蓝色和橙色箱线图的中位数之间存在相对较大的差异,这两者分别代表了不同的分层,分别表示低和中等范围的“malic_acid”水平。另一个观察是,蓝色箱线图的范围要大得多(从约11到约14.8),而绿色箱线图的“malic_acid”水平较高,范围较小(从约11.5到约14.4)。
让我们进一步将其分层为一个练习。
问题10:
创建与上一个问题类似的箱线图,但适用于每个培育品种。
答案:
# Assign a figure size
plt.figure(figsize = (15, 5))
# Create the box plots
sns.boxplot(data = df, x = 'malic_acid_level', y = 'alcohol', hue = 'class_verbose')
plt.show()
结果:
接下来,让我们尝试以表格方式总结这些内容。
数据透视表是分组值的表格表示,它在某些离散类别内聚合数据。让我们看一些示例来了解实际中的数据透视表。
问题11:
创建一个数据透视表,指示在每个“malic acid level”内每个培育品种有多少个酒精含量的实例。
答案:
# Create the pivot table
pd.pivot_table(df[['malic_acid_level', 'class_verbose', 'alcohol']], index = ['malic_acid_level', 'class_verbose'], aggfunc = 'count')
结果:
让我们读取其中一行以了解结果。第一行告诉我们,在(0.74, 1.67]的“malic_acid_level”内有16个“cultivar_a”的实例。如上面的脚本所示,我们在这个数据透视表中使用“count”作为聚合函数,因为问题要求在这些离散类别中有多少个实例。还有其他可以使用的聚合函数。让我们在下一个示例中尝试其中一个。
问题12:
创建一个数据透视表,显示每个“malic acid level”内每个培育品种的平均酒精含量。
答案:
请注意,这次我们要实施一个聚合函数来计算平均值。
# Create the pivot table
pd.pivot_table(df[['malic_acid_level', 'class_verbose', 'alcohol']], index = ['malic_acid_level', 'class_verbose'], aggfunc = 'mean')
结果:
以下是包含问题和答案的笔记本,你可以下载并练习。
https://gist.github.com/fmnobar/cdb630d53cc86be9269fba7049887c8f#file-univariateanalysis-ipynb
在本文中,我们讨论了如何在通过数据了解新空间的第一步中利用单变量分析。
在开始对数据做任何推断之前,我们希望了解数据的相关信息,而单变量分析为我们提供了一种逐个变量地了解每个变量的工具。
作为单变量分析的一部分,我们学会了如何实施频率分析,如何将数据汇总到各种子集/分层中,以及如何利用直方图和箱线图等可视化工具来更好地了解数据的分布。