该题目应该是我第一次处理三个表格的情况。
该题目来源于力扣的pandas题库,链接为:
1280. 学生们参加各科测试的次数 - 力扣(LeetCode)
——题目要求
学生表: Students
+---------------+---------+ | Column Name | Type | +---------------+---------+ | student_id | int | | student_name | varchar | +---------------+---------+ 在 SQL 中,主键为 student_id(学生ID)。 该表内的每一行都记录有学校一名学生的信息。
科目表: Subjects
+--------------+---------+ | Column Name | Type | +--------------+---------+ | subject_name | varchar | +--------------+---------+ 在 SQL 中,主键为 subject_name(科目名称)。 每一行记录学校的一门科目名称。
考试表: Examinations
+--------------+---------+ | Column Name | Type | +--------------+---------+ | student_id | int | | subject_name | varchar | +--------------+---------+ 这个表可能包含重复数据(换句话说,在 SQL 中,这个表没有主键)。 学生表里的一个学生修读科目表里的每一门科目。 这张考试表的每一行记录就表示学生表里的某个学生参加了一次科目表里某门科目的测试。
查询出每个学生参加每一门科目测试的次数,结果按 student_id
和 subject_name
排序。
查询结构格式如下所示。
示例 1:
输入: Students table: +------------+--------------+ | student_id | student_name | +------------+--------------+ | 1 | Alice | | 2 | Bob | | 13 | John | | 6 | Alex | +------------+--------------+ Subjects table: +--------------+ | subject_name | +--------------+ | Math | | Physics | | Programming | +--------------+ Examinations table: +------------+--------------+ | student_id | subject_name | +------------+--------------+ | 1 | Math | | 1 | Physics | | 1 | Programming | | 2 | Programming | | 1 | Physics | | 1 | Math | | 13 | Math | | 13 | Programming | | 13 | Physics | | 2 | Math | | 1 | Math | +------------+--------------+ 输出: +------------+--------------+--------------+----------------+ | student_id | student_name | subject_name | attended_exams | +------------+--------------+--------------+----------------+ | 1 | Alice | Math | 3 | | 1 | Alice | Physics | 2 | | 1 | Alice | Programming | 1 | | 2 | Bob | Math | 1 | | 2 | Bob | Physics | 0 | | 2 | Bob | Programming | 1 | | 6 | Alex | Math | 0 | | 6 | Alex | Physics | 0 | | 6 | Alex | Programming | 0 | | 13 | John | Math | 1 | | 13 | John | Physics | 1 | | 13 | John | Programming | 1 | +------------+--------------+--------------+----------------+ 解释: 结果表需包含所有学生和所有科目(即便测试次数为0): Alice 参加了 3 次数学测试, 2 次物理测试,以及 1 次编程测试; Bob 参加了 1 次数学测试, 1 次编程测试,没有参加物理测试; Alex 啥测试都没参加; John 参加了数学、物理、编程测试各 1 次。
——思路流程
面对三张表的联合查询,首先将学生表和科目表合并(利用笛卡尔积),然后用size函数统计考试表的频数,最后将前者和后者进行合并即可。
——代码实现
import pandas as pd
def students_and_examinations(students: pd.DataFrame, subjects: pd.DataFrame, examinations: pd.DataFrame) -> pd.DataFrame:
data=pd.merge(students,subjects,how='cross')
#注意!!!size()函数代表计算该类型出现的频数,并且新建一行列名为0的频数列!!!
examinations=examinations.groupby(['student_id','subject_name']).size().reset_index(name='attended_exams')
data1=pd.merge(data,examinations,how='left',on=['student_id','subject_name'])
data2=data1.sort_values(by= ['student_id','subject_name'])
#细节:题目要求姓名如果为空的话保留空值,但如果频数统计列出现空值要求用0填充空值
data2['attended_exams']=data2['attended_exams'].fillna(0)
#最后在输出时利用reindex函数通过设置参数axis=1,重置列的索引,使数据符合要求
return data2.reindex(['student_id', 'student_name', 'subject_name', 'attended_exams'],axis=1)
——特殊参数:
在函数merge函数中,on有一个特殊的参数“cross”,表示笛卡尔积合并。笛卡尔积合并表示它会将两个 DataFrame 中的所有可能的行组合在一起,形成一个新的 DataFrame。这种合并方式会保留两个 DataFrame 的所有行,并且不会有交集,即每个结果中的行在左 DataFrame 或右 DataFrame 中是唯一的。当我们遇到学生的名字和科目表(多对多),像对它们依次进行合并,可以使用这个参数,即 pd.merge(数据框1,数据框2,on='cross') 来进行合并。并且笛卡尔合并不需要指定连接键,可适用于没有连接键的多对多的表格连接。