解放教师双手:用python打造智能excel作业自动批改
背景
前阵子,我的一个老同学无意中跟我聊起来她的工作内容中,需要使用大量的Excel,其中批改学生作业比较痛苦,需要逐个去看学生上交的Excel作业,然后再汇总得分,是一个枯燥又耗时的工作。
于是,我想到处理Excel,那么python中的pandas十分合适,评分的过程其实就是对比答案的文件和作业的文件是否一致,一致就算对,不一致就算错。此外,再增加上计分的功能,那么这个需求其实很简单。
实现 1.0版本
稍微整理了流程之后,很快第一版的代码就产出了,主要逻辑如下:
比较的逻辑
def compare_columns(df1, df2, columns_config):
diff_scores = {}
for col_letter, score in columns_config.items():
index = column_letter_to_index(col_letter)
column1 = df1.iloc[:, index]
column2 = df2.iloc[:, index]
diff = (column1 != column2).astype(int) # 计算差异,不同为1,相同为0
total_score = diff.sum() * score # 计算总分
diff_scores[f'Column {col_letter} (分值:{score})'] = total_score
return diff_scores
2. 保存比较结果
def create_comparison_result(file_name, scores):
result_data = {
'File Name': file_name,
'Total Score': sum(scores.values()),
}
# 添加每列的分数
result_data.update(scores)
return result_data
3. 用户输入需要比较的列
columns_config = {}
print("n请输入需要比对的列和对应的分值")
print("示例:输入 'L 0.1' 表示L列每个差异扣0.1分")
print("输入空行结束配置")
while True:
try:
line = input("n请输入列名和分值 (格式:列名 分值),或直接按Enter结束: ").strip()
if not line:
if columns_config:
break
print("请至少配置一个列!")
continue
parts = line.split()
if len(parts) != 2:
print("格式错误!请使用'列名 分值'的格式,例如:'L 0.1'")
continue
column, score = parts
column = column.upper()
score = float(score)
if not column.isalpha():
print("列名必须是字母!")
continue
columns_config[column] = score
print(f"已添加:{column}列,分值:{score}")
except ValueError:
print("分值必须是数字!")
continue
使用问题
初版代码很快完成,但是在同学的使用过程中,发现了几个问题:
当需要计算分数的列比较多时,需要频繁的输入,比较费劲
如果需要计算分数的部分不是一整列时,现在的代码不能满足
需要安装python环境及对应的依赖库,门槛稍高
优化思路
针对如上几个问题,
需要重新设计对比的逻辑,可以支持多列,甚至是不规则的单元格进行对比
将整个工程打包成一个exe文件,无需额外安装环境
优化 1.1版本
首先,将逻辑拆分成需要用户来设计一个模板,用做评分依据,在这个模板中,只要被标记了背景色,即视为需要评分的单元格。这样的好处是,无需额外输入要评分的列(行),以及支持无规则的单元格
其次,不再使用windows下的cmd命令窗口,而是增加了对话框,选择文件、目录时更方便
最后,整个代码打包成一个单独的exe文件
比较的逻辑
def compare_sheets(self,
standard_df: pd.DataFrame,
submission_df: pd.DataFrame) -> pd.DataFrame:
"""
比较两个DataFrame,返回差异矩阵(相同为0,不同为1)
"""
# 确保两个DataFrame大小一致
max_rows = max(len(standard_df), len(submission_df))
max_cols = max(len(standard_df.columns), len(submission_df.columns))
# 创建空的差异矩阵
diff_matrix = pd.DataFrame(0, index=range(max_rows), columns=range(max_cols), dtype=int)
# 比较每个单元格
for i in range(max_rows):
for j in range(max_cols):
# 获取标准答案的值
std_val = standard_df.iloc[i, j] if i < len(standard_df) and j < len(standard_df.columns) else 0
# 获取提交作业的值
sub_val = submission_df.iloc[i, j] if i < len(submission_df) and j < len(submission_df.columns) else 0
# 处理空值
if pd.isna(std_val):
std_val = 0
if pd.isna(sub_val):
sub_val = 0
try:
# 尝试数值比较
std_num = float(std_val) if str(std_val).strip() != '' else 0
sub_num = float(sub_val) if str(sub_val).strip() != '' else 0
if abs(std_num - sub_num) >= 0.01:
diff_matrix.iloc[i, j] = 1
except (ValueError, TypeError):
# 字符串比较
if str(std_val).strip() != str(sub_val).strip():
diff_matrix.iloc[i, j] = 1
return diff_matrix
2. 计算得分
def calculate_sheet_score(self,
standard_df: pd.DataFrame,
submission_df: pd.DataFrame,
scoring_df: pd.DataFrame) -> Tuple[float, Dict]:
"""
计算单个sheet的得分
"""
# 获取差异矩阵
diff_matrix = self.compare_sheets(standard_df, submission_df)
# 计算扣分(差异矩阵与评分矩阵相乘)
deduction_matrix = diff_matrix * scoring_df
total_deduction = deduction_matrix.sum().sum()
# 收集扣分详情
deductions = {}
for i in range(len(diff_matrix)):
for j in range(len(diff_matrix.columns)):
if diff_matrix.iloc[i, j] == 1: # 如果有差异
# 获取标准答案的值(如果存在)
expected = standard_df.iloc[i, j] if i < len(standard_df) and j < len(standard_df.columns) else None
# 获取提交作业的值(如果存在)
submitted = submission_df.iloc[i, j] if i < len(submission_df) and j < len(
submission_df.columns) else None
# 获取扣分值(如果存在)
points = scoring_df.iloc[i, j] if i < len(scoring_df) and j < len(scoring_df.columns) else 0
deductions[(i, j)] = {
'expected': expected if expected is not None else '未填写',
'submitted': submitted if submitted is not None else '未填写',
'points_deducted': points
}
return total_deduction, deductions
使用问题
这次的评分过程足够灵活,但是由于当时设计了几个目录及文件(答案的文件、评分标准的文件、学生作业的目录、评分结果的目录),导致很容易误操作。所以需要再次优化
优化 1.1.1版本
优化逻辑改为:
目录选择逻辑优化 让用户选择一次目录,自动检测该目录下是否有答案的文件以及评分标准的文件,如果检测到,让用户确认;没有检测到,则提示用户进行选择。并且在该目录下自动生成结果文件夹
用户体验优化 程序完成后,自动打开生成结果文件夹
至此,这个简单的需求,算是满足了老同学的全部使用要求,而且比较简洁

最后,有兴趣的同学可以下载试试看,附上仓库地址
作者声明本文无利益相关,欢迎值友理性交流,和谐讨论~

myfishc
校验提示文案
gmalwxfa
校验提示文案
三爷最帅
校验提示文案
myfishc
校验提示文案
三爷最帅
校验提示文案
gmalwxfa
校验提示文案