
关键词:Python / pandas / 绩点统计 / GPA / 自动化
适合:AI/计算机/数据方向同学,把"绩点统计"做成一个可展示的小项目(作品集也能用!)
目录
- [1. 为什么要用 Python 统计绩点](#1. 为什么要用 Python 统计绩点)
- [2. 数据准备:成绩表应该长什么样](#2. 数据准备:成绩表应该长什么样)
- [3. 规则说明:5.0 制 + 百分制直算 + 重修取最高](#3. 规则说明:5.0 制 + 百分制直算 + 重修取最高)
- [4. 核心实现:学期 GPA / 累计 GPA(完整代码)](#4. 核心实现:学期 GPA / 累计 GPA(完整代码))
- [5. 可视化:GPA 趋势图](#5. 可视化:GPA 趋势图)
- [6. AI 专业进阶扩展(作品集方向)](#6. AI 专业进阶扩展(作品集方向))
- [7. 常见坑与解决方案](#7. 常见坑与解决方案)
- [8. 小结](#8. 小结)
1. 为什么要用 Python 统计绩点
手算/Excel 算绩点经常会遇到这些麻烦:
- 课程多了容易乱,尤其是学分加权
- 重修规则(取最高)一不小心就算错
- 你想看趋势、找出拖后腿课程,Excel 费劲
Python 的好处就是:
✅ 一次写好,长期复用
✅ 规则清晰(重修/学分加权)
✅ 能画图、能分析、能做成作品集
2. 数据准备:成绩表应该长什么样
推荐你用 CSV(Excel 也行,导出 CSV 最方便),每行一门课:
text
student_id,name,semester,course_code,course_name,credit,score
20230001,张三,2023-1,CS101,程序设计基础,4,88
20230001,张三,2023-2,CS101,程序设计基础,4,92
20230001,张三,2023-2,CS102,数据结构,4,79
字段解释:
semester:建议用2023-1 / 2023-2这类可排序格式(非常省事)credit:学分(决定权重)score:百分制分数(0-100)
如果你还想区分必修/选修、课程类型,也可以加列
category,后面统计会更强大。
3. 规则说明:5.0 制 + 百分制直算 + 重修取最高
你给的规则是:
- 5.0 制
- 百分制直算
- 重修取最高
这里"百分制直算"常见理解是:
把分数线性映射到 0~5 之间,比如:
\\text{GPA} = \\frac{\\text{score}}{100} \\times 5
- 100 分 → 5.0
- 80 分 → 4.0
- 60 分 → 3.0
- 0 分 → 0.0
如果你们学校对不及格(<60)有特殊处理(比如直接 0),也能在函数里一行改掉。
4. 核心实现:学期 GPA / 累计 GPA(完整代码)
这段代码会:
- 读取
grades.csv - 按规则把
score转成gpa_point(0-5) - 对同一门课(同学 + 课程号)只保留 GPA 最高的一次(重修取最高)
- 计算 学期 GPA 与 累计 GPA
依赖:
pip install pandas numpy matplotlib
python
import pandas as pd
import numpy as np
# ======================
# 1) 读取数据
# ======================
df = pd.read_csv("grades.csv")
# 确保数值列类型正确
df["credit"] = df["credit"].astype(float)
df["score"] = df["score"].astype(float)
# ======================
# 2) 百分制直算 -> 5.0 GPA
# GPA = score/100 * 5
# (可选:若你们学校 <60 直接算 0,可打开下面一行)
# ======================
def score_to_gpa_5(score: float) -> float:
if pd.isna(score):
return np.nan
score = float(score)
# 可选规则:不及格直接 0
# if score < 60:
# return 0.0
gpa = (score / 100.0) * 5.0
# 防止越界
return max(0.0, min(5.0, gpa))
df["gpa_point"] = df["score"].apply(score_to_gpa_5)
# ======================
# 3) 重修取最高:同一学生同一课程,保留 gpa_point 最大的那次
# 若出现并列(同分),默认保留最晚学期那条(通过排序实现)
# ======================
df_sorted = df.sort_values(by=["student_id", "course_code", "gpa_point", "semester"])
df_best = df_sorted.groupby(["student_id", "course_code"], as_index=False).tail(1)
# ======================
# 4) 学期 GPA(学分加权平均)
# semester_gpa = sum(credit * gpa) / sum(credit)
# ======================
def weighted_gpa(group: pd.DataFrame) -> float:
credits = group["credit"].astype(float)
points = group["gpa_point"].astype(float)
total_credits = credits.sum()
if total_credits == 0:
return np.nan
return (credits * points).sum() / total_credits
semester_gpa = (
df_best.groupby(["student_id", "semester"])
.apply(weighted_gpa)
.reset_index()
)
semester_gpa.columns = ["student_id", "semester", "semester_gpa"]
# ======================
# 5) 累计 GPA:按学期排序逐步累加
# cumulative_gpa = cumulative_points / cumulative_credits
# ======================
df_best = df_best.copy()
df_best["credit_point"] = df_best["credit"] * df_best["gpa_point"]
sem_stats = (
df_best.groupby(["student_id", "semester"])
.agg(
semester_credits=("credit", "sum"),
semester_points=("credit_point", "sum"),
)
.reset_index()
.sort_values(["student_id", "semester"])
)
sem_stats["cumulative_credits"] = sem_stats.groupby("student_id")["semester_credits"].cumsum()
sem_stats["cumulative_points"] = sem_stats.groupby("student_id")["semester_points"].cumsum()
sem_stats["cumulative_gpa"] = sem_stats["cumulative_points"] / sem_stats["cumulative_credits"]
# ======================
# 6) 输出查看
# ======================
print("=== Semester GPA (5.0 scale) ===")
print(semester_gpa)
print("\n=== Cumulative GPA (5.0 scale) ===")
print(sem_stats[[
"student_id", "semester",
"semester_credits", "semester_points",
"cumulative_credits", "cumulative_gpa"
]])
5. 可视化:GPA 趋势图
给某个同学画"累计 GPA 随学期变化"折线图:
python
import matplotlib.pyplot as plt
sid = "20230001" # 改成你的 student_id
stu = sem_stats[sem_stats["student_id"] == sid].sort_values("semester")
plt.figure(figsize=(8, 4))
plt.plot(stu["semester"], stu["cumulative_gpa"], marker="o")
plt.title("Cumulative GPA over Semesters (5.0 scale)")
plt.xlabel("Semester")
plt.ylabel("Cumulative GPA")
plt.grid(True)
plt.tight_layout()
plt.show()
你还可以顺手加两张图:
- 每学期学分(压力)变化:看看是不是"学分太大导致 GPA 下滑"
- 课程 GPA 分布直方图:看自己集中在哪个区间
6. AI 专业进阶扩展(作品集方向)
想把它做成更"AI/数据味儿"的项目,可以往这些方向加:
6.1 哪些课最影响 GPA?(课程贡献排行)
- 计算
credit * gpa_point,贡献高且低绩点的课程就是"重点改进对象"。
6.2 下学期 GPA 预测(入门机器学习)
特征示例:
- 上学期 GPA、累计 GPA
- 学分总量、核心课均分
- 课程难度标签(可手动标注)
模型:线性回归 / 随机森林 都能做出可解释结果。
6.3 Streamlit 仪表盘(一键上传 CSV 出报告)
这会让你的项目变成"可交付的小工具",非常适合写进简历/作品集
7. 常见坑与解决方案
7.1 学期排序不对
强烈建议用 2023-1 / 2023-2 这种格式。
如果你是 2023-Fall / 2024-Spring,建议加一列 semester_index 做排序。
7.2 "重修取最高"到底按什么最高?
一般是"绩点最高/分数最高"。这两者在"线性直算"下等价。
如果你学校对不及格处理特殊(比如记 0),要在 score_to_gpa_5() 里体现。
7.3 有些课不计入 GPA
比如体育/通识/实践课等,可以加一列 count_in_gpa(Y/N),统计前过滤即可。
8. 小结
现在已经有了一个完整、可复用的绩点统计工具:
支持 5.0 制 + 百分制直算
支持重修取最高
自动算学期/累计 GPA + 出图