学习小项目:用 Python 自动统计编程课绩点(5.0 制|百分制直算|重修取最高)

关键词: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(完整代码)

这段代码会:

  1. 读取 grades.csv
  2. 按规则把 score 转成 gpa_point(0-5)
  3. 对同一门课(同学 + 课程号)只保留 GPA 最高的一次(重修取最高)
  4. 计算 学期 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 + 出图


相关推荐
a程序小傲2 小时前
得物Java面试被问:反射机制的原理和应用场景
java·python·面试
xingzhemengyou12 小时前
Python GUI中常用的after
开发语言·python
confiself2 小时前
UI-Ins技术报告学习
学习
老华带你飞2 小时前
智能菜谱推荐|基于java + vue智能菜谱推荐系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
航Hang*2 小时前
第五章:网络系统建设与运维(中级)——生成树协议
运维·服务器·网络·笔记·华为·ensp
郝学胜-神的一滴2 小时前
Python抽象基类与abc模块详解:优雅设计接口的利器
开发语言·python·程序人生
lsx2024062 小时前
NumPy 创建数组
开发语言
小南知更鸟2 小时前
前端静态项目快速启动:python -m http.server 4173 与 npx serve . 全解析
前端·python·http
小钟不想敲代码2 小时前
Python(三)
java·python·servlet