数值型数据编程练习#
请花点时间完成以下练习,以巩固您在“「数值型数据入门」”中学到的内容。
本节内容一共包含两个部分:
-
获取数据集的统计信息,这将向您展示如何找出包含明显异常值的列。
-
找出数据集中的问题部分——本指南将引导你如何通过可视化和数学方法,发掘数据集中隐藏的异常值或错误数据。
以下为本站根据 Google 机器学习速成课程源示例代码内容整理而来,同时对其中的部分内容进行了梳理和补充,完整示例代码见文末。
1. 数据集的统计数据#
在“「数值型数据入门」”这一节中,你已经学习了如何:
-
通过图表将数据可视化;
-
从数学角度评估潜在的特征和标签;
-
找出数据集中的离群值;
本练习将引导你如何找出包含明显离群值的列,随后您可以决定是保留这些列还是将其从数据集中删除。
1.1 导入数据集#
def load_dataset(filepath_or_buffer="california_housing_train.csv"):
training_df = pd.read_csv(filepath_or_buffer=filepath_or_buffer)
return training_df1.2 获取基本统计信息#
在以下代码块中,DataFrame 的 describe 方法会返回数据集中所有列的基本统计信息,具体包括:
-
count:该列中非空元素的数量。理想情况下,每一列的 count 值应相同,但实际情况未必如此。
-
mean:该列数值的算术平均值。建议将每一列的平均值(mean)与中位数(median)进行比较。中位数对应于数据分布的 50% 分位点。
-
std:该列数值的标准差。
-
min、25%、50%、75% 和 max:分别表示 0、25、50、75 和 100 百分位点处的数值。
def basic_statistics(training_df):
info = training_df.describe()
print(info)上述代码运行结束后的结果为:
longitude latitude ... median_income median_house_value
count 17000.0 17000.0 ... 17000.0 17000.0
mean -119.6 35.6 ... 3.9 207300.9
std 2.0 2.1 ... 1.9 115983.8
min -124.3 32.5 ... 0.5 14999.0
25% -121.8 33.9 ... 2.6 119400.0
50% -118.5 34.2 ... 3.5 180400.0
75% -118.0 37.7 ... 4.8 265000.0
max -114.3 42.0 ... 15.0 500001.0根据前面的统计数据,您是否发现任何可能包含异常值的列?
点击查看答案
以下列可能包含离群值:
-
total_rooms
-
total_bedrooms
-
population
-
households
-
median_income(有可能)
因为在这些列中:
-
标准差几乎与平均值相当
-
75% 分位数与最大值之间的差值,远大于最小值与 25% 分位数之间的差值。
2. 找出数据集中的问题部分#
本节是数值型数据编程练习的第二部分,我们将使用一份虚构的数据集来引导你通过可视化与数学方法,找出数据中隐藏的「坏值」。
2.1 预期情况#
在上一节 数值型数据:入门 中,你已经学过:
-
用图表(plot 或 graph)把数据可视化;
-
从数学角度评估潜在的特征和标签;
-
找出 「异常值(outliers)」。
本练习将引导你通过可视化与数学方法,找出数据集中隐藏的异常值。例如使用散点图与基础统计量来定位数据集中不可靠的值。
2.2 数据集构建#
本练习使用的数据集是虚构的,一共 1,400 行。每一行包含以下两列:
-
calories:由营养师测定的早餐摄入热量。
-
test_score:学生的数学测验成绩。
test_score的取值由一个可靠的程序确定。
数据池由 50 名学生组成,每位学生连续被观测 28 天。所以,第 0-49 行展示的是第一天 50 名学生各自的表现,第 50-99 行展示的是第二天的,以此类推。
这份数据集的目标是判断「学生早餐摄入的热量」与「测验成绩」之间的关系。但本练习的目标更简单:你只需要判断数据中是否存在异常值即可。
2.3 安装与导入相关模块#
# @title 安装相关模块
!pip install matplotlib~=3.10.0 \
pandas~=2.2.0# @title 导入相关模块
# 下面这段代码导入运行本示例所需的模块。
# 如果你在运行后续某些代码段时遇到技术问题,
# 请尝试再运行一次本段代码。
import pandas as pd
from matplotlib import pyplot as plt
import io
# 下面两行调整报告的粒度。
pd.options.display.max_rows = 10
pd.options.display.float_format = "{:.1f}".format2.4 定义数据集#
# @title 定义数据集
# 下面这段代码定义你在示例代码中中使用的数据集。
dataset = '''calories,test_score
201,76
142,72
397,84
294,79
334,76
173,60
...
'''由于这里这部分数据集行数太多,所以我们直接将其放到的示例代码文件中。
2.5 把数据集加载到 DataFrame#
下面的代码把数据集加载到一个名为 training_df 的 DataFrame 中。
def load_calories_dataset():
training_df = pd.read_csv(filepath_or_buffer="calories.csv")
return training_df2.6 任务 1:检查基础统计量#
下面这行代码生成数据集的基础统计量:
training_df.describe()上述代码运行后的结果为:
calories test_score
count 1400.0 1400.0
mean 185.4 69.9
std 116.9 12.8
min 0.0 42.0
25% 80.0 59.0
50% 177.0 70.0
75% 285.0 80.0
max 399.0 98.0请问:上述基础统计量是否暗示存在异常值?
点击查看答案
基础统计量并未提示存在大量异常值,标准差明显小于均值。 此外,四分位边界大致是均匀分布的。
2.7 定义绘图函数#
下面的代码使用 matplotlib 创建以下两个函数:
-
plot_the_dataset:在数据集中随机抽点画散点图。把number_of_points_to_plot设为 1,400 时,会把数据集中全部 1,400 个点都画出来。 -
plot_a_contiguous_portion_of_dataset:画出从start到end这一连续区间内的所有点。例如,把start设为 0、end设为 100,就会画出前 100 个点的散点图。
定义如下两个可视化函数:
#@title 定义绘图函数 { display-mode: "form" }
# 下面这段代码定义用于可视化数据的绘图函数。
def plot_the_dataset(feature, label, number_of_points_to_plot,training_df):
"""画出数据集中的 N 个随机点。"""
# 给坐标轴打标签。
plt.xlabel(feature)
plt.ylabel(label)
# 用数据集中的 n 个随机点画散点图。
random_examples = training_df.sample(n=number_of_points_to_plot)
plt.scatter(random_examples[feature], random_examples[label])
# 渲染散点图。
plt.show()
def plot_a_contiguous_portion_of_dataset(feature, label, start, end,training_df):
"""画出从 start 到 end 范围内的数据点。"""
# 给坐标轴打标签。
plt.xlabel(feature + "Day")
plt.ylabel(label)
# 画散点图。
plt.scatter(training_df[feature][start:end], training_df[label][start:end])
# 渲染散点图。
plt.show()2.8 任务 2:把数据可视化#
虽然通过上面的统计量没有发现异常,但是异常值可能仍然藏在数据集中。进一步,用散点图把数据可视化,或许能找出那些在基础统计量里看不到的异常值。
plot_the_dataset("calories", "test_score", number_of_points_to_plot=50)上述代码运行结束后的可视化结果为:

由 50 个随机数据点画出的散点图,是否暗示了异常值的存在?
如果把 number_of_points_to_plot 调大,又会发生什么?
点击查看答案
只画 50 个数据点时,看不出明显异常。 但当你把随机抽点的数量调大时,就会出现一团异常值。 同时,请留意那些 test_score 较高、但 calories 不到 200 的点。
2.9 任务 3:按周统计#
有可能是不同的实验人员对 calories 的编码方式不同。例如,第 0 周与第 1 周可能用了不同的编码器。运行下面这些代码段,按周获取统计量。你能看出各周之间存在显著差异吗?
def week_statistics(training_df):
# 获取第 0 周的统计量
info = training_df[0:350].describe()
print(f"获取第 0 周的统计量:\n {info}")
# 获取第 1 周的统计量
info = training_df[350:700].describe()
print(f"获取第 1 周的统计量:\n {info}")
# 获取第 2 周的统计量
info = training_df[700:1050].describe()
print(f"获取第 2 周的统计量:\n {info}")
# 获取第 3 周的统计量
info = training_df[1050:1400].describe()
print(f"获取第 3 周的统计量:\n {info}")上述代码运行结束以后,前两周输出结果为:
获取第 0 周的统计量:
calories test_score
count 350.0 350.0
mean 181.3 69.7
std 117.5 12.8
min 1.0 43.0
25% 77.0 60.0
50% 172.0 70.0
75% 281.5 80.0
max 399.0 98.0
获取第 1 周的统计量:
calories test_score
count 350.0 350.0
mean 184.4 69.8
std 112.4 12.8
min 0.0 42.0
25% 83.2 59.2
50% 185.0 70.0
75% 278.0 80.0
max 399.0 97.0根据输出内容,我们能够得到什么结论呢?
点击查看答案
2.10 任务 4:按星期几可视化#
既然按周来看没有差异,那是不是不同星期几雇了不同的实验人员?比如星期一的实验人员与星期二的实验人员,编码方式可能就不一样。
下面这段代码会生成七张散点图——分别对应第一周里的每一天。每一天看起来都一致吗?
def each_statistics(training_df):
for i in range(0, 7):
start = i * 50
end = start + 49
print("\nDay %d" % i)
plot_a_contiguous_portion_of_dataset("calories", "test_score", start, end,training_df)上述代码运行结束后前两天的可视化结果为:

根据可视化的结果,你有发现不对劲儿的地方吗?
点击查看答案
2.11 任务 5:验证你的猜想#
如果你怀疑 Day 4(星期四)的编码方式与其他日子不同。请写一些代码,验证你的猜想。
# 在这里写你的代码。
# 注意:training_df["calories"][position] 返回的是
# 数据集中指定 position 处的 calories 取值。最终,输出结果为:
星期四的 calories 均值为 93
非星期四的 calories 均值为 201以上所有结果,按以下顺序运行即可,完整示例代码见
if __name__ == '__main__':
# 第一部分:
training_df = load_dataset()
basic_statistics(training_df)
# 第二部分:
training_df = load_dataset("calories.csv")
# 任务 1:检查基础统计量
basic_statistics(training_df)
# 任务 2:把数据可视化
plot_the_dataset("calories", "test_score", number_of_points_to_plot=50, training_df=training_df)
# 任务 3:按周统计#
week_statistics(training_df)
# 任务 4:按星期几可视化
each_statistics(training_df)
# 任务 5:验证你的猜想
validation(training_df)