高中成绩可视化平台开发笔记

发布于:2025-06-27 ⋅ 阅读:(10) ⋅ 点赞:(0)

高中成绩可视化平台(1)


一、项目概述

本系统是一个基于 PyQt5 和 Matplotlib 的高中成绩数据可视化分析平台,旨在帮助教师快速了解学生成绩分布、班级对比、学科表现等关键指标。平台支持文科与理科的数据切换,并提供多个维度的图表展示和交互式操作。

核心功能:

  • 文科/理科数据动态切换
  • 四个核心分析页面(总览、学科分析、班级分析、排名分析)
  • 图表联动刷新机制
  • 表格与图表双向绑定
  • 自定义样式与视觉美化

二、技术选型

技术 用途
PyQt5 GUI 界面构建
Pandas 数据处理与分析
Matplotlib 图表绘制
QTabWidget 多选项卡管理
QComboBox / QTableWidget 控件交互

三、模块划分与类结构

整个平台主要由两个类组成:

  • ScoreVisualizationPlatform:主窗口类,负责 UI 构建与事件处理
  • DataProcessor:数据处理类,封装所有数据读取与分析逻辑

四、UI 构建与控件初始化

def __init__(self):
    super().__init__()
    self.setWindowTitle("2023级成绩可视化平台")
    self.resize(1200, 800)

    # 初始化数据处理器
    self.data_processor = DataProcessor()

    # 主布局
    main_layout = QVBoxLayout(self)
    control_panel = QWidget()
    control_layout = QHBoxLayout(control_panel)

    # 下拉框选择文理类型
    self.stream_combo = QComboBox()
    self.stream_combo.addItems(["文科", "理科"])
    control_layout.addWidget(QLabel("文理类型:"))
    control_layout.addWidget(self.stream_combo)

    # 科目选择下拉框
    self.subject_combo = QComboBox()
    control_layout.addWidget(QLabel("科目选择:"))
    control_layout.addWidget(self.subject_combo)

    # 班级选择下拉框
    self.classes_combo = QComboBox()
    control_layout.addWidget(QLabel("班级选择:"))
    control_layout.addWidget(self.classes_combo)

    # 加载数据按钮
    load_button = QPushButton("加载数据")
    control_layout.addWidget(load_button)

    # 添加控件到主布局
    main_layout.addWidget(control_panel)

    # 创建选项卡
    self.tab_widget = QTabWidget()
    main_layout.addWidget(self.tab_widget)

    # 初始化各选项卡
    self.create_overview_tab()
    self.create_subject_analysis_tab()
    self.create_class_analysis_tab()
    self.create_ranking_tab()

    # 绑定信号
    self.stream_combo.currentTextChanged.connect(self.on_data_type_changed)
    self.subject_combo.currentTextChanged.connect(lambda: self.refresh_all_charts())
    self.classes_combo.currentTextChanged.connect(lambda: self.refresh_all_charts())
    load_button.clicked.connect(self.load_data)

📌 提示:该部分完成主窗口的创建,包含控制面板、四个选项卡以及数据加载按钮。


五、选项卡页面设计与实现

1. 总览页 create_overview_tab()

def create_overview_tab(self):
    tab = QWidget()
    self.tab_widget.addTab(tab, "总览")
    layout = QGridLayout(tab)

    # 图1:总分前20名图表
    self.total_score_chart = ChartWidget("总分Top20")
    layout.addWidget(self.total_score_chart, 0, 0, 1, 1)

    # 图2:班级占比图表
    self.class_distribution_chart = ChartWidget("Top20班级占比")
    layout.addWidget(self.class_distribution_chart, 0, 3, 1, 3)

    # 图3:各科目平均分对比
    self.subject_avg_chart = ChartWidget("学科Top20")
    layout.addWidget(self.subject_avg_chart, 1, 0, 1, 1)

    # 图4:班级学科分布(占第1行后两列)
    self.class_subject_chart = ChartWidget("学科Top20班级占比")
    layout.addWidget(self.class_subject_chart, 1, 3, 1, 3)

图表说明:

区域 内容
左上 总分前20名柱状图
右上 班级分布饼图
左下 当前科目前20名柱状图
右下 当前科目班级分布饼图

2. 学科分析页 create_subject_analysis_tab()

def create_subject_analysis_tab(self):
    tab = QWidget()
    self.tab_widget.addTab(tab, "学科分析")
    layout = QGridLayout(tab)

    self.passing_rank = ChartWidget("本科上线排名")
    layout.addWidget(self.passing_rank, 0, 0)

    self.subject_stats_chart = ChartWidget("各科目统计分析")
    layout.addWidget(self.subject_stats_chart, 0, 1)

    self.single_subject_chart = ChartWidget("单科目上线人数排名")
    layout.addWidget(self.single_subject_chart, 1, 0)

    self.correlation_chart = ChartWidget("科目成绩相关性分析")
    layout.addWidget(self.correlation_chart, 1, 1)

图表说明:

区域 内容
左上 各班过线人数柱状图
右上 各科平均分柱状图
左下 各科及格人数柱状图
右下 两个科目的散点图(显示相关性)

3. 班级分析页 create_class_analysis_tab()

def create_class_analysis_tab(self):
    tab = QWidget()
    self.tab_widget.addTab(tab, "班级分析")
    layout = QGridLayout(tab)

    self.class_avg_chart = ChartWidget("各班级平均分对比")
    layout.addWidget(self.class_avg_chart, 0, 0)

    self.class_score_dist_chart = ChartWidget("班级成绩分布")
    layout.addWidget(self.class_score_dist_chart, 0, 1)

    self.class_subject_performance_chart = ChartWidget("班级各科表现")
    layout.addWidget(self.class_subject_performance_chart, 1, 0)

    self.total_top_5 = ChartWidget("各班级top5各科表现")
    layout.addWidget(self.total_top_5, 1, 1)

图表说明:

区域 内容
左上 班级平均分柱状图
右上 成绩分布直方图
左下 各科平均分折线图
右下 每个班级 top5 学生的各科成绩雷达图

4. 排名分析页 create_ranking_tab()

def create_ranking_tab(self):
    tab = QWidget()
    self.tab_widget.addTab(tab, "排名分析")
    main_layout = QVBoxLayout(tab)

    tables_container = QWidget()
    tables_layout = QVBoxLayout(tables_container)
    inner_layout = QHBoxLayout(tab)

    ranking_group = QVBoxLayout()
    self.ranking_title = QLabel("年级前100名学生")
    self.ranking_table = QTableWidget()
    self.ranking_table.setSortingEnabled(True)
    ranking_group.addWidget(self.ranking_title)
    ranking_group.addWidget(self.ranking_table)

    class_group = QVBoxLayout()
    self.class_title = QLabel("当前班级单科成绩排名")
    self.class_tables = QTableWidget()
    self.class_tables.setSortingEnabled(True)
    class_group.addWidget(self.class_title)
    class_group.addWidget(self.class_tables)

    inner_layout.addLayout(ranking_group, stretch=1)
    inner_layout.addLayout(class_group, stretch=1)
    tables_layout.addLayout(inner_layout)
    main_layout.addWidget(tables_container)

    self.figure = Figure(figsize=(5, 3))
    self.canvas = FigureCanvas(self.figure)
    self.canvas.setStyleSheet("background-color:rgba(0, 1, 1, 0.3); border: 1px solid #ccc;")
    main_layout.addWidget(self.canvas)

图表说明:

区域 内容
上部 两个表格(年级前100名 / 当前班级单科排名)
下部 动态绘图区域(用于展示趋势、对比等图表)

六、数据处理与图表联动

1. 数据加载与刷新机制

def load_data(self):
    if self.data_processor.load_data():
        self.refresh_all_charts()
        QMessageBox.information(self, "成功", "数据加载完成!")
    else:
        QMessageBox.warning(self, "错误", "数据加载失败,请检查数据文件!")

def on_data_type_changed(self, data_type):
    self.refresh_all_charts()

def refresh_all_charts(self):
    data_type = "liberal" if self.stream_combo.currentText() == "文科" else "science"
    subject_prefix = "文科" if data_type == "liberal" else "理科"
    subject_type = self.subject_combo.currentText()

    self.total_score_chart.title = f"2023级{subject_prefix}总分前20名"
    self.class_distribution_chart.title = f"2023级{subject_prefix}前20名班级占比"

    self.update_overview_charts(data_type, subject_type)
    self.update_subject_analysis_charts(data_type)
    self.update_class_analysis_charts(data_type)
    self.update_ranking_table(data_type)

✅ 特点:通过组合文理科类型 + 科目 + 班级,动态更新所有图表与表格内容。


2. 总览页图表更新 update_overview_charts()

def update_overview_charts(self, data_type, subject):
    # 总分前20名柱状图
    top_students = self.data_processor.get_top_students(data_type, 20)
    if top_students is not None:
        self.total_score_chart.plot_bar_chart(
            top_students, '姓名', '总分',
            f"{'文科' if data_type == 'liberal' else '理科'}总分Top20"
        )

    # 班级分布饼图
    top20_class_dist = top_students['班级'].value_counts().reset_index()
    top20_class_dist.columns = ['班级', '人数']
    self.class_distribution_chart.plot_pie_chart(
        top20_class_dist, '班级', '人数',
        f"{'文科' if data_type == 'liberal' else '理科'}Top20班级占比"
    )

    # 单科前20名柱状图
    subject_top20 = data.nlargest(20, subject)[['姓名', subject]]
    self.subject_avg_chart.plot_bar_chart(
        subject_top20, '姓名', subject,
        f"{'文科' if data_type == 'liberal' else '理科'}{subject}Top20"
    )

    # 班级学科分布饼图
    class_subject_data = self.data_processor.get_class_subject_top20(data_type)
    subject_class_dist = class_subject_data[subject].reset_index()
    subject_class_dist.columns = ['班级', '人数']
    self.class_subject_chart.plot_pie_chart(
        subject_class_dist, '班级', '人数',
        f"{'文科' if data_type == 'liberal' else '理科'}{subject}Top20班级占比"
    )

3. 学科分析页图表更新 update_subject_analysis_charts()

def update_subject_analysis_charts(self, data_type):
    passing = self.data_processor.get_pass_line(data_type)
    totals = self.data_processor.calculate_total_scores(data_type)
    ranks = totals[totals['总分'] > passing].groupby('班级').size(). \
        reset_index(name='人数').sort_values(by='人数', ascending=False)
    self.passing_rank.plot_bar_chart(
        ranks, '班级', '人数', f"{'文科' if data_type == 'liberal' else '理科'}各班过线人数"
    )

    subject_analysis = self.data_processor.get_subject_analysis(data_type)
    avg_scores = subject_analysis['平均分'].reset_index()
    avg_scores.columns = ['科目', '平均分']
    self.subject_stats_chart.plot_bar_chart(avg_scores, '科目', '平均分', "各科目平均分对比")

    online_counts = []
    for subject in subjects:
        if subject in data.columns:
            online_count = (data[subject] >= 60).sum()
            online_counts.append({'科目': subject, '及格人数': online_count})
    online_df = pd.DataFrame(online_counts)
    self.single_subject_chart.plot_bar_chart(online_df, '科目', '及格人数', "各科目及格人数统计")

    subject1, subject2 = subjects[0], subjects[1]
    clean_data = data[[subject1, subject2]].dropna()
    ax.scatter(clean_data[subject1], clean_data[subject2], alpha=0.9, edgecolors='#8A0808')
    self.correlation_chart.figure.tight_layout()
    self.correlation_chart.canvas.draw()

4. 班级分析页图表更新 update_class_analysis_charts()

def update_class_analysis_charts(self, data_type):
    # 平均总分柱状图
    class_avg_scores = []
    for class_name in data['班级'].unique():
        class_data = data[data['班级'] == class_name]
        total_scores = class_data[subjects].sum(axis=1, skipna=True)
        avg_score = total_scores.mean()
        class_avg_scores.append({'班级': class_name, '平均总分': avg_score})
    class_avg_df = pd.DataFrame(class_avg_scores).sort_values('平均总分', ascending=False)
    self.class_avg_chart.plot_bar_chart(class_avg_df, '班级', '平均总分', "各班级平均总分对比")

    # 分数段分布柱状图
    bins = [0, 300, 400, 500, 600, 700, 800]
    labels = ['0-300', '300-400', '400-500', '500-600', '600-700', '700-800']
    score_dist = []
    for label, (low, high) in zip(labels, zip(bins[:-1], bins[1:])):
        count = ((total_scores_data['总分'] >= low) & (total_scores_data['总分'] < high)).sum()
        score_dist.append({'分数段': label, '人数': count})
    score_dist_df = pd.DataFrame(score_dist)
    self.class_score_dist_chart.plot_bar_chart(score_dist_df, '分数段', '人数', "总分分布统计")

    # 各科表现堆叠柱状图
    stacked_data = []
    for class_name in sorted(all_classes):
        row = {'班级': class_name}
        for subject, subject_data in class_subject_data.items():
            row[subject] = subject_data.get(class_name, 0)
        stacked_data.append(row)
    stacked_df = pd.DataFrame(stacked_data)
    self.class_subject_performance_chart.plot_stacked_bar(stacked_df, "各班级各科目前20名人数分布")

    # 各班前5名图表
    top_5 = self.data_processor.get_class_top_5(data_type, class_name)[0][['姓名'] + subjects]
    self.total_top_5.plot_stacked_bar(
        data=top_5, title=f"{class_name} 学生学科成绩分布",
        item_1='姓名', item_2='姓名',
        x_label='学生姓名', y_label='分数'
    )

5. 排名分析页表格与图表更新 update_ranking_table()

def update_ranking_table(self, data_type):
    top_students = self.data_processor.get_top_students(data_type, 100)
    if top_students is not None:
        self.ranking_table.setRowCount(len(top_students))
        self.ranking_table.setColumnCount(4)
        self.ranking_table.setHorizontalHeaderLabels(['排名', '姓名', '班级', '总分'])
        for i, (_, row) in enumerate(top_students.iterrows()):
            self.ranking_table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
            self.ranking_table.setItem(i, 1, QTableWidgetItem(str(row['姓名'])))
            self.ranking_table.setItem(i, 2, QTableWidgetItem(str(row['班级'])))
            self.ranking_table.setItem(i, 3, QTableWidgetItem(f"{row['总分']:.1f}"))
        self.ranking_table.resizeColumnsToContents()

    cla = self.classes_combo.currentText()
    sujects = self.subject_combo.currentText()
    data = self.data_processor.get_subject_scores(data_type, cla, sujects)
    if data is not None:
        self.class_tables.setRowCount(len(data))
        self.class_tables.setColumnCount(4)
        self.class_tables.setHorizontalHeaderLabels(['单科排名', '姓名', '班级', sujects])
        for i, (_, row) in enumerate(data.iterrows()):
            self.class_tables.setItem(i, 0, QTableWidgetItem(str(i + 1)))
            self.class_tables.setItem(i, 1, QTableWidgetItem(str(row['姓名'])))
            self.class_tables.setItem(i, 2, QTableWidgetItem(str(row['班级'])))
            self.class_tables.setItem(i, 3, QTableWidgetItem(f"{row[sujects]:.1f}"))
        self.class_tables.resizeColumnsToContents()

    data = data.head()
    self.figure.clear()
    self.figure.patch.set_alpha(0.0)
    ax = self.figure.add_subplot(111)
    ax.set_facecolor((0, 1, 1, 0.3))
    bars = ax.bar(data["姓名"], data[sujects], color="#4CAF50")
    for bar in bars:
        yval = bar.get_height()
        ax.text(bar.get_x() + bar.get_width() / 2.0, yval, int(yval),
                va='bottom', ha='center', color='cyan')
    ax.set_title(f"{'文科' if data_type == 'liberal' else '理科'}-{cla}-{sujects}前5名", color='cyan')
    ax.set_ylabel('分数', color='cyan')
    ax.set_xlabel('姓名', color='cyan')
    ax.grid(True, linestyle='--', alpha=0.6)
    ax.tick_params(axis='x', colors='cyan')
    ax.tick_params(axis='y', colors='cyan')
    self.canvas.draw()

高中成绩可视化平台(2)


一、项目概述

本系统是一个基于 PyQt5 和 Matplotlib 的高中成绩数据可视化分析平台,旨在帮助教师快速了解学生成绩分布、班级对比、学科表现等关键指标。平台支持文科与理科的数据切换,并提供多个维度的图表展示和交互式操作。

核心功能:

  • 文科/理科数据动态切换
  • 四个核心分析页面(总览、学科分析、班级分析、排名分析)
  • 图表联动刷新机制
  • 表格与图表双向绑定
  • 自定义样式与视觉美化

二、技术选型

技术 用途
PyQt5 GUI 界面构建
Pandas 数据处理与分析
Matplotlib 图表绘制
QTabWidget 多选项卡管理
QComboBox / QTableWidget 控件交互

三、模块划分与类结构

整个平台主要由两个类组成:

  • ScoreVisualizationPlatform:主窗口类,负责 UI 构建与事件处理
  • DataProcessor:数据处理类,封装所有数据读取与分析逻辑

四、UI 构建与控件初始化

def __init__(self):
    super().__init__()
    self.setWindowTitle("2023级成绩可视化平台")
    self.resize(1200, 800)

    # 初始化数据处理器
    self.data_processor = DataProcessor()

    # 主布局
    main_layout = QVBoxLayout(self)
    control_panel = QWidget()
    control_layout = QHBoxLayout(control_panel)

    # 下拉框选择文理类型
    self.stream_combo = QComboBox()
    self.stream_combo.addItems(["文科", "理科"])
    control_layout.addWidget(QLabel("文理类型:"))
    control_layout.addWidget(self.stream_combo)

    # 科目选择下拉框
    self.subject_combo = QComboBox()
    control_layout.addWidget(QLabel("科目选择:"))
    control_layout.addWidget(self.subject_combo)

    # 班级选择下拉框
    self.classes_combo = QComboBox()
    control_layout.addWidget(QLabel("班级选择:"))
    control_layout.addWidget(self.classes_combo)

    # 加载数据按钮
    load_button = QPushButton("加载数据")
    control_layout.addWidget(load_button)

    # 添加控件到主布局
    main_layout.addWidget(control_panel)

    # 创建选项卡
    self.tab_widget = QTabWidget()
    main_layout.addWidget(self.tab_widget)

    # 初始化各选项卡
    self.create_overview_tab()
    self.create_subject_analysis_tab()
    self.create_class_analysis_tab()
    self.create_ranking_tab()

    # 绑定信号
    self.stream_combo.currentTextChanged.connect(self.on_data_type_changed)
    self.subject_combo.currentTextChanged.connect(lambda: self.refresh_all_charts())
    self.classes_combo.currentTextChanged.connect(lambda: self.refresh_all_charts())
    load_button.clicked.connect(self.load_data)

📌 提示:该部分完成主窗口的创建,包含控制面板、四个选项卡以及数据加载按钮。


五、选项卡页面设计与实现

1. 总览页 create_overview_tab()

def create_overview_tab(self):
    tab = QWidget()
    self.tab_widget.addTab(tab, "总览")
    layout = QGridLayout(tab)

    # 图1:总分前20名图表
    self.total_score_chart = ChartWidget("总分Top20")
    layout.addWidget(self.total_score_chart, 0, 0, 1, 1)

    # 图2:班级占比图表
    self.class_distribution_chart = ChartWidget("Top20班级占比")
    layout.addWidget(self.class_distribution_chart, 0, 3, 1, 3)

    # 图3:各科目平均分对比
    self.subject_avg_chart = ChartWidget("学科Top20")
    layout.addWidget(self.subject_avg_chart, 1, 0, 1, 1)

    # 图4:班级学科分布(占第1行后两列)
    self.class_subject_chart = ChartWidget("学科Top20班级占比")
    layout.addWidget(self.class_subject_chart, 1, 3, 1, 3)

图表说明:

区域 内容
左上 总分前20名柱状图
右上 班级分布饼图
左下 当前科目前20名柱状图
右下 当前科目班级分布饼图

2. 学科分析页 create_subject_analysis_tab()

def create_subject_analysis_tab(self):
    tab = QWidget()
    self.tab_widget.addTab(tab, "学科分析")
    layout = QGridLayout(tab)

    self.passing_rank = ChartWidget("本科上线排名")
    layout.addWidget(self.passing_rank, 0, 0)

    self.subject_stats_chart = ChartWidget("各科目统计分析")
    layout.addWidget(self.subject_stats_chart, 0, 1)

    self.single_subject_chart = ChartWidget("单科目上线人数排名")
    layout.addWidget(self.single_subject_chart, 1, 0)

    self.correlation_chart = ChartWidget("科目成绩相关性分析")
    layout.addWidget(self.correlation_chart, 1, 1)

图表说明:

区域 内容
左上 各班过线人数柱状图
右上 各科平均分柱状图
左下 各科及格人数柱状图
右下 两个科目的散点图(显示相关性)

3. 班级分析页 create_class_analysis_tab()

def create_class_analysis_tab(self):
    tab = QWidget()
    self.tab_widget.addTab(tab, "班级分析")
    layout = QGridLayout(tab)

    self.class_avg_chart = ChartWidget("各班级平均分对比")
    layout.addWidget(self.class_avg_chart, 0, 0)

    self.class_score_dist_chart = ChartWidget("班级成绩分布")
    layout.addWidget(self.class_score_dist_chart, 0, 1)

    self.class_subject_performance_chart = ChartWidget("班级各科表现")
    layout.addWidget(self.class_subject_performance_chart, 1, 0)

    self.total_top_5 = ChartWidget("各班级top5各科表现")
    layout.addWidget(self.total_top_5, 1, 1)

图表说明:

区域 内容
左上 班级平均分柱状图
右上 成绩分布直方图
左下 各科平均分折线图
右下 每个班级 top5 学生的各科成绩雷达图

4. 排名分析页 create_ranking_tab()

def create_ranking_tab(self):
    tab = QWidget()
    self.tab_widget.addTab(tab, "排名分析")
    main_layout = QVBoxLayout(tab)

    tables_container = QWidget()
    tables_layout = QVBoxLayout(tables_container)
    inner_layout = QHBoxLayout(tab)

    ranking_group = QVBoxLayout()
    self.ranking_title = QLabel("年级前100名学生")
    self.ranking_table = QTableWidget()
    self.ranking_table.setSortingEnabled(True)
    ranking_group.addWidget(self.ranking_title)
    ranking_group.addWidget(self.ranking_table)

    class_group = QVBoxLayout()
    self.class_title = QLabel("当前班级单科成绩排名")
    self.class_tables = QTableWidget()
    self.class_tables.setSortingEnabled(True)
    class_group.addWidget(self.class_title)
    class_group.addWidget(self.class_tables)

    inner_layout.addLayout(ranking_group, stretch=1)
    inner_layout.addLayout(class_group, stretch=1)
    tables_layout.addLayout(inner_layout)
    main_layout.addWidget(tables_container)

    self.figure = Figure(figsize=(5, 3))
    self.canvas = FigureCanvas(self.figure)
    self.canvas.setStyleSheet("background-color:rgba(0, 1, 1, 0.3); border: 1px solid #ccc;")
    main_layout.addWidget(self.canvas)

图表说明:

区域 内容
上部 两个表格(年级前100名 / 当前班级单科排名)
下部 动态绘图区域(用于展示趋势、对比等图表)

六、数据处理与图表联动

1. 数据加载与刷新机制

def load_data(self):
    if self.data_processor.load_data():
        self.refresh_all_charts()
        QMessageBox.information(self, "成功", "数据加载完成!")
    else:
        QMessageBox.warning(self, "错误", "数据加载失败,请检查数据文件!")

def on_data_type_changed(self, data_type):
    self.refresh_all_charts()

def refresh_all_charts(self):
    data_type = "liberal" if self.stream_combo.currentText() == "文科" else "science"
    subject_prefix = "文科" if data_type == "liberal" else "理科"
    subject_type = self.subject_combo.currentText()

    self.total_score_chart.title = f"2023级{subject_prefix}总分前20名"
    self.class_distribution_chart.title = f"2023级{subject_prefix}前20名班级占比"

    self.update_overview_charts(data_type, subject_type)
    self.update_subject_analysis_charts(data_type)
    self.update_class_analysis_charts(data_type)
    self.update_ranking_table(data_type)

✅ 特点:通过组合文理科类型 + 科目 + 班级,动态更新所有图表与表格内容。


2. 总览页图表更新 update_overview_charts()

def update_overview_charts(self, data_type, subject):
    # 总分前20名柱状图
    top_students = self.data_processor.get_top_students(data_type, 20)
    if top_students is not None:
        self.total_score_chart.plot_bar_chart(
            top_students, '姓名', '总分',
            f"{'文科' if data_type == 'liberal' else '理科'}总分Top20"
        )

    # 班级分布饼图
    top20_class_dist = top_students['班级'].value_counts().reset_index()
    top20_class_dist.columns = ['班级', '人数']
    self.class_distribution_chart.plot_pie_chart(
        top20_class_dist, '班级', '人数',
        f"{'文科' if data_type == 'liberal' else '理科'}Top20班级占比"
    )

    # 单科前20名柱状图
    subject_top20 = data.nlargest(20, subject)[['姓名', subject]]
    self.subject_avg_chart.plot_bar_chart(
        subject_top20, '姓名', subject,
        f"{'文科' if data_type == 'liberal' else '理科'}{subject}Top20"
    )

    # 班级学科分布饼图
    class_subject_data = self.data_processor.get_class_subject_top20(data_type)
    subject_class_dist = class_subject_data[subject].reset_index()
    subject_class_dist.columns = ['班级', '人数']
    self.class_subject_chart.plot_pie_chart(
        subject_class_dist, '班级', '人数',
        f"{'文科' if data_type == 'liberal' else '理科'}{subject}Top20班级占比"
    )

3. 学科分析页图表更新 update_subject_analysis_charts()

def update_subject_analysis_charts(self, data_type):
    passing = self.data_processor.get_pass_line(data_type)
    totals = self.data_processor.calculate_total_scores(data_type)
    ranks = totals[totals['总分'] > passing].groupby('班级').size(). \
        reset_index(name='人数').sort_values(by='人数', ascending=False)
    self.passing_rank.plot_bar_chart(
        ranks, '班级', '人数', f"{'文科' if data_type == 'liberal' else '理科'}各班过线人数"
    )

    subject_analysis = self.data_processor.get_subject_analysis(data_type)
    avg_scores = subject_analysis['平均分'].reset_index()
    avg_scores.columns = ['科目', '平均分']
    self.subject_stats_chart.plot_bar_chart(avg_scores, '科目', '平均分', "各科目平均分对比")

    online_counts = []
    for subject in subjects:
        if subject in data.columns:
            online_count = (data[subject] >= 60).sum()
            online_counts.append({'科目': subject, '及格人数': online_count})
    online_df = pd.DataFrame(online_counts)
    self.single_subject_chart.plot_bar_chart(online_df, '科目', '及格人数', "各科目及格人数统计")

    subject1, subject2 = subjects[0], subjects[1]
    clean_data = data[[subject1, subject2]].dropna()
    ax.scatter(clean_data[subject1], clean_data[subject2], alpha=0.9, edgecolors='#8A0808')
    self.correlation_chart.figure.tight_layout()
    self.correlation_chart.canvas.draw()

4. 班级分析页图表更新 update_class_analysis_charts()

def update_class_analysis_charts(self, data_type):
    # 平均总分柱状图
    class_avg_scores = []
    for class_name in data['班级'].unique():
        class_data = data[data['班级'] == class_name]
        total_scores = class_data[subjects].sum(axis=1, skipna=True)
        avg_score = total_scores.mean()
        class_avg_scores.append({'班级': class_name, '平均总分': avg_score})
    class_avg_df = pd.DataFrame(class_avg_scores).sort_values('平均总分', ascending=False)
    self.class_avg_chart.plot_bar_chart(class_avg_df, '班级', '平均总分', "各班级平均总分对比")

    # 分数段分布柱状图
    bins = [0, 300, 400, 500, 600, 700, 800]
    labels = ['0-300', '300-400', '400-500', '500-600', '600-700', '700-800']
    score_dist = []
    for label, (low, high) in zip(labels, zip(bins[:-1], bins[1:])):
        count = ((total_scores_data['总分'] >= low) & (total_scores_data['总分'] < high)).sum()
        score_dist.append({'分数段': label, '人数': count})
    score_dist_df = pd.DataFrame(score_dist)
    self.class_score_dist_chart.plot_bar_chart(score_dist_df, '分数段', '人数', "总分分布统计")

    # 各科表现堆叠柱状图
    stacked_data = []
    for class_name in sorted(all_classes):
        row = {'班级': class_name}
        for subject, subject_data in class_subject_data.items():
            row[subject] = subject_data.get(class_name, 0)
        stacked_data.append(row)
    stacked_df = pd.DataFrame(stacked_data)
    self.class_subject_performance_chart.plot_stacked_bar(stacked_df, "各班级各科目前20名人数分布")

    # 各班前5名图表
    top_5 = self.data_processor.get_class_top_5(data_type, class_name)[0][['姓名'] + subjects]
    self.total_top_5.plot_stacked_bar(
        data=top_5, title=f"{class_name} 学生学科成绩分布",
        item_1='姓名', item_2='姓名',
        x_label='学生姓名', y_label='分数'
    )

5. 排名分析页表格与图表更新 update_ranking_table()

def update_ranking_table(self, data_type):
    top_students = self.data_processor.get_top_students(data_type, 100)
    if top_students is not None:
        self.ranking_table.setRowCount(len(top_students))
        self.ranking_table.setColumnCount(4)
        self.ranking_table.setHorizontalHeaderLabels(['排名', '姓名', '班级', '总分'])
        for i, (_, row) in enumerate(top_students.iterrows()):
            self.ranking_table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
            self.ranking_table.setItem(i, 1, QTableWidgetItem(str(row['姓名'])))
            self.ranking_table.setItem(i, 2, QTableWidgetItem(str(row['班级'])))
            self.ranking_table.setItem(i, 3, QTableWidgetItem(f"{row['总分']:.1f}"))
        self.ranking_table.resizeColumnsToContents()

    cla = self.classes_combo.currentText()
    sujects = self.subject_combo.currentText()
    data = self.data_processor.get_subject_scores(data_type, cla, sujects)
    if data is not None:
        self.class_tables.setRowCount(len(data))
        self.class_tables.setColumnCount(4)
        self.class_tables.setHorizontalHeaderLabels(['单科排名', '姓名', '班级', sujects])
        for i, (_, row) in enumerate(data.iterrows()):
            self.class_tables.setItem(i, 0, QTableWidgetItem(str(i + 1)))
            self.class_tables.setItem(i, 1, QTableWidgetItem(str(row['姓名'])))
            self.class_tables.setItem(i, 2, QTableWidgetItem(str(row['班级'])))
            self.class_tables.setItem(i, 3, QTableWidgetItem(f"{row[sujects]:.1f}"))
        self.class_tables.resizeColumnsToContents()

    data = data.head()
    self.figure.clear()
    self.figure.patch.set_alpha(0.0)
    ax = self.figure.add_subplot(111)
    ax.set_facecolor((0, 1, 1, 0.3))
    bars = ax.bar(data["姓名"], data[sujects], color="#4CAF50")
    for bar in bars:
        yval = bar.get_height()
        ax.text(bar.get_x() + bar.get_width() / 2.0, yval, int(yval),
                va='bottom', ha='center', color='cyan')
    ax.set_title(f"{'文科' if data_type == 'liberal' else '理科'}-{cla}-{sujects}前5名", color='cyan')
    ax.set_ylabel('分数', color='cyan')
    ax.set_xlabel('姓名', color='cyan')
    ax.grid(True, linestyle='--', alpha=0.6)
    ax.tick_params(axis='x', colors='cyan')
    ax.tick_params(axis='y', colors='cyan')
    self.canvas.draw()

七、项目结果图(部分)

总览
学科分析

附:qss样式

QMainWindow {
    background-color: #16003a;
}
QWidget {
    background-color: #16003a;
    color: cyan;
}
QTabWidget::pane {
    border: 1px solid rgba(221, 221, 221, 0);
    background-color: #16003a;
}
QTabBar::tab {
    background-color: #16003a;
    padding: 8px 16px;
    margin-right: 2px;
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
}
QTabBar::tab:selected {
    background-color: #00385e;
    color: #ffffff;
}
QPushButton {
    background-color: rgba(31, 106, 152, 0.81);
    color: cyan;
    border: none;
    padding: 8px 16px;
    border-radius: 4px;
    font-weight: bold;
}
QPushButton:hover {
    background-color: #00560f;
}
QGroupBox {
    color: #4dffff;
    font-size: 13px;
    border: 2px solid #4dffff;
    border-radius: 5px;
    margin-top: 2px;
    padding-top: 2px;
}
QGroupBox::title {
    color: cyan;
    subcontrol-origin: margin;
    left: 5px;
    padding: 0 5px 0 5px;
}
QLabel {
    color: cyan;
    text-align: center;
    background-color: #16003a;
    font-size: 20px;
    font-weight: bold;
}
QLabel#titleLabel {
    padding: 5px;
    font-size: 40px;
    font-family: "Microsoft YaHei";
    text-align: center;
}
QComboBox {
    background-color: rgba(162, 88, 0, 0.7);
    color: cyan;
    font-size: 12px;
    border: 1px solid #ffffff;
    border-radius: 35px;
    padding: 0px 5px;
}
QComboBox QAbstractItemView::item:hover {
    background-color: #0B6121;
}
  • 数据: 需要数据或遇到问题可联系QQ(1591236129)

网站公告

今日签到

点亮在社区的每一天
去签到