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

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


一、项目概述

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

核心功能:

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

二、技术选型

技术用途
PyQt5GUI 界面构建
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 的高中成绩数据可视化分析平台,旨在帮助教师快速了解学生成绩分布、班级对比、学科表现等关键指标。平台支持文科与理科的数据切换,并提供多个维度的图表展示和交互式操作。

核心功能:

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

二、技术选型

技术用途
PyQt5GUI 界面构建
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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/911911.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/911911.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自动化按需导入组件库的工具rust版本完成开源了

背景 当我为每个Vue项目使用ui组件库的时候&#xff0c;都会使用按需导入的方式来使用ui组件库。但是每次按需导入&#xff0c;不可避免的就需要做以下三步。我们以element plus ui组件库为例。 1. 安装依赖 第一步&#xff0c;当然是需要安装依赖。命令如下: pnpm add unp…

Linux内核中TCP分段的核心机制:tcp_fragment函数解析

引言:TCP分段的必要性 在TCP/IP协议栈中,MSS(最大分段大小) 限制了单次传输的数据量。当应用层发送的数据超过当前路径的MSS时,内核必须执行分段操作。tcp_fragment函数正是Linux内核中处理TCP分段的核心机制,它巧妙地在协议合规性、内存安全和性能效率之间取得平衡。 一…

【赵渝强老师】OceanBase OBServer节点的SQL层

OceanBase OBServer节点的SQL层将用户的SQL请求转化成对一个或多个Tablet的数据访问。SQL层会按照以下顺序经过一系列组件来处理一个SQL&#xff1a; Parser -->Resolver-->Transformer-->Optimizer-->CodeGenerator-->Executor。视频讲解如下 【赵渝强老师】O…

从“高配”到“普惠”,黑芝麻智能携手Nullmax打造辅助驾驶主流量产方案

近日&#xff0c;黑芝麻智能携手Nullmax打造的辅助驾驶主流量产方案正式发布。该方案面向8-15万元级别主流车型&#xff0c;基于单颗黑芝麻智能武当C1236跨域计算芯片&#xff0c;集成Nullmax全栈自研的软件技术架构&#xff0c;结合领先的视觉感知算法&#xff0c;打造高性能辅…

信息安全认证体系全解析:从资质证明到行业准入的实践指南

Hello&#xff01;大家好&#xff0c;小编是一名专注IT领域的资深探索家&#xff0c;大家发现了吗&#xff1f;现在刷招聘软件&#xff0c;国企安全岗必标 "CISP 优先"&#xff0c;外企招聘悄悄写着 "CISSP 加分"—— 这些带字母的证书到底是啥&#xff1f…

优雅地创建实体类——Builder 链式调用

我们来看以下的代码。改造前构造实体类用重载构造器或用 setter 对变量进行赋值&#xff0c;一旦变量变多则需要对每个变量进行 set 赋值&#xff0c;并且有可能会赋值错对象。 private static void test() {//1.构造器赋值Task task1 new Task("2023000000009439"…

如何轻松将照片从 iPhone 传输到 Android?

从 iPhone 换到 Android 手机后&#xff0c;你肯定不想丢掉珍贵的照片回忆吧&#xff1f;好在&#xff0c;本文分享了 6 种有效的解决方案&#xff0c;教你如何轻松地将照片从 iPhone 传输到 Android。 第 1 部分&#xff1a;如何通过 iReaShare Phone Transfer 将照片从 iPhon…

AI编程:[体验]存量微服务架构下植入WebSocket的“踩坑”与“填坑”

一、核心需求 功能需求&#xff1a;用户可以通过语音与AI对话&#xff0c;并实现类似ChatGPT的实时交互&#xff08;流式响应&#xff0c;打字机效果&#xff09;技术需求&#xff1a;在现有微服务架构中进行扩展&#xff08;SpringCloud&#xff09; 二、技术盲点 陌生领域 …

uniapp事件onLoad区分大小写

区分大小写。不然会不起作用。onLoad方法中的功能均不会被执行。 除了功能逻辑要检查外。大小写是要认真检查的一部分

《打破微前端困局:样式冲突与资源隔离破局指南》

微前端架构凭借其独特优势&#xff0c;正逐渐成为众多大型项目的首选架构模式。它将一个庞大的前端应用拆解为多个相对独立的子应用&#xff0c;每个子应用可以独立开发、部署和维护&#xff0c;极大地提升了开发效率与团队协作的灵活性。然而&#xff0c;随着微前端架构的广泛…

OpenCV——边缘检测

边缘检测 一、边缘检测二、边缘检测算子2.1、Sobel算子2.2、Scharr算子2.3、Laplacian算子 三、Canny边缘检测3.1、Canny边缘检测的步骤3.2、Canny算法的实现 一、边缘检测 边缘是指图像中像素的灰度值发生剧烈变化的区域&#xff1a; 图像中的边缘主要有以下几种成因&#x…

2506认证资讯|工信部出手整治多品牌充电宝,WMC上海稍逊往年,RED修订Common Charger,WiFi7 FCC测试

01 — 中国 工信部拟制定移动电源强制性国家标准 该标准将从以下方面全面提升移动电源安全性&#xff1a; 1. 拟在GB 31241、GB 4943.1基础上&#xff0c;新增或加严过充电、针刺等试验要求。 2. 拟提出影响电池安全的正负极材料、隔膜等关键材料要求。 3. 拟规范锂离子电池…

Linux Regulator 子系统核心逻辑与关键问题全解析

Linux Regulator 子系统核心逻辑与关键问题全解析 一、什么是 regulator 子系统&#xff1f;核心作用&#xff1f; regulator 子系统是 Linux 内核为板级/SoC 多路可控电源设计的统一电源管理框架。它的主要作用是&#xff1a; 为每一路可控电源&#xff08;Buck、LDO、DCDC …

制造业官网3D应用,让产品会“说话”

在当今数字化时代&#xff0c;装备制造业正经历着前所未有的变革。随着消费升级和国内经济的蓬勃发展&#xff0c;中国社会的经济格局从传统的“工业经济”向多元化的“服务经济”转型。装备制造业作为制造业与服务业融合的核心领域&#xff0c;积极探索全新的“服务化”发展模…

SCAU15--气球狂欢节

15 气球狂欢节 Time Limit:1000MS Memory Limit:65535K 题型: 编程题 语言: G;GCC 描述&#xff1a; 一个充满魔法的国度中&#xff0c;存在一场年度的节日&#xff0c;名为“气球狂欢节”。在这个节日中&#xff0c;有一个传统的比赛&#xff0c;那就是“气球挑战赛”…

python打卡day56@浙大疏锦行

知识点回顾&#xff1a; 假设检验基础知识 原假设与备择假设P值、统计量、显著水平、置信区间 白噪声 白噪声的定义自相关性检验&#xff1a;ACF检验和Ljung-Box 检验偏自相关性检验&#xff1a;PACF检验 平稳性 平稳性的定义单位根ADF检验: 越小越平稳 季节性检验 ACF检验序列…

采集文章+原创AI处理+发布网站详细教程

简数采集器是新一代的网站文章采集和发布平台&#xff0c;完全在线配置和使用云采集&#xff0c;功能强大&#xff0c;操作简单&#xff0c;配置快捷高效。 简数不仅提供网页文章采集、数据批量处理、定时采集、定时定量自动发布等基本功能&#xff0c;还集成强大的SEO工具与接…

Hystrix超时降级机制全解析

Hystrix的超时降级实现主要通过以下核心机制完成&#xff0c;结合配置、注解和Fallback逻辑实现服务容错&#xff1a; 1. 超时触发条件 默认超时时间&#xff1a;Hystrix默认超时阈值为1秒&#xff0c;超过该时间未响应则触发降级。自定义配置&#xff1a;可通过HystrixComman…

6月份最新代发考试战报:思科华为HCIP HCSE 考试通过

6月份最新代发考试战报&#xff1a;思科华为HCIP HCSE 考试通过 H19-423 HCSA-Presales-IP Network 数通考试通过&#xff0c; H12-725 HCIP-Security安全 考试通过&#xff0c;H13-121 HCIP-Kunpeng Application Developer鲲鹏计算 考试通过&#xff0c;CCNP 350-401考试通过…

谈谈我的软考经历

我 2020 年高考进入大学&#xff0c;软件工程专业&#xff0c;去年&#xff08;24年7月&#xff09;毕业开始工作。我实习是在一家云计算公司&#xff0c;公司内部对软考的证书没有什么激励或补助之类的&#xff0c;我也一直认为计算机嘛&#xff0c;“talk is cheap&#xff0…