更新于 2026年10月15日 全文 2064 字未阅读

数值型数据编程练习#

请花点时间完成以下练习,以巩固您在“「数值型数据入门」”中学到的内容。

本节内容一共包含两个部分:

  • 获取数据集的统计信息,这将向您展示如何找出包含明显异常值的列。

  • 找出数据集中的问题部分——本指南将引导你如何通过可视化和数学方法,发掘数据集中隐藏的异常值或错误数据。

以下为本站根据 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_df

1.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 预期情况#

在上一节 数值型数据:入门 中,你已经学过:

本练习将引导你通过可视化与数学方法,找出数据集中隐藏的异常值。例如使用散点图与基础统计量来定位数据集中不可靠的值。

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}".format

2.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_df

2.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)

上述代码运行结束后前两天的可视化结果为:

「学生早餐摄入的热量」与「测验成绩」数据集关系

根据可视化的结果,你有发现不对劲儿的地方吗?

点击查看答案
Day 4 的 calories 取值范围是 0 到 200,而其他所有日子的 calories 取值范围都是 0 到 400。至少在第一周里,Day 4 的数据有问题。

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)
阅读 --