Logo
Section 1 / 3

解决方案:任务 I 至任务 IV

先前分配的挑战任务 I 到任务 IV 的解决方案。

导入

解决方案:任务 I 至任务 IV

先前分配的挑战任务 I 到任务 IV 的解决方案。

做好准备,现在我们将逐一完成每个任务,以系统化的方式推进项目。你可以运行每个可执行代码,并验证不同数值下的结果。

说明

任务1:数据分组

为了将列与其对应的值进行分组,我们需要列的名称和数据。还有一个额外的要求:需要决定分组的记录数量,但这个参数应为可选。因此,我们还需要一个关键字参数 n。函数的定义如下:

1def group_values_with_columns(orig_data, req_col, **kwargs):

其中,orig_data 是之前从 CSV 文件读取数据后存储的列表(即 data)。此外,req_col 是我们事先硬编码的 required_columns。请看以下代码片段。

1import csv 2from collections import namedtuple 3 4 5def group_values_with_columns(orig_data, req_col, **kwargs): 6 7 # 使用 namedtuple 创建包含列名及其值的元组 8 Student = namedtuple('Student', 'GPA Gender drink exercise fries income sports weight') 9 columns_index = [orig_data[0].index(name) for name in req_col] 10 11 if kwargs: # 如果传入了 n 参数 12 individuals = orig_data[1:kwargs['n']+1] 13 else: 14 individuals = orig_data[1:51] # 默认取前 50 条记录 15 16 grouped_data = [] # 存储列与值对应关系的元组列表 17 18 for individual in individuals: 19 value = [individual[i] for i in columns_index] 20 grouped_data.append(Student._make(value)) 21 22 return grouped_data 23 24 25# 读取数据集到列表中 26with open('food.csv', newline='') as f: 27 reader = csv.reader(f) 28 data = list(reader) # 保存原始格式的数据 29 30# 本项目中将处理的列 31required_columns = ['GPA', 'Gender', 'drink', 'exercise', 'fries', 'income', 'sports', 'weight'] 32 33 34# 调用函数 35grouped_data = group_values_with_columns(data, required_columns, n=60) 36for entry in grouped_data: 37 print(entry)

最简便的方法是使用 namedtuple 来建立值与列之间的关联。我们创建了一个名为 Student 的元组,包含字段:GPA、Gender、drink、exercise、fries、income、sports 和 weight。如前一课所述,orig_data 中的第一个列表包含了数据中所有列的名称。

要建立这种关联,必须知道某一列名称在第一个列表中的 索引。我们需要获取所需列名称的索引,这些索引将准确对应后续每条记录中相应位置的值。这些索引被存储在 columns_index 中。接着,通过判断 kwargs 是否存在来决定是否传入 n。如果传入了 n,则提取前 n 条记录;否则,默认提取前 50 条记录。

选定记录后,接下来是提取所需的值。从每条记录中,取出位于 columns_index 中索引位置的值,并存入一个临时列表 value。最后,将 value 转换为类型为 Student 的元组,使其值能正确映射到各个字段。所有这样的元组被添加到 grouped_data 列表中,最终返回该列表,供后续所有任务使用。


任务2:识别性别差异 👱‍♂️👱‍♀️

你需要编写一个装饰器 count,用于统计任意列表的长度,从而找出分组数据中所有男性和女性的数量。

首先编写装饰器。请看以下代码:

1def count(func): 2 def wrapper(*args): 3 result = func(*args) 4 result = len(result) 5 return result 6 return wrapper

非常简单。第4行 表明,我们必须返回由任何被 @count 装饰的函数 func 所返回的迭代器 result 的 长度(见 第3行)。接下来编写其余函数。

1from task1 import group_values_with_columns 2 3 4# 用于返回迭代器长度的装饰器 5def count(func): 6 def wrapper(*args): 7 result = func(*args) 8 result = len(result) 9 return result 10 return wrapper 11 12 13# 获取女性数据 14@count 15def get_females(data): 16 return [entry for entry in data if entry.Gender == '1'] 17 18 19# 获取男性数据 20@count 21def get_males(data): 22 return [entry for entry in data if entry.Gender == '2'] 23 24 25# 调用函数 26grouped_data = group_values_with_columns(data, required_columns, n=60) 27 28print(get_females(grouped_data)) 29print(get_males(grouped_data))

显然,要获取女性人数,只需要第一项任务生成的分组数据(data)。这里我们遍历每一个元组,构建一个新的列表,仅包含性别为女性('1')的元组。由于函数已用 @count 装饰,调用时会自动触发 count,返回新列表的长度。

同样的逻辑适用于统计男性人数。只需将条件中的 '1' 改为 '2'(见第22行)。


任务3:收集饮酒统计数据 🥤🍹

你需要统计选择“橙汁”和“汽水”的学生人数,使用字典实现。请看以下代码:

1from task1 import group_values_with_columns 2from collections import defaultdict 3 4# 获取饮酒统计数据 5def drink_stats(data): 6 7 d = defaultdict(int) # 创建一个默认值为整数的字典 8 9 for entry in data: 10 d[entry.drink] += 1 # 为每种饮品创建唯一键并计数 11 12 return d 13 14 15# 调用函数 16grouped_data = group_values_with_columns(data, required_columns, n=60) 17 18print(drink_stats(grouped_data))

要获取统计数据,只需使用第一项任务生成的分组数据(data)。为了统计每种饮品的学生数量,字典的值类型应为 int(见第7行)。

然后,从每条分组数据的元组中提取 drink 字段。为每种可能的饮品创建一个键,并通过自增操作(第10行)统计其出现次数。

最终返回的字典包含了所有学生提供的选项以及每种选项对应的总人数。


任务4:收集运动统计 🏃‍♀️🏃

你需要从分组数据中统计符合特定每周运动模式的男性和女性人数。这需要第一项任务生成的分组数据(data)以及一个表示运动模式的参数。请看以下代码:

1from task1 import group_values_with_columns 2from task2 import get_females, get_males 3 4 5# 获取运动统计数据 6def exercise_stats(data, x): 7 8 temp = [entry for entry in data if entry.exercise == x] 9 return get_males(temp), get_females(temp) 10 11 12# 调用函数 13grouped_data = group_values_with_columns(data, required_columns, n=60) 14 15print(exercise_stats(grouped_data, '2'))

观察第9行:我们调用了两个函数 get_males 和 get_females(已在第二项任务中实现),并将它们分别传入一个列表。从分组数据的每个元组中筛选出 exercise 字段等于 x 的记录,存入临时列表 temp(见第8行)。由于 get_males 和 get_females 已被 count 装饰,最终返回的是两个整数,分别代表符合条件的男性和女性人数。

内置数据结构

解决方案:任务V至任务VIII