1.读取excel文件
# 读取数据
df_a = pd.read_excel('b1_t_stat_exam_answer.xlsx')
df_b = pd.read_excel('b1_t_stat_exam_relation.xlsx')
2.选择需要的列以及合并文件
df_b = df_b[['exam_id', 'score']] # 假设关系表中有 exam_id 和 score 列
df_a = pd.merge(df_a, df_b, on='exam_id', how='left')
这是使用 Pandas 中的 merge
函数将两个 DataFrame(数据框) df_a
和 df_b
按照共同的列 'exam_id'
进行左连接的操作。具体来说,df_a
是左表,df_b
是右表,它们通过 'exam_id'
列进行连接。
参数说明:
on='exam_id'
:指定连接的列是'exam_id'
。how='left'
:指定连接方式为左连接,即保留左表中的所有行,右表中没有匹配的行填充为缺失值(NaN)。
左连接的含义是,保留左表(df_a
)中的所有行,并将右表(df_b
)中与左表匹配的行合并到结果中。如果右表中没有与左表匹配的行,则对应的列将填充为缺失值。
这种操作通常用于合并两个表格,其中一个表格包含主要信息,而另一个表格包含附加信息。在这里,df_a
是主表,df_b
是附加表,通过 'exam_id'
列进行关联,将附加信息合并到主表中。
3.一列除以一列,以及增加新的一列
# 计算新分数
df_a['new_score'] = df_a['score_x'] / df_a['score_y'] * 100
4. 替换操作
# 根据 personid、clazzid 和 exam_id 替换原始分数
df_a['score'] = df_a['new_score'].where(df_a['new_score'].notna(), df_a['score_x'])
这是使用 Pandas 中的 where
函数对 DataFrame(数据框) df_a
中的 'score' 列进行条件替换的操作。具体来说,该操作使用 'new_score' 列的非缺失值来替换 'score' 列中的对应值,如果 'new_score' 列的值是缺失的,则保留 'score_x' 列的值。
解释各部分:
-
df_a['new_score'].notna()
:这是一个布尔条件,它生成一个与 'new_score' 列相同长度的布尔序列,指示在 'new_score' 列中哪些位置的值是非缺失的。 -
df_a['new_score'].where(...)
:这是 Pandas 中的where
函数,它根据给定的条件对数组进行条件替换。在这里,它将 'new_score' 列的非缺失值应用到 'score' 列,如果 'new_score' 列的值是缺失的,则保持 'score' 列不变。 -
df_a['score_x']
:这是备选值,如果 'new_score' 列的值是缺失的,将使用 'score_x' 列的对应值。
综合起来,这行代码的作用是将 'score' 列中的值用 'new_score' 列中的非缺失值替换,如果 'new_score' 列中的值是缺失的,则用 'score_x' 列的值进行替换。这种替换操作可以用于处理缺失值或者根据条件生成新的列。
5.用group聚合数据
# 选择需要的列
df_a = df_a[['personid', 'clazzid', 'exam_id', 'score']]
# 计算班级平均分和标准差
class_stats = df_a.groupby('clazzid')['score'].agg(['mean', 'std']).reset_index()
class_stats.columns = ['clazzid', 'class_mean', 'class_std']
这段代码使用 Pandas 中的 groupby
函数对 DataFrame(数据框) df_a
按照 'clazzid' 列进行分组,然后对每个班级计算 'score' 列的平均值和标准差。最后,通过 reset_index()
函数将结果重新设置为一个新的 DataFrame,并为列起名为 'clazzid'、'class_mean' 和 'class_std'。
逐步解释:
-
df_a.groupby('clazzid')['score']
:这部分代码按照 'clazzid' 列进行分组,并选择 'score' 列作为聚合的列。这会生成一个按班级分组的 Pandas GroupBy 对象。 -
.agg(['mean', 'std'])
:对每个分组计算 'score' 列的平均值(mean)和标准差(std)。这使用了agg
函数,它允许对每个分组应用多个聚合函数。 -
.reset_index()
:将 GroupBy 对象转换为新的 DataFrame,并将 'clazzid' 列从索引中还原为普通的列。 -
columns = ['clazzid', 'class_mean', 'class_std']
:为新生成的 DataFrame 的列重新命名,使其更具有描述性。
最终的结果是一个包含每个班级平均分和标准差的 DataFrame,其中包含 'clazzid'、'class_mean' 和 'class_std' 三列。这样的数据表达了每个班级的评分分布特征,方便进一步的分析和可视化。
6.计算排名
# 计算班级排名
df_a['class_rank'] = df_a.groupby('clazzid')['score'].rank(ascending=False, method='min')
这段代码用于为 DataFrame df_a
中的每个学生计算其在班级中的排名,并将排名结果存储在新的 'class_rank' 列中。
逐步解释:
-
df_a.groupby('clazzid')['score']
:这部分代码按照 'clazzid' 列进行分组,并选择 'score' 列作为聚合的列。这会生成一个按班级分组的 Pandas GroupBy 对象。 -
.rank(ascending=False, method='min')
:对每个分组中的 'score' 列进行排名操作。参数ascending=False
表示降序排列,即得分高的排名靠前;参数method='min'
表示当存在相同分数时,使用最小排名。这个函数返回一个 Series,包含了每个学生在其班级中的排名。 -
df_a['class_rank']
:将上一步得到的排名结果赋值给新的列 'class_rank',该列记录了每个学生在其班级中的排名信息。
最终,DataFrame df_a
被更新,添加了 'class_rank' 列,其中包含了每个学生在其班级中的排名信息。这样的排名信息可以用于了解学生在班级中的相对位置,例如,谁是班级中的前几名等。
7.填补NAN
#处理个人考试方差/平均值/分数最大值/最小值
person_class_stats = df_a.groupby(['personid', 'clazzid']).agg(
mean_score=('score', 'mean'),
std_score=('score', 'std'),
max_score=('score', 'max'),
min_score=('score', 'min'),
).reset_index()
person_class_stats.fillna(0, inplace=True)
#NaN没有方差,只参加一次考试
print(person_class_stats)
这段代码用于处理每个学生在每个班级中的考试成绩的统计信息,包括平均分(mean_score
)、标准差(std_score
)、最大分(max_score
)和最小分(min_score
)。统计信息存储在一个新的 DataFrame 中,名为 person_class_stats
。
逐步解释:
-
df_a.groupby(['personid', 'clazzid'])
:这部分代码按照 'personid' 和 'clazzid' 两列进行分组。这意味着每个学生在每个班级中形成一个分组。 -
.agg(...)
:使用agg
函数对每个分组进行聚合计算。在这里,对 'score' 列进行了四种聚合计算,分别是平均值、标准差、最大值和最小值。 -
reset_index()
:将 GroupBy 对象转换为新的 DataFrame,并将 'personid' 和 'clazzid' 从索引中还原为普通的列。 -
person_class_stats.fillna(0, inplace=True)
:用 0 替换结果中的缺失值(NaN)。这是因为当学生只参加一次考试时,标准差会为 NaN。使用 0 替换 NaN 是为了表示这种情况。 -
print(person_class_stats)
:输出最终的学生在每个班级中的统计信息,包括平均分、标准差、最大分和最小分。
最终,person_class_stats
是一个包含每个学生在每个班级中考试成绩统计信息的 DataFrame。这有助于分析每个学生在不同班级中的成绩表现。
8.关于计数
# 统计同一个 personid 和 clazzid 出现的 examid 次数
person_clazz_exam_count = df_a.groupby(['personid', 'clazzid']).size().reset_index(name='person_clazz_exam_count')
# 统计同一个 clazzid 出现的不同 examid 次数
clazz_unique_exam_count = df_a.groupby(['clazzid'])['exam_id'].nunique().reset_index(name='clazz_unique_exam_count')
# 合并数据
merged_result = pd.merge(person_clazz_exam_count, clazz_unique_exam_count, on='clazzid', how='left')
# 计算 count_percentage
merged_result['count_percentage'] = (merged_result['person_clazz_exam_count'] / merged_result['clazz_unique_exam_count']).fillna(0)
# 打印结果
print(merged_result)
这段代码用于统计在 DataFrame df_a
中,对于每个学生和班级组合('personid' 和 'clazzid'),统计其参加考试的次数('person_clazz_exam_count')。同时,对于每个班级('clazzid'),统计该班级中不同考试的数量('clazz_unique_exam_count')。最后,将这两个统计结果合并到一个新的 DataFrame merged_result
中,并计算参加考试次数在总考试次数中的比例('count_percentage')。
逐步解释:
-
df_a.groupby(['personid','clazzid']).size().reset_index(name='person_clazz_exam_count')
:这部分代码按照 'personid' 和 'clazzid' 两列进行分组,然后使用size()
函数统计每个组合中的行数,即参加考试的次数。reset_index(name='person_clazz_exam_count')
将结果转换为 DataFrame,并将列名设置为 'person_clazz_exam_count'。 -
df_a.groupby(['clazzid'])['exam_id'].nunique().reset_index(name='clazz_unique_exam_count')
:这部分代码按照 'clazzid' 进行分组,然后使用nunique()
函数统计每个班级中不同考试的数量。同样,通过reset_index(name='clazz_unique_exam_count')
将结果转换为 DataFrame,并将列名设置为 'clazz_unique_exam_count'。 -
pd.merge(person_clazz_exam_count, clazz_unique_exam_count, on='clazzid', how='left')
:将两个统计结果按照 'clazzid' 进行左连接,合并到一个新的 DataFramemerged_result
中。 -
merged_result['count_percentage'] = (merged_result['person_clazz_exam_count'] / merged_result['clazz_unique_exam_count']).fillna(0)
:计算每个学生和班级组合的参加考试次数在总考试次数中的比例,并将结果存储在 'count_percentage' 列中。这里使用了fillna(0)
来处理可能的除零情况,将除零的结果设为 0。 -
print(merged_result)
:打印最终的合并结果,其中包含了每个学生和班级组合的参加考试次数,以及每个班级中不同考试的数量和参加考试次数在总考试次数中的比例。
这样的统计信息有助于了解每个学生在每个班级中的考试参与情况,并计算参与度的相对指标。
区别:
-
size()
方法: 返回分组中的元素总数,包括缺失值。它用于计算分组中的行数。df.groupby('column_name').size()
-
nunique()
方法: 返回分组中的唯一元素的数量,即去重后的数量。它用于计算分组中不同元素的个数。df.groupby('column_name')['another_column'].nunique()
-
count()
方法: 返回分组中非缺失值的数量。它用于计算分组中的非缺失值的个数。df.groupby('column_name')['another_column'].count()
9.在合并数据时,可以选择列
merged_df = pd.merge(merged_df, df_course[['name','courseid','type']], on='courseid')
10.保存数据
# 将结果导出到 CSV 文件
merged_result.to_csv('final_result.csv', index=False)