软件233数据分析和可视化

张开发
2026/4/16 11:40:07 15 分钟阅读

分享文章

软件233数据分析和可视化
一、项目背景 本项目基于班级《软件233学生信息表.xlsx》开展数据分析与可视化实践目标是把原始学生信息转化为可读、可解释、可展示的图形结果。通过对性别、生源省份、生源城市、签名文本、成绩、宿舍、生日等字段进行分析完成“从数据清洗 - 统计分析 - 图形展示 - 结论建议”的完整流程为班级管理、教学反馈、招生建议提供参考。 二、项目目标 1. 读取并清洗学生信息 Excel 数据。 2. 完成以下可视化任务 - 性别饼状图 - 生源省份中国地图 - 生源城市柱状图 - 个性签名词云 - 成绩分布折线图 - 宿舍分布关系图 - 生日散点图 3. 在可视化基础上输出可解释分析结论与建议。 4. 形成可复用脚本后续可扩展为问答 Agent 的数据支撑模块。 三、开发环境与技术栈 1. 开发语言Python 3.x 2. 核心库 - pandas读取/处理 Excel 数据 - matplotlib常规统计图饼图、柱状图、折线图、散点图 - pyecharts省份地图可视化HTML 交互 - wordcloud文本词云 - networkx宿舍关系网络图 3. 开发工具Cursor 本地 Python 环境 4. 数据源软件233学生信息表.xlsx 四、软件制作过程详细 1需求拆解阶段 先把题目要求拆成可执行的小任务 - 数据读取 - 列识别避免列名不完全一致导致报错 - 各图表函数模块化 - 结果文件保存 - 博客文稿自动输出 2数据读取与容错设计 脚本优先读取固定文件名“软件233学生信息表.xlsx”。如果文件缺失则直接给出错误提示避免后续运行到中途才失败。 同时在字段识别时采用“候选列名匹配”策略比如性别列允许是“性别/gender”省份列允许“生源省份/省份/籍贯省份”提升了脚本通用性。 3数据清洗策略 1. 空值处理对关键统计列使用 dropna()。 2. 类型处理成绩列转 float生日列转 datetime。 3. 省份名称标准化 - 统一“北京/北京市”等不同写法 - 统一“内蒙古/内蒙古自治区”等自治区写法 - 规避地图匹配失败导致 NaN 的问题 4可视化实现阶段 每个图都封装成独立函数主函数顺序调用便于维护和排错 1. 性别饼图展示班级男女比例结构。 2. 生源省份地图用地图呈现地域分布视觉上更直观。 3. 城市柱状图对 TopN 城市计数长尾并入“其他”。 4. 签名词云提取高频表达感知班级整体语义倾向。 5. 成绩折线图按成绩排序观察分布走势与分层。 6. 宿舍关系图构建“宿舍-学生”二分图识别组织结构。 7. 生日散点图按月/日分布观察生日集中区间。 5输出与交付阶段 1. 图片输出为 png便于插入文档或答辩 PPT。 2. 地图输出为 html可交互查看缩放、悬浮提示。 3. 额外输出博客文稿 txt减少重复整理工作。 五、设计思想 1. 模块化思想 每个图独立函数做到“一个函数只做一件事”便于后续改图风、替换字段、单独调试。 2. 容错优先思想 真实学生表经常存在“列名不统一、空值、格式混乱”等问题。设计时先考虑“怎么不崩”再考虑“怎么漂亮”。 3. 可解释性优先 本项目不是只追求图多而是强调“图 - 结论 - 建议”的闭环输出确保每张图都能支持实际决策。 4. 可扩展思想 脚本保留了后续扩展空间如情感分析、聚类、多届对比为将来做智能问答 Agent 提前打基础。 六、关键源码说明可写入博客 1. 列名兼容函数col_like 通过候选列名列表做匹配降低对固定表头的依赖提高复用性。 2. 省份规范化函数normalize_province_name 将“省/市/自治区”的不同写法映射到地图可识别名称避免地图显示 NaN 或无着色。 3. 地图提示格式化tooltip label 对空值显示 0避免界面出现 NaN提升交互体验与展示专业度。 4. 可视化函数解耦 plot_gender_pie / plot_province_map / plot_city_bar / signature_wordcloud / score_line / dorm_relation_graph / birthday_scatter 各函数均支持独立执行、独立输出。 七、数据分析结论示例写法可按你班实际替换 1. 生源省份结论 班级生源集中在少数核心省份说明学院在这些省份具备稳定的招生吸引力。 2. 城市来源结论 城市分布呈“头部集中长尾分散”特点省会及地级市均有覆盖生源结构相对多元。 3. 性别结构结论 若比例明显失衡建议在招生宣讲中增加面向弱势性别群体的专业认知引导。 4. 成绩结构结论 通过折线图可观察是否有明显分层。如果中低分段密集可建议增加基础强化与分层辅导。 5. 签名文本观察 签名词云可反映同学关注主题学习、压力、目标、兴趣等。可进一步结合情感分析评估积极/消极倾向。 6. 宿舍与生日观察 宿舍关系图可辅助班级活动组织生日散点可支持月度生日会安排与班级凝聚活动设计。 八、对招生与教学管理的建议 1. 招生建议 - 对生源占比高的省份做精准宣传重点高中、专业特色、就业方向 - 对生源薄弱省份可尝试线上宣讲和校友案例传播 2. 教学建议 - 对成绩分布中的低分群体建立早预警机制 - 对编程基础薄弱同学提供分层任务与答疑时段 - 利用签名/兴趣字段开展项目制分组提升学习投入度 3. 班级管理建议 - 用宿舍关系与兴趣信息组织学习共同体 - 按生日月策划活动提高班级参与感 九、可新增字段建议下一版学生信息表 1. 高中类型普通/重点/职教 2. 编程基础等级零基础/入门/熟练 3. 兴趣方向前端/后端/算法/数据/测试 4. 竞赛与证书情况 5. 未来规划就业/考研/考公/出国 6. 学习困难点自评 7. 心理与压力状态自评匿名化后统计 十、总结 本项目完成了从“班级原始数据”到“图形化表达结论建议”的完整实践。技术上实现了字段容错、名称标准化、图表模块化、结果文件自动化输出应用上为班级画像、教学改进、招生分析提供了直观依据。整个流程也为后续开发“学生常见问题问答 Agent”提供了结构化数据基础与分析模板。 十一、未来展望 1. 增强分析深度 - 引入签名情感分析积极/中性/消极 - 引入聚类分析识别学生群体画像 - 引入相关性分析如基础水平与成绩关系 2. 增强系统能力 - 做成可配置脚本上传任意班级 Excel 自动出图 - 做成 Web 小系统一键导入、可视化看板、自动报告导出 - 做成自然语言问答 Agent输入问题自动返回图表解释 3. 增强数据治理 - 统一字段命名规范 - 增加数据字典与填写校验 - 建立多届数据仓库支持长期趋势分析 软件233 班级学生信息可视化分析脚本 功能 1. 读取《软件233学生信息表.xlsx》 2. 性别饼图 3. 生源省份中国地图需 pyecharts 4. 生源城市柱状图 5. 个性签名词云 6. 成绩分布折线图 7. 宿舍关系图简单网络图 8. 生日散点图 from __future__ import annotations import webbrowser from pathlib import Path from typing import Optional import matplotlib.pyplot as plt import networkx as nx import pandas as pd from matplotlib.font_manager import FontProperties from wordcloud import WordCloud try: from pyecharts.charts import Map from pyecharts import options as opts from pyecharts.globals import CurrentConfig from pyecharts.commons.utils import JsCode except ImportError: Map None opts None CurrentConfig None JsCode None PROJECT_DIR Path(__file__).resolve().parent EXCEL_NAME 软件233学生信息表.xlsx EXCEL_PATH PROJECT_DIR / EXCEL_NAME plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False def load_data(path: Path) - pd.DataFrame: if not path.exists(): raise FileNotFoundError(f未找到学生信息表{path}) df pd.read_excel(path) return df def col_like(df: pd.DataFrame, candidates: list[str]) - Optional[str]: 从候选列名中找到第一个在 df 中出现的列。 for c in candidates: if c in df.columns: return c return None def normalize_province_name(name: str) - Optional[str]: 把省份名称规范成 pyecharts 中国地图可识别的名称。 if name is None: return None s str(name).strip() if not s: return None mapping { 北京: 北京市, 北京市: 北京市, 天津: 天津市, 天津市: 天津市, 上海: 上海市, 上海市: 上海市, 重庆: 重庆市, 重庆市: 重庆市, 内蒙古: 内蒙古自治区, 内蒙古自治区: 内蒙古自治区, 广西: 广西壮族自治区, 广西壮族自治区: 广西壮族自治区, 西藏: 西藏自治区, 西藏自治区: 西藏自治区, 宁夏: 宁夏回族自治区, 宁夏回族自治区: 宁夏回族自治区, 新疆: 新疆维吾尔自治区, 新疆维吾尔自治区: 新疆维吾尔自治区, 香港: 香港特别行政区, 香港特别行政区: 香港特别行政区, 澳门: 澳门特别行政区, 澳门特别行政区: 澳门特别行政区, 黑龙江: 黑龙江省, 黑龙江省: 黑龙江省, 吉林: 吉林省, 吉林省: 吉林省, 辽宁: 辽宁省, 辽宁省: 辽宁省, 河北: 河北省, 河北省: 河北省, 河南: 河南省, 河南省: 河南省, 山东: 山东省, 山东省: 山东省, 山西: 山西省, 山西省: 山西省, 陕西: 陕西省, 陕西省: 陕西省, 甘肃: 甘肃省, 甘肃省: 甘肃省, 青海: 青海省, 青海省: 青海省, 江苏: 江苏省, 江苏省: 江苏省, 浙江: 浙江省, 浙江省: 浙江省, 安徽: 安徽省, 安徽省: 安徽省, 福建: 福建省, 福建省: 福建省, 江西: 江西省, 江西省: 江西省, 湖北: 湖北省, 湖北省: 湖北省, 湖南: 湖南省, 湖南省: 湖南省, 广东: 广东省, 广东省: 广东省, 海南: 海南省, 海南省: 海南省, 四川: 四川省, 四川省: 四川省, 贵州: 贵州省, 贵州省: 贵州省, 云南: 云南省, 云南省: 云南省, 台湾: 台湾省, 台湾省: 台湾省, 河北省 : 河北省, } if s in mapping: return mapping[s] if s in {, 中国, 中华人民共和国}: return None return mapping.get(s, s) def plot_gender_pie(df: pd.DataFrame) - None: col col_like(df, [性别, gender]) if col is None: print(未找到性别列跳过性别饼图。) return counts df[col].value_counts(dropnaTrue) plt.figure(figsize(6, 6)) plt.pie( counts.values, labelscounts.index.astype(str), autopct%1.1f%%, startangle90, ) plt.title(软件233同学性别分布) out PROJECT_DIR / 软件233_性别分布饼图.png plt.savefig(out, dpi200, bbox_inchestight) print(f已保存{out}) def plot_province_map(df: pd.DataFrame) - None: if Map is None: print(未安装 pyecharts跳过省份中国地图可 pip install pyecharts。) return CurrentConfig.ONLINE_HOST https://assets.pyecharts.org/assets/v5/ col col_like(df, [生源省份, 省份, 籍贯省份]) if col is None: print(未找到省份列跳过中国地图。) return raw df[col].dropna().astype(str).map(normalize_province_name).dropna() counts raw.value_counts() data [(p, int(v)) for p, v in counts.items()] if not data: print(省份数据为空或未匹配成功跳过中国地图。) return print(省份地图数据示例前10, data[:10]) m ( Map() .add(人数, data, china) .set_series_opts( label_optsopts.LabelOpts( is_showTrue, formatterJsCode( function(params){ if (params.value undefined || params.value null || isNaN(params.value)) { return ; } return params.name : params.value; } ), font_size9, ) ) .set_global_opts( title_optsopts.TitleOpts(title软件233生源省份分布中国地图), visualmap_optsopts.VisualMapOpts(min_0, max_int(max(counts.values))), tooltip_optsopts.TooltipOpts( formatterJsCode( function(params){ var v params.value; if (v undefined || v null || isNaN(v)) { v 0; } return params.name br/人数 v; } ) ), ) ) out_html PROJECT_DIR / 软件233_生源省份地图.html m.render(str(out_html)) print(f已生成省份中国地图 HTML{out_html}) print(若仍白屏请先联网或更换网络后重试。) try: webbrowser.open(out_html.resolve().as_uri()) print(已在默认浏览器中打开地图。) except Exception as e: print(f自动打开浏览器失败请手动双击该 HTML 文件。{e}) def plot_city_bar(df: pd.DataFrame) - None: col col_like(df, [生源地城市, 城市, 生源城市]) if col is None: print(未找到城市列跳过城市柱状图。) return counts df[col].dropna().value_counts() top_n 15 if len(counts) top_n: top counts.head(top_n) others counts[top_n:].sum() if others 0: counts_plot pd.concat([top, pd.Series({其他: others})]) else: counts_plot top else: counts_plot counts plt.figure(figsize(12, 6)) xs range(len(counts_plot)) plt.bar(xs, counts_plot.values, colorskyblue) plt.xticks(xs, counts_plot.index.astype(str), rotation45, haright) for i, v in enumerate(counts_plot.values): plt.text(i, v max(0.05, v * 0.01), str(int(v)), hacenter, vabottom) plt.xlabel(城市) plt.ylabel(人数) plt.title(软件233生源城市分布TopN 其他) plt.tight_layout() out PROJECT_DIR / 软件233_生源城市柱状图.png plt.savefig(out, dpi200, bbox_inchestight) print(f已保存{out}) def signature_wordcloud(df: pd.DataFrame) - None: col col_like(df, [签名, 个性签名, 自我介绍]) if col is None: print(未找到签名列跳过词云。) return text .join(str(x) for x in df[col].dropna()) if not text.strip(): print(签名文本为空跳过词云。) return font_path None # 如需指定中文字体文件可改为具体路径 wc WordCloud( width800, height400, background_colorwhite, font_pathfont_path, ).generate(text) plt.figure(figsize(10, 5)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.title(软件233同学签名词云) out PROJECT_DIR / 软件233_签名词云.png plt.savefig(out, dpi200, bbox_inchestight) print(f已保存{out}) def score_line(df: pd.DataFrame) - None: col col_like(df, [成绩, 总评成绩, 期末成绩, 综合成绩]) if col is None: print(未找到成绩列跳过成绩分布折线图。) return s df[col].dropna().astype(float).sort_values() plt.figure(figsize(10, 5)) plt.plot(range(1, len(s) 1), s.values, markero, linestyle-) plt.xlabel(学生序号按成绩排序) plt.ylabel(成绩) plt.title(软件233成绩分布折线图) plt.grid(alpha0.3) out PROJECT_DIR / 软件233_成绩分布折线图.png plt.savefig(out, dpi200, bbox_inchestight) print(f已保存{out}) def dorm_relation_graph(df: pd.DataFrame) - None: dorm_col col_like(df, [宿舍, 寝室, 宿舍号]) name_col col_like(df, [姓名, 名字, name]) if dorm_col is None or name_col is None: print(未找到宿舍或姓名列跳过宿舍关系图。) return G nx.Graph() for _, row in df[[dorm_col, name_col]].dropna().iterrows(): dorm str(row[dorm_col]) name str(row[name_col]) G.add_node(name, typestudent) G.add_node(dorm, typedorm) G.add_edge(dorm, name) pos nx.spring_layout(G, seed42) plt.figure(figsize(10, 8)) dorm_nodes [n for n, d in G.nodes(dataTrue) if d.get(type) dorm] stu_nodes [n for n, d in G.nodes(dataTrue) if d.get(type) student] nx.draw_networkx_nodes(G, pos, nodelistdorm_nodes, node_colorlightcoral, node_size800, label宿舍) nx.draw_networkx_nodes(G, pos, nodeliststu_nodes, node_colorskyblue, node_size300, label学生) nx.draw_networkx_edges(G, pos, alpha0.5) nx.draw_networkx_labels(G, pos, font_propertiesFontProperties(fnameNone, size8)) plt.axis(off) plt.title(软件233宿舍分布关系图) plt.legend() out PROJECT_DIR / 软件233_宿舍关系图.png plt.savefig(out, dpi200, bbox_inchestight) print(f已保存{out}) def birthday_scatter(df: pd.DataFrame) - None: col col_like(df, [生日, 出生日期, 出生年月]) if col is None: print(未找到生日列跳过生日散点图。) return series pd.to_datetime(df[col], errorscoerce).dropna() if series.empty: print(生日列无有效日期跳过散点图。) return months series.dt.month days series.dt.day plt.figure(figsize(8, 6)) plt.scatter(months, days, alpha0.7) plt.xlabel(月份) plt.ylabel(日期) plt.title(软件233同学生日散点分布) plt.xticks(range(1, 13)) plt.grid(alpha0.3) out PROJECT_DIR / 软件233_生日散点图.png plt.savefig(out, dpi200, bbox_inchestight) print(f已保存{out}) def main() - None: df load_data(EXCEL_PATH) print(数据列名, df.columns.tolist()) plot_gender_pie(df) plot_province_map(df) plot_city_bar(df) signature_wordcloud(df) score_line(df) dorm_relation_graph(df) birthday_scatter(df) print(全部可视化生成完成。) if __name__ __main__: main()

更多文章