feat: 论文

This commit is contained in:
shuo
2026-04-27 12:00:47 +08:00
parent 304441c888
commit 1393689fe7
15 changed files with 1436 additions and 37 deletions

View File

@@ -2,16 +2,19 @@
## 中文摘要草案
随着企业管理数字化水平的提升,员工缺勤行为分析逐渐成为人力资源管理中的重要研究内容。针对传统缺勤管理方式依赖人工统计、分析效率较低、风险预警能力不足缺乏理论解释支撑等问题,本文设计并实现了一套基于中国企业员工缺勤事件的分析与预测系统。系统围绕缺勤事件数据,构建了数据概览、影响因素分析、`JD-R` 理论分析、缺勤风险预测和员工群体画像五类核心功能,实现了缺勤时长统计分析、关键因素挖掘、理论驱动解释、多模型预测与聚类画像展示
随着企业管理数字化水平的提升,员工缺勤行为分析逐渐成为人力资源管理中的重要研究内容。针对传统缺勤管理方式依赖人工统计、分析效率较低、风险预警能力不足缺乏机制解释的问题,本文围绕中国企业员工缺勤场景,构建了一套集统计分析、理论解释与风险预测于一体的分析框架与系统实现。本文并不将研究目标局限于“预测缺勤时长”本身,而是希望回答三个问题:员工缺勤呈现出什么特征,缺勤受到哪些因素影响,以及这些因素通过何种机制作用于缺勤结果
系统实现过程中,后端采用 Flask 框架构建接口服务,结合 Pandas、Scikit-learn 与 PyTorch 完成数据处理、特征工程、模型训练、理论指标计算与可解释分析;前端采用 Vue 3、Element Plus 与 ECharts 实现交互式可视化界面。针对毕业设计场景,系统构建了一套符合中国企业特征的员工缺勤事件模拟数据集,并在数据层补充工作要求、工作资源、个人资源、工作倦怠和工作投入等 `JD-R` 理论变量,用于解释员工缺勤的心理学驱动机制。同时,系统引入树模型、聚类模型与时序注意力融合深度学习模型,对单次缺勤时长进行预测,并结合 `SHAP` 全局与局部解释技术,提高模型结果的可解释性
研究设计上,本文以 `JD-R`(工作要求-资源)理论为解释主线,将工作要求、工作资源、个人资源、工作倦怠和工作投入等心理学变量映射为可计算字段,并进一步构建工作要求指数、工作资源指数、个人资源指数、`JD-R` 平衡度等复合指标,用于解释员工缺勤背后的深层心理与组织机制。在此基础上,系统先通过数据概览和影响因素分析识别缺勤现象与关键风险因素,再通过 `JD-R` 双路径分析解释高工作要求如何通过倦怠推高缺勤,以及工作资源与个人资源如何通过提升投入缓冲缺勤风险,最后结合机器学习模型对缺勤时长与风险进行预测判断
研究结果表明,系统能够较好地完成缺勤时长预测任务,并通过 `JD-R` 双路径分析和 `SHAP` 特征解释揭示缺勤行为背后的工作负荷、资源支持与个体心理资源作用机制。本文的研究工作为企业缺勤行为分析、风险预警和管理干预提供了可视化、可解释、可扩展的技术方案,也为后续开展员工离职预警、职业健康管理和组织行为分析提供了基础
在系统实现过程中,后端采用 Flask 框架构建接口服务,结合 Pandas、Scikit-learn 与 PyTorch 完成数据处理、特征工程、理论指标计算、预测建模与可解释分析;前端采用 Vue 3、Element Plus 与 ECharts 实现交互式可视化界面。针对毕业设计场景,系统构建了一套符合中国企业特征的员工缺勤事件模拟数据集,并在预测阶段引入树模型、聚类模型与时序注意力融合深度学习模型,在解释阶段引入 `SHAP` 全局与局部分析,并按 `JD-R` 理论维度聚合特征贡献,将模型结果转化为可读的机制说明
研究结果表明,本文构建的系统不仅能够完成缺勤时长预测任务,更能够从 `JD-R` 理论视角解释缺勤行为形成的原因,并将预测结果进一步转化为“压力-资源结构”下的文字化解释与管理建议。本文的研究工作为企业开展员工缺勤分析、风险识别和干预决策提供了可视化、可解释、可扩展的技术方案,也为后续开展职业健康管理、组织行为分析和离职预警研究提供了基础。
## 关键词
- 员工缺勤分析
- `JD-R` 模型
- 缺勤机制解释
- 风险预测
- `SHAP` 可解释分析
- 机器学习
@@ -20,4 +23,4 @@
## 英文摘要标题参考
Employee Absence Analysis and Prediction System with JD-R Theory and Explainable Modeling
Employee Absence Analysis, Explanation and Prediction System with JD-R Theory

View File

@@ -10,19 +10,20 @@
- 1.1 研究背景与意义
- 1.2 国内外研究现状
- 1.3 研究内容
- 1.3 研究问题与研究目标
- 1.4 论文结构安排
### 第2章 相关技术与理论基础
- 2.1 `JD-R` 工作要求-资源模型
- 2.2 Flask 后端框架
- 2.3 Vue 3 前端框架
- 2.4 ECharts 可视化技术
- 2.5 机器学习相关算法
- 2.6 深度学习时序融合算法
- 2.7 `SHAP` 可解释分析方
- 2.8 `K-Means` 聚类方法
- 2.2 缺勤分析、解释与预测的逻辑关系
- 2.3 Flask 后端框架
- 2.4 Vue 3 前端框架
- 2.5 ECharts 可视化技术
- 2.6 机器学习相关算法
- 2.7 深度学习时序融合算
- 2.8 `SHAP` 可解释分析方法
- 2.9 `K-Means` 聚类方法
### 第3章 系统需求分析
@@ -44,21 +45,24 @@
- 5.1 中国企业缺勤模拟数据集实现
- 5.2 特征工程与 `JD-R` 指标构建
- 5.3 数据概览与影响因素分析模块实现
- 5.4 `JD-R` 理论分析模块实现
- 5.5 缺勤预测与 `SHAP` 解释实现
- 5.6 时序融合深度学习模型实现
- 5.7 员工画像模块实现
- 5.8 前端界面实现
- 5.4 `JD-R` 理论解释与双路径分析实现
- 5.5 缺勤预测与风险识别实现
- 5.6 `SHAP` 可解释分析与理论维度聚合实现
- 5.7 文字化解释与管理建议生成思路
- 5.8 时序融合深度学习模型实现
- 5.9 员工画像模块实现
- 5.10 前端界面实现
### 第6章 系统测试与结果分析
- 6.1 测试环境
- 6.2 功能测试
- 6.3 接口测试
- 6.4 传统模型与深度学习模型对比
- 6.5 `JD-R` 理论分析结果讨论
- 6.6 `SHAP` 可解释结果分析
- 6.7 系统展示效果分析
- 6.4 缺勤现象与关键因素结果分析
- 6.5 `JD-R` 理论解释结果讨论
- 6.6 缺勤预测与模型对比分析
- 6.7 `SHAP` 可解释结果与文字化解释分析
- 6.8 系统展示效果分析
### 第7章 总结与展望
@@ -72,8 +76,8 @@
## 章节写作建议
- 第1章强调课题意义、问题来源与研究价值
- 第2章突出“技术基础 + 心理学理论基础”双支撑
- 第3章与第4章体现系统分析与设计能力
- 第5章重点写数据、理论、算法和页面如何落地
- 第6章突出系统已完成的功能效果、模型结果与解释结果
- 第1章强调“分析 + 解释 + 预测”的整体研究目标,而不是把重点缩成单一预测任务
- 第2章突出 `JD-R` 是整篇论文的解释框架,`SHAP` 是模型解释工具,两者角色不同但相互支撑
- 第3章与第4章体现系统分析与设计能力,但论文结构不要完全受页面模块顺序牵引
- 第5章重点写“现象发现 -> 机制解释 -> 风险预测 -> 文字化解释”的实现链条
- 第6章每组结果都要同时回答现象、机制和管理意义,不能只给图表或表格

View File

@@ -13,6 +13,8 @@
- 企业缺勤对生产效率、排班稳定性和管理成本的影响
- 数据驱动管理在人力资源领域的应用趋势
- “预测缺勤”与“解释缺勤”并重的研究价值
- 研究不能停留在几个预测结果或几张图表上,而应形成完整的分析、解释与干预逻辑
- 明确三类研究问题:缺勤现象、缺勤机制、缺勤预测与解释
## 第2章 相关技术与理论基础
@@ -24,6 +26,8 @@
可写内容:
- `JD-R` 模型及其健康损伤路径、激励路径
- `JD-R` 作为缺勤解释框架的适用性
- `SHAP` 作为模型解释工具与 `JD-R` 理论解释的区别与联系
- Flask 的基本特点
- Vue 3 的组件化优势
- Element Plus 和 ECharts 的可视化能力
@@ -45,6 +49,7 @@
- 影响因素分析需求
- `JD-R` 理论解释需求
- 缺勤预测与可解释预测需求
- 文字化解释与管理建议输出需求
- 员工画像需求
- 易用性与可维护性要求
@@ -61,6 +66,8 @@
- 功能模块划分
- 数据集字段设计
- `JD-R` 变量设计与复合指标设计
- 统计分析、理论解释、风险预测之间的业务流程关系
- 个体预测结果如何回到理论维度并转化为说明文字
- 接口交互流程
- 页面原型说明
@@ -76,9 +83,13 @@
- 数据生成与预处理实现
- `JD-R` 理论变量构造实现
- 特征工程实现
- 数据概览与影响因素识别如何为后续理论解释提供输入
- `JD-R` 双路径分析如何解释缺勤形成机制
- 模型训练与保存实现
- 时序融合深度学习训练流程
- `SHAP` 全局/局部解释实现
- `SHAP``JD-R` 维度聚合实现
- 预测结果的文字化解释思路
- 时序融合深度学习训练流程
- 后端接口实现
- 前端页面实现
@@ -93,11 +104,14 @@
- 页面访问测试
- 接口联调测试
- 预测功能测试
- 聚类与分析结果测试
- 模型性能指标分析
- 缺勤现象与影响因素结果分析
- `JD-R` 维度结果分析
- 双路径机制结果分析
- 预测功能测试
- 模型性能指标分析
- `SHAP` 解释结果分析
- 个体预测结果的文字化解释示例分析
- 聚类与分析结果测试
- 传统模型与深度学习模型对比分析
## 第7章 总结与展望
@@ -111,5 +125,7 @@
- 已完成的系统功能
- 理论与系统结合的研究成果
- 本文不仅实现缺勤预测,还构建了基于 `JD-R` 的缺勤解释框架
- 文字化解释和管理建议部分仍可继续深化
- 系统存在的限制
- 后续可扩展方向

View File

@@ -5,6 +5,7 @@
- 企业缺勤管理存在统计分散、分析不及时、预测能力不足的问题
- 单纯做预测不够,还需要解释“为什么员工会缺勤”
- 本课题以 `JD-R` 理论为基础,构建一个可视化、可分析、可预测、可解释的缺勤管理辅助系统
- 论文主线不是几个独立页面,而是“现象发现 -> 机制解释 -> 风险预测 -> 管理建议”
## 2. 课题目标
@@ -12,6 +13,7 @@
- 分析关键影响因素
-`JD-R` 理论解释缺勤形成机制
- 实现缺勤风险预测与解释
- 将预测结果转化为可读的文字化说明
- 构建员工群体画像
## 3. 系统总体设计
@@ -20,6 +22,7 @@
- 前端负责界面与图表
- 后端负责数据处理、模型预测、`JD-R` 分析与聚类分析
- 系统包含 `SHAP` 解释能力
- `JD-R` 负责回答“为什么缺勤”,预测模块负责回答“缺勤到什么程度”
## 4. 核心功能展示顺序
@@ -27,29 +30,35 @@
- 展示总量指标
- 展示月度趋势、星期分布、原因分布、季节分布
- 结论:先说明缺勤现象是什么
### 4.2 影响因素分析
- 展示特征重要性排序
- 解释为什么请假类型、医院证明、加班通勤压力等因素更重要
- 结论:识别出哪些业务因素需要进一步用理论解释
### 4.3 `JD-R` 理论分析
- 展示工作要求、工作资源、个人资源三维度
- 展示工作倦怠与工作投入分布
- 展示健康损伤路径和激励路径
- 说明理论如何支撑“为什么要分析缺勤
- 说明这些因素如何通过倦怠和投入作用于缺勤
- 结论:回答“为什么会缺勤”
### 4.4 缺勤预测
- 输入关键字段
- 展示预测时长、风险等级与模型对比
- 展示 `SHAP` 局部解释
- 结合 `JD-R` 维度说明当前预测背后的压力-资源结构
- 结论:回答“未来会缺勤到什么程度,以及为什么会这样”
### 4.5 员工画像
- 展示群体雷达图
- 展示聚类结果与散点图
- 说明不同群体在要求、资源和缺勤表现上的差异
## 5. 技术实现亮点
@@ -58,10 +67,12 @@
- 引入基于 `Transformer` 的时序注意力融合深度学习模型
-`JD-R` 心理学理论映射为可计算的数据字段与复合指标
- 融合 `SHAP` 全局/局部解释,增强结果可解释性
- 将模型输出进一步组织成面向论文与答辩的机制解释思路
## 6. 项目成果
- 系统可完成统计、分析、理论解释、预测、画像五类任务
- 论文价值不只在“预测一个人缺勤多久”,更在于解释缺勤形成机制并提出干预方向
- 页面可视化效果完整
- 文档、论文材料与代码实现保持一致

View File

@@ -8,18 +8,20 @@
本课题围绕企业员工缺勤管理场景,采用前后端分离的系统设计思路开展研究与实现。前端基于 `Vue 3``Element Plus``ECharts` 构建可视化展示界面,实现缺勤趋势、影响因素、`JD-R` 理论分析、预测结果和员工画像等内容的交互式展示;后端基于 `Flask` 搭建接口服务,负责数据处理、分析计算、模型推理、理论指标组织和聚类结果封装。
理论支撑方面,课题引入 `JD-R`(工作要求-资源)模型,用来解释“为什么员工缺勤”。系统将工作要求、工作资源、个人资源、工作倦怠和工作投入等心理学变量映射为可计算字段,并进一步构建工作要求指数、工作资源指数、个人资源指数、`JD-R` 平衡度等复合指标,用于分析员工缺勤背后的深层机制
研究目标上,课题并不将论文限定为“预测某个员工缺勤多少小时”,而是围绕“缺勤现象是什么、缺勤为何发生、缺勤会发展到什么程度”三个层面展开,形成分析、解释与预测一体化的研究框架。也就是说,本文希望先识别缺勤现象和关键因素,再通过理论解释回答缺勤形成机制,最后通过预测模型回答风险程度,并将模型输出进一步转化为可读的机制说明
在理论支撑方面,课题引入 `JD-R`(工作要求-资源)模型,用来解释“为什么员工会缺勤”。系统将工作要求、工作资源、个人资源、工作倦怠和工作投入等心理学变量映射为可计算字段,并进一步构建工作要求指数、工作资源指数、个人资源指数、`JD-R` 平衡度等复合指标,用于分析员工缺勤背后的深层机制。前面的统计分析和影响因素分析负责识别“哪些因素重要”,`JD-R` 理论模块负责解释“这些因素为什么重要”,后面的预测与 `SHAP` 则负责说明“在具体个体上,这些机制如何转化为风险结果”。
在数据处理方面,首先结合项目内部构建的中国企业员工缺勤事件模拟数据集,使用 `Pandas``NumPy` 完成数据清洗、字段转换、统计分析与特征整理。随后围绕员工属性、岗位信息、班次安排、健康风险、请假原因、通勤压力和加班情况等因素进行特征工程,并补充 `JD-R` 相关变量,形成适用于分析、解释与预测的结构化数据。
在算法研究方面,课题采用传统机器学习、深度学习与可解释分析相结合的技术路线。传统模型依托 `scikit-learn``XGBoost` 完成缺勤时长预测与模型对比分析,并通过特征重要性排序和相关性分析挖掘关键影响因素;深度学习部分基于 `PyTorch` 构建时序注意力融合模型,以 `8` 步历史缺勤事件窗口结合静态属性特征进行建模;可解释分析部分引入 `SHAP`,从全局与局部两个层面对预测结果进行解释,并按 `JD-R` 理论维度进行聚合展示。
在算法研究方面,课题采用传统机器学习、深度学习与可解释分析相结合的技术路线。传统模型依托 `scikit-learn``XGBoost` 完成缺勤时长预测与模型对比分析,并通过特征重要性排序和相关性分析挖掘关键影响因素;深度学习部分基于 `PyTorch` 构建时序注意力融合模型,以 `8` 步历史缺勤事件窗口结合静态属性特征进行建模;可解释分析部分引入 `SHAP`,从全局与局部两个层面对预测结果进行解释,并按 `JD-R` 理论维度进行聚合展示。这样,预测结果不再只是一个孤立数值,而能够被解释为某种“压力-资源结构”下的风险表现。
在员工画像分析方面,课题采用 `K-Means` 聚类方法对员工缺勤行为进行分群,结合散点图、雷达图和群体说明完成群体画像展示,从而辅助企业识别不同类型的缺勤风险群体。最终通过系统集成与前后端联调,实现缺勤数据概览、影响因素分析、`JD-R` 理论解释、单次缺勤预测和员工画像分析五类核心功能。
在员工画像分析方面,课题采用 `K-Means` 聚类方法对员工缺勤行为进行分群,结合散点图、雷达图和群体说明完成群体画像展示,从而辅助企业识别不同类型的缺勤风险群体。最终通过系统集成与前后端联调,实现缺勤数据概览、影响因素分析、`JD-R` 理论解释、单次缺勤预测和员工画像分析五类核心功能,并在论文表达上形成“现象发现 -> 因素识别 -> 理论解释 -> 预测判断 -> 文字化解释 -> 管理建议”的完整链条
## 3. 论文(设计)预期结果
本课题预期完成一个可运行、可展示、可支撑论文撰写的员工缺勤分析与预测系统。系统能够实现缺勤事件统计展示、趋势分析、原因分布分析、关键因素挖掘、`JD-R` 理论解释、缺勤时长预测、风险等级评估、`SHAP` 解释和员工群体画像展示等功能,满足本科毕业设计对系统实现和功能展示的要求。
本课题预期完成一个可运行、可展示、可支撑论文撰写的员工缺勤分析、解释与预测系统。系统能够实现缺勤事件统计展示、趋势分析、原因分布分析、关键因素挖掘、`JD-R` 理论解释、缺勤时长预测、风险等级评估、`SHAP` 解释和员工群体画像展示等功能,满足本科毕业设计对系统实现和功能展示的要求。
在研究结果方面,预期能够形成一套较完整的员工缺勤分析方法流程,包括数据预处理、理论变量构造、特征工程、相关性分析、特征重要性评估、预测建模、可解释分析和聚类画像分析。系统应能够根据输入的关键业务字段输出缺勤时长预测结果、风险等级、多模型对比结果和解释结果,并通过可视化图表直观展示分析结论,为企业人力资源管理提供辅助决策依据。
在研究结果方面,预期能够形成一套较完整的员工缺勤分析方法流程,包括数据预处理、理论变量构造、特征工程、相关性分析、特征重要性评估、理论机制解释、预测建模、可解释分析和聚类画像分析。系统应能够根据输入的关键业务字段输出缺勤时长预测结果、风险等级、多模型对比结果和解释结果,并进一步将结果组织为面向论文和管理场景的文字化说明,为企业人力资源管理提供辅助决策依据。
在论文成果方面,预期形成与项目实现一致的毕业设计文档与论文材料,包括需求分析、系统架构设计、接口设计、数据设计、系统实现、实验分析和总结展望等内容,并能够支撑后续开题、中期检查、论文提交和答辩展示工作。
在论文成果方面,预期形成与项目实现一致的毕业设计文档与论文材料,包括需求分析、系统架构设计、接口设计、数据设计、系统实现、实验分析和总结展望等内容,并能够支撑后续开题、中期检查、论文提交和答辩展示工作。论文最终应明确体现:本文不仅完成了员工缺勤预测,还基于 `JD-R` 理论解释了缺勤形成机制,并为企业管理干预提供了分析依据。

View File

@@ -0,0 +1,421 @@
# 基于JD-R理论的中国企业员工缺勤分析、解释与预测系统设计与实现
## 摘要
随着企业管理数字化水平不断提升,员工缺勤行为分析逐渐成为人力资源管理中的重要研究内容。员工缺勤不仅会影响生产效率、排班稳定性和组织运营成本,还会进一步影响团队协同与服务质量。传统缺勤管理方式往往依赖人工统计和经验判断,难以及时识别高风险缺勤事件,也难以从机制层面解释缺勤行为形成的原因。基于此,本文围绕中国企业员工缺勤管理场景,设计并实现了一套集统计分析、理论解释与风险预测于一体的员工缺勤分析系统。
本文并不将研究目标局限于“预测员工缺勤时长”本身,而是尝试回答三个核心问题:第一,企业员工缺勤现象整体呈现出什么特征;第二,缺勤受到哪些关键因素影响;第三,这些因素通过何种机制作用于缺勤结果,并如何转化为具体个体的风险判断。为此,本文以工作要求-资源模型Job Demands-ResourcesJD-R作为理论主线将工作要求、工作资源、个人资源、工作倦怠和工作投入等心理学变量映射为可计算字段并构建工作要求指数、工作资源指数、个人资源指数、JD-R平衡度、倦怠风险指数和工作投入指数等复合指标用于解释员工缺勤背后的深层机制。
在系统实现方面,后端采用 Flask 构建接口服务,结合 Pandas、Scikit-learn、XGBoost、LightGBM 与 PyTorch 实现数据处理、特征工程、模型训练、理论指标计算和可解释分析;前端采用 Vue 3、Element Plus 与 ECharts 实现交互式可视化界面。系统以中国企业员工缺勤模拟数据集为基础,共覆盖 12000 条缺勤事件、2575 名员工和 180 家企业。系统首先完成缺勤现象概览与关键影响因素识别,其次基于 JD-R 双路径模型解释高工作要求如何通过倦怠推高缺勤风险,以及工作资源和个人资源如何通过提升投入缓冲风险,最后利用多种机器学习模型和时序注意力融合深度学习模型完成缺勤时长预测,并结合 SHAP 全局与局部解释将模型结果转化为可读的机制说明。
研究表明本文构建的系统不仅能够实现员工缺勤时长预测和风险识别还能够从理论层面解释缺勤行为形成的心理与组织机制并进一步将预测结果转化为面向管理场景的文字化解释与干预建议。实验结果显示时序注意力融合网络在测试集上的回归性能最佳R² 达到 0.9397RMSE 为 0.5457MAE 为 0.4322在传统结构化模型中XGBoost 的综合表现最优,且能够较好支持 SHAP 解释和风险分类分析。本文的研究成果可为企业开展员工缺勤分析、风险预警和干预决策提供可视化、可解释和可扩展的技术支持,也为后续职业健康管理、离职预警和组织行为研究提供了参考。
**关键词:** 员工缺勤JD-R理论缺勤机制解释风险预测SHAP可解释分析机器学习
## 第1章 绪论
### 1.1 研究背景与意义
随着企业数字化管理能力的持续提升,人力资源管理正在由经验驱动逐步转向数据驱动。员工作为企业运营的核心主体,其出勤稳定性直接影响排班执行、岗位协同、产线连续性和服务质量。对于制造业、物流运输、零售连锁、医药健康等岗位密集型行业而言,缺勤不仅意味着一次人员缺口,更可能进一步引发临时替班、工作积压、生产延迟和组织成本上升等连锁反应。因此,对员工缺勤开展系统化分析具有明显的现实价值。
然而,传统缺勤管理仍主要依赖人工统计、经验判断和事后汇总。管理者通常通过历史请假记录、主管经验和简单报表对缺勤情况进行观察,这种方式能够描述“已经发生了什么”,却难以及时发现“即将发生什么”。当企业规模扩大、数据维度增加、事件类型复杂化后,仅凭人工方法已难以同时满足时效性、准确性和解释性的要求。尤其是对高风险缺勤事件而言,如果只能在事件发生后被动处理,往往会错失前置干预时机。
在此背景下,企业对缺勤分析系统的需求已经不再停留在简单统计或单点预测层面。一方面,企业希望通过数据概览和因素分析把握缺勤现象的整体特征;另一方面,更希望进一步理解缺勤背后的形成机制,例如高负荷排班、通勤压力、健康风险、家庭负担和组织支持不足如何共同影响员工缺勤行为。换言之,若系统只能预测“某位员工可能缺勤多少小时”,其管理价值仍然有限;只有进一步回答“为什么会缺勤”“属于何种压力-资源结构”“应采取何种干预措施”,预测结果才真正具备应用意义。
基于上述需求本文将员工缺勤研究定位为“分析、解释与预测”的一体化问题而不是孤立的回归预测问题。本文以中国企业员工缺勤场景为对象设计并实现一个集数据概览、影响因素分析、JD-R 理论解释、缺勤预测、可解释分析与员工画像于一体的系统。本文的研究意义主要体现在以下三个方面。
1. 在实践层面,系统能够帮助企业从数据角度识别缺勤现象、定位高风险因素、发现重点群体,并为排班优化、健康管理和组织支持提供依据。
2. 在理论层面,本文将 JD-R 理论引入员工缺勤研究,以工作要求、工作资源和个人资源为核心维度解释缺勤形成机制,提升研究的理论完整性。
3. 在方法层面本文将统计分析、特征工程、机器学习、深度学习、SHAP 可解释分析和文字化机制说明加以整合,形成“现象发现-因素识别-理论解释-预测判断-管理建议”的完整研究链条。
### 1.2 国内外研究现状
现有关于员工缺勤的研究大致可以归纳为三个方向。第一类研究关注缺勤现象本身的统计规律,主要通过描述性统计、相关分析和群体比较揭示缺勤频率、持续时间、季节分布、请假原因和群体差异等特征。第二类研究关注缺勤风险预测,通常利用线性回归、集成学习和分类模型对缺勤时长、缺勤频率或高风险状态进行建模。第三类研究则从组织行为学和职业健康心理学视角出发,尝试解释工作压力、资源支持、职业倦怠、工作投入等因素与缺勤行为之间的机制关系。
从国外研究来看员工缺勤通常被纳入职业健康、组织行为和劳动力管理的综合框架中讨论。大量研究表明高工作要求、夜班安排、工作家庭冲突、长距离通勤和健康问题会显著提高员工的疲劳感和倦怠水平进而提升缺勤风险相对地上级支持、同事支持、工作自主性和个人韧性等资源因素有助于提升工作投入并缓冲风险。近年来随着机器学习技术的广泛应用随机森林、梯度提升树、XGBoost 和 LightGBM 等模型被越来越多地用于缺勤预测,同时 SHAP 等可解释方法也被引入以增强模型透明度。
从国内研究来看,现有成果更多聚焦于考勤制度、排班管理、劳动关系、职业健康管理等应用层面。在系统实现类研究中,部分工作能够完成缺勤统计、图表展示和基础预测,但往往存在两类不足:一是偏重页面展示,缺乏清晰的理论主线;二是偏重模型预测,缺少对缺勤形成机制的解释。尤其在本科毕业设计语境下,若论文仅展示若干图表或预测结果,而不能说明各模块之间的逻辑联系,其研究深度和说服力往往不足。
综合国内外研究现状可以发现,当前仍存在三方面值得改进的问题。其一,统计分析、理论解释和预测建模之间常常彼此割裂,缺少统一的问题链条。其二,现有预测研究虽然能够给出较好的数值结果,但对“为什么模型这样判断”的解释多停留在特征重要性层面,尚未有效上升到理论框架。其三,系统实现与论文表达之间容易出现脱节,即系统功能较多,但论文叙事仍然呈现为功能堆叠,缺乏清晰的研究主线。基于此,本文以 JD-R 理论为主线,对缺勤分析、解释与预测进行贯通式设计,以弥补单纯统计或单纯预测研究的不足。
### 1.3 研究问题与研究目标
围绕中国企业员工缺勤管理场景,本文重点回答以下三个研究问题。
第一,企业员工缺勤现象整体呈现出什么特征。本文需要通过数据概览和统计分析回答缺勤事件在总量、时序、原因、季节和群体差异等方面的分布规律,明确缺勤问题的基本轮廓。
第二,缺勤受到哪些关键因素影响,这些因素又通过何种机制作用于缺勤结果。本文不仅关注加班、通勤、夜班、健康状态、家庭负担、请假类型和医疗证明等业务变量,还进一步借助 JD-R 理论解释这些变量如何通过工作要求、工作资源、个人资源、工作倦怠和工作投入等路径影响缺勤行为。
第三,在已知业务与心理特征条件下,如何预测缺勤时长与风险,并将预测结果转化为可读的文字化解释。也就是说,本文并不满足于输出单一数值结果,而是希望进一步识别个体所处的压力-资源结构、主要驱动因素以及可执行的干预方向。
为回答上述问题,本文设置以下研究目标。
1. 设计并实现一套面向中国企业员工缺勤场景的分析、解释与预测系统。
2. 构建符合中国企业业务背景的员工缺勤模拟数据集,并补充 JD-R 理论相关字段。
3. 建立统一的特征工程体系和 JD-R 复合指标体系,为统计分析、理论解释和预测建模提供共同输入。
4. 实现数据概览、影响因素分析、JD-R 双路径解释、缺勤预测、SHAP 可解释分析和员工画像等核心模块。
5. 将模型结果转换为面向管理场景的文字化机制说明和干预建议,增强系统输出的解释性和可用性。
围绕上述目标,本文开展的主要工作包括:构建 12000 条缺勤事件、2575 名员工和 180 家企业组成的场景化数据集;完成 73 个原始字段与 89 个建模特征的设计;基于 JD-R 理论构建工作要求指数、工作资源指数、个人资源指数、JD-R 平衡度、倦怠风险指数和工作投入指数;在预测层面实现树模型与时序注意力融合深度学习模型的并行建模;在解释层面将 SHAP 结果与 JD-R 维度聚合结合,输出面向论文和管理实践的文字化结论。通过上述工作,本文试图把“做一个预测系统”提升为“构建一个具备理论解释能力的缺勤分析框架”。
### 1.4 论文结构安排
本文共分为七章,各章安排如下。
第一章为绪论,主要介绍研究背景与意义、国内外研究现状、研究问题与研究目标,并说明论文整体结构。
第二章为相关技术与理论基础,重点介绍 JD-R 模型、缺勤分析与预测的逻辑关系,以及 Flask、Vue 3、ECharts、机器学习、深度学习、SHAP 与 K-Means 等相关技术和方法。
第三章为系统需求分析,围绕系统功能需求、非功能需求和业务流程展开说明,明确系统建设目标及应用场景。
第四章为系统总体设计,介绍系统的总体架构、模块划分、数据设计、接口设计和页面组织方式,说明系统如何从结构上支撑“分析—解释—预测”的整体逻辑。
第五章为系统详细设计与实现重点阐述数据集构建、特征工程、JD-R 指标构建、理论分析模块、预测模块、SHAP 解释模块和员工画像模块的实现过程。
第六章为系统测试与结果分析围绕功能测试、接口测试、缺勤现象分析、JD-R 理论结果讨论、模型性能对比和文字化解释结果展开分析,说明系统效果及其管理意义。
第七章为总结与展望,对全文工作进行总结,分析研究中的不足,并提出后续改进方向。
## 第2章 相关技术与理论基础
### 2.1 JD-R 工作要求-资源模型
JD-R 模型由工作要求Job Demands与工作资源Job Resources两个核心概念构成并进一步扩展到个人资源Personal Resources、工作倦怠Burnout和工作投入Work Engagement等变量。该理论认为员工在工作中会同时受到要求和资源两类力量的影响。工作要求越高员工越容易产生疲劳、耗竭和心理负担工作资源越充足员工越容易获得支持、恢复能力和积极投入状态。
在本文中,工作要求主要反映高加班、长通勤、夜班安排、时间压力感知、角色模糊和工作家庭冲突等因素;工作资源主要反映上级支持、同事支持、工作自主性、技能多样性、职业发展机会、参与决策和组织公平感等因素;个人资源则主要包括自我效能感、心理韧性和乐观程度。通过构建这些维度的复合指标,本文能够从理论层面解释员工缺勤行为,而不仅仅是停留在现象层面的统计描述。
### 2.2 缺勤分析、解释与预测的逻辑关系
本文将缺勤研究划分为三个连续层面。首先是缺勤现象分析,即回答“缺勤发生了什么”,主要依赖统计指标和可视化图表。其次是缺勤机制解释,即回答“为什么会缺勤”,主要由 JD-R 理论承担。最后是缺勤风险预测,即回答“未来可能缺勤到什么程度”,并借助 SHAP 与 JD-R 维度聚合回答“为什么模型这样判断”。
这一逻辑关系意味着预测模块并不是孤立存在的它建立在前面的因素识别和理论解释之上。统计分析为理论解释提供输入理论解释为预测结果提供意义SHAP 则把模型内部的贡献关系重新翻译为理论可读的文字说明。由此,本文形成了“现象发现—因素识别—理论解释—预测判断—文字化解释—管理建议”的完整闭环。
### 2.3 Flask 后端框架
Flask 是一种轻量级 Python Web 框架,具有结构简单、扩展灵活、路由清晰等优点,适合本科毕业设计中需要快速构建接口服务的场景。本文后端基于 Flask 搭建 REST 风格接口将统计分析、JD-R 理论分析、预测、聚类和可解释分析能力封装为独立接口,由前端通过 HTTP 请求统一调用。
在本系统中Flask 的应用主要体现在接口层组织与服务层解耦。接口层负责接收参数、返回统一 JSON 结构;服务层负责执行数据分析、模型推理与结果封装;核心算法层则负责数据预处理、特征工程、模型训练和解释分析。这样的分层设计有助于保持系统结构清晰,也便于论文中展示系统架构逻辑。
### 2.4 Vue 3 前端框架
Vue 3 是当前主流的前端框架之一,具有组件化、响应式和易于维护等特点。本文采用 Vue 3 实现数据概览、影响因素分析、JD-R 理论分析、缺勤预测和员工画像等前端页面,并结合 Vue Router 组织多页面路由结构。
Vue 3 的响应式机制使得前端页面能够方便地响应后端接口返回结果,例如在预测页面中动态更新预测时长、风险等级、机制说明和干预建议;在 JD-R 页面中动态切换不同分析标签页;在员工画像页面中根据聚类数量切换图表和表格结果。这些能力有效提升了系统展示效果和交互性。
### 2.5 ECharts 可视化技术
ECharts 是一套功能完善的图表可视化库,支持柱状图、折线图、雷达图、散点图、热力图和环形图等多种形式。本文大量使用 ECharts 完成缺勤概览图表、相关性热力图、JD-R 雷达图、双路径示意、SHAP 条形图和聚类散点图等可视化任务。
在论文和答辩展示中图表不仅用于提升页面表现更承担研究结论表达的功能。例如缺勤分布图用于展示现象相关性图用于识别关键因素JD-R 图表用于解释机制SHAP 图表用于解释模型判断,聚类图用于展现群体差异。因此,可视化技术是本文实现“分析—解释—预测”叙事的重要组成部分。
### 2.6 机器学习相关算法
为实现员工缺勤时长预测本文引入了多种传统机器学习算法包括随机森林、梯度提升树、极端随机树、XGBoost 和 LightGBM 等模型。随机森林具有稳健性强、泛化能力较好的特点梯度提升树适合处理非线性回归问题极端随机树具有更高随机性和较快训练速度XGBoost 与 LightGBM 在集成学习和特征建模方面具有较强优势。
本文采用多模型并行训练与比较的方式,一方面提高预测结果的可靠性,另一方面也为系统提供模型对比展示能力。在应用层面,模型对比不是简单地罗列结果,而是帮助用户理解不同算法在同一输入下的判断差异,并选取更适合展示和解释的候选模型。
### 2.7 深度学习时序融合算法
除传统机器学习模型外,本文还设计并实现了基于时序注意力融合的深度学习模型。该模型以员工历史缺勤事件序列为输入,使用固定长度时间窗口构造序列样本,通过 Transformer 对时序上下文进行编码,并与员工静态属性特征进行门控融合,最终输出缺勤时长回归结果。
相比传统模型,深度学习模型更关注员工缺勤行为的时间连续性和上下文依赖,适合从历史事件序列中学习潜在模式。虽然深度学习模型的解释难度更高,但它为本文提供了更加完整的建模路径,也使论文在方法层面具备一定的扩展深度。
### 2.8 SHAP 可解释分析方法
SHAP 是基于 Shapley 值思想构建的模型可解释方法能够从局部和全局两个层面衡量特征对预测结果的贡献。在本文中SHAP 不仅用于计算全局特征重要性,还用于单次预测的局部解释,并进一步按 JD-R 理论维度聚合特征贡献。
这意味着SHAP 在本文中的作用并不只是“告诉我们哪个变量重要”,而是帮助将模型内部的贡献关系翻译成“工作要求、工作资源、个人资源、事件上下文”等更符合论文叙事的解释结构。借助这一机制,本文实现了理论可解释与模型可解释的统一。
### 2.9 K-Means 聚类方法
为补充群体层面的分析,本文采用 K-Means 聚类算法对员工缺勤行为进行分群。K-Means 的基本思想是通过最小化类内距离将样本划分为若干个相似群体。本文结合年龄、司龄、加班、通勤、BMI 和缺勤水平等变量构建聚类输入,并通过雷达图、散点图和文字说明展示不同群体的结构差异。
聚类分析的意义不在于替代预测,而在于从群体层面识别不同类型的缺勤风险画像。例如,有的群体表现为高加班高通勤型,有的群体表现为成熟稳定低缺勤型,有的群体表现为资源不足或健康关注型。聚类结果可以为后续群体管理和差异化干预提供参考。
## 第3章 系统需求分析
### 3.1 功能需求分析
根据企业缺勤管理场景和毕业设计展示需求,本文系统主要应具备五类核心功能。
第一,数据概览功能。系统应能够展示缺勤总量、员工覆盖数量、平均缺勤时长、高风险事件占比、月度趋势、星期分布、请假原因分布和季节分布等基础统计结果,用于快速把握缺勤现象的整体轮廓。
第二,影响因素分析功能。系统应能够通过特征重要性、相关性热力图和群体对比分析识别影响员工缺勤的关键变量,帮助用户了解哪些业务因素值得重点关注。
第三JD-R 理论分析功能。系统应能够从工作要求、工作资源和个人资源三个维度展开统计分析,展示工作倦怠和工作投入分布,并进一步通过健康损耗路径和激励路径解释缺勤的形成机制。
第四,缺勤预测与解释功能。系统应能够输入核心业务字段,输出缺勤时长预测结果、风险等级、风险概率和置信度,同时提供局部 SHAP 解释、JD-R 快照、机制说明和干预建议。
第五员工画像功能。系统应能够通过聚类分析展示不同员工群体在加班、通勤、BMI、司龄和缺勤水平上的差异用于辅助管理者识别典型风险群体。
### 3.2 非功能需求分析
在非功能层面,系统应满足以下要求。
首先是性能要求。常规统计接口和图表接口应能够较快返回结果,预测接口应在合理时间内完成响应,保证演示和答辩场景下的可用性。其次是易用性要求,页面布局应清晰简洁,交互路径明确,图表表达直观,便于在较短时间内向教师或管理者说明系统价值。再次是可维护性要求,前后端应保持清晰分层,数据生成、训练、服务与展示逻辑彼此独立,方便后续扩展。最后是安全性要求,系统不直接依赖真实企业敏感数据,而是使用符合中国企业场景假设的模拟数据集进行建模和展示。
### 3.3 业务流程分析
本文系统的业务流程可以概括为五个步骤。第一步基于模拟数据集进行数据加载与清洗。第二步构建缺勤分析、JD-R 理论变量和特征工程结果。第三步,利用统计分析识别缺勤现象和关键因素。第四步,基于理论和模型实现机制解释与风险预测。第五步,将预测结果和解释结果通过前端页面直观展示给用户。
这一流程的关键不在于模块数量而在于逻辑顺序。数据概览负责发现现象影响因素分析负责识别重点变量JD-R 模块负责解释机制预测模块负责实现个体风险判断SHAP 模块负责将结果转化为可解释说明,员工画像则进一步补充群体层面的分层观察。
### 3.4 可行性分析
从技术可行性看,本文所使用的 Flask、Vue 3、Element Plus、ECharts、Scikit-learn 和 PyTorch 均为成熟技术,能够支持系统开发和论文研究。从数据可行性看,项目已构建覆盖 12000 条缺勤事件、2575 名员工、180 家企业、7 个行业、73 个字段的模拟数据集,具备较好的支撑能力。从实现可行性看,系统已经形成前后端分离架构,并完成模型训练、接口封装和页面展示。综合来看,本文课题具备良好的实现基础与研究可行性。
## 第4章 系统总体设计
### 4.1 系统总体架构设计
本文系统采用前后端分离架构,由表现层、接口层、业务层和数据与模型层四部分组成。表现层基于 Vue 3 构建页面交互与图表展示;接口层基于 Flask 负责请求转发和统一 JSON 响应业务层负责数据统计、特征分析、JD-R 理论解释、预测和聚类逻辑;数据与模型层负责原始数据、清洗数据、训练产物和解释缓存等内容的组织与存储。
这种架构设计的优点在于职责边界清晰,既便于开发,又有利于论文表达。前端主要负责展示和交互,后端主要负责计算和封装,模型与数据处理链路则相对独立,从而使系统能够同时支持统计分析、预测建模、理论解释和可解释分析。
### 4.2 模块划分设计
前端页面划分为五个核心视图数据概览页、影响因素分析页、JD-R 理论分析页、缺勤预测页和员工画像页。后端接口则对应划分为 overview、analysis、jdr、predict、shap 和 cluster 六类接口模块。服务层包括 `data_service``analysis_service``jdr_service``predict_service``shap_service``cluster_service` 等组件,分别承担不同业务职责。
该模块划分并不是简单地把功能拆散而是围绕论文主线进行组织。统计模块负责“现象发现”理论模块负责“机制解释”预测模块负责“风险判断”SHAP 模块负责“模型说明”,聚类模块负责“群体分层”。这样可以确保系统设计与论文结构保持一致。
### 4.3 数据设计
系统数据集以中国企业员工缺勤事件为粒度,每条记录代表一次员工缺勤事件。数据字段涵盖企业与组织信息、员工基础属性、工作负荷、健康与生活方式、缺勤事件信息以及 JD-R 理论字段五大部分。目标变量为“缺勤时长(小时)”,同时在系统展示层将其映射为低、中、高三档风险等级。
在原始字段基础上系统进一步构建加班通勤压力指数、家庭负担指数、健康风险指数、岗位稳定性指数、节假日风险标记、排班压力标记、缺勤历史强度、生活规律指数和管理负荷指数等派生特征同时构建工作要求指数、工作资源指数、个人资源指数、JD-R 平衡度、倦怠风险指数和工作投入指数等理论复合指标,为后续统计、解释和预测提供统一支持。
### 4.4 接口设计
系统所有接口均采用 REST 风格,统一使用 `/api` 作为前缀,采用 JSON 作为数据交互格式,并保持统一返回结构,即 `code``message``data` 三段式封装。数据概览模块提供统计、趋势、原因和季节等接口影响因素分析模块提供重要性、相关性和群体对比接口JD-R 模块提供维度分布、倦怠投入分析、双路径分析和风险分布接口预测模块提供单次预测、模型对比、模型列表和风险分类接口SHAP 模块提供全局解释、局部解释、依赖关系和交互强度接口;员工画像模块提供聚类结果、雷达画像和散点图接口。
### 4.5 UI 设计
系统前端整体采用适合演示和答辩场景的可视化设计风格。每个页面均包含显著的页头说明、清晰的卡片分区和直观的图表结构。预测页面尤其强调“输入—结果—解释”的一体化展示确保用户能够在同一页面中看到预测值、风险等级、JD-R 快照、机制说明和干预建议。这样的界面设计不仅提升了系统可用性,也强化了论文叙事的一致性。
## 第5章 系统详细设计与实现
### 5.1 中国企业缺勤模拟数据集实现
由于真实企业员工缺勤数据往往具有较强隐私属性,难以在毕业设计场景中直接获取和公开使用,本文采用模拟数据集方案。数据生成过程以中国企业管理场景为背景,覆盖制造业、互联网、零售连锁、物流运输、金融服务、医药健康和建筑工程等行业,并在字段设计时考虑员工属性、岗位安排、健康风险和请假事件特征。
数据集共包含 12000 条缺勤事件记录,覆盖 2575 名员工和 180 家企业。通过合理设计字段分布与变量关系,数据集既具备一定的业务真实性,又能够支持后续统计分析、聚类分析、理论解释和预测建模任务。
### 5.2 特征工程与 JD-R 指标构建
在数据进入建模阶段之前,系统首先对原始字段进行清洗、标准化和编码处理。随后,在 `model_features.py` 中完成派生特征构建。该模块不仅生成业务层面的复合特征,还构建 JD-R 理论相关的复合指标。例如,工作要求指数综合考虑加班、通勤、夜班、时间压力和工作家庭冲突;工作资源指数综合考虑自主性、上级支持、同事支持、技能多样性、职业发展和组织公平感;个人资源指数则主要由自我效能感、心理韧性和乐观程度构成。
这些理论指标是本文系统的关键纽带。一方面,它们支撑 JD-R 页面中的群体解释;另一方面,它们也直接参与预测模块和 SHAP 维度聚合,使理论解释与模型预测共享同一套指标体系。
### 5.3 数据概览与影响因素分析模块实现
数据概览模块通过 `overview` 类接口返回统计结果,主要包括总缺勤事件数、员工覆盖数、平均缺勤时长、高风险事件占比以及月度、星期、原因和季节分布。影响因素分析模块通过加载训练后的树模型,结合特征重要性和相关性分析识别关键变量,并通过分组比较展示不同行业、班次、岗位、婚姻状态和健康状况下的缺勤差异。
在实现逻辑上,数据概览负责建立“缺勤现象是什么”的基础认识;影响因素分析则进一步回答“哪些变量值得关注”。这两部分为后续 JD-R 理论解释提供了事实基础。
### 5.4 JD-R 理论解释与双路径分析实现
`jdr_service.py` 是本文理论解释层的核心。该服务首先读取清洗后的数据并执行特征工程,然后围绕工作要求指数、工作资源指数和个人资源指数计算分布统计结果;接着,对工作倦怠和工作投入进行分布与相关性分析;最后,通过简单中介式相关分析构造健康损耗路径与激励路径。
其中,健康损耗路径强调“工作要求上升—倦怠累积—缺勤增加”的关系,激励路径强调“工作资源提升—工作投入增强—缺勤降低”的关系。通过这一路径设计,系统能够将原本分散的变量组织为具有理论意义的解释结构,从而使论文不再只是若干张统计图,而是形成能够自洽的机制叙事。
### 5.5 缺勤预测与风险识别实现
预测模块由 `predict_service.py` 负责实现。系统首先加载训练后的模型、特征名、筛选后特征、标签编码器和模型性能指标,然后将前端输入的核心字段补齐为完整预测样本,执行与训练阶段一致的特征工程和编码流程,最终调用指定模型完成缺勤时长预测。
系统支持自动选择较优模型,也支持手动选择模型和查看模型对比结果。在输出层,系统不仅返回缺勤时长,还进一步将结果映射为低、中、高三档风险等级,并给出风险概率与置信度。这一设计使预测模块从单纯回归输出升级为更符合管理场景的风险识别工具。
### 5.6 SHAP 可解释分析与理论维度聚合实现
为增强模型结果的可解释性,系统在 `shap_analysis.py` 中构建了基于 TreeExplainer 的解释模块。全局解释用于计算各特征对模型总体预测的平均贡献,并展示特征重要性排序;局部解释则针对单次预测样本,计算各输入特征对本次预测结果的贡献值。
更重要的是,系统进一步将特征贡献按 JD-R 理论维度进行聚合,把离散的特征贡献重新组织为“工作要求”“工作资源”“个人资源”“中介变量”和“事件上下文”等更容易被论文和管理者理解的结构。这种做法使 SHAP 不再只是技术层面的解释工具,而成为连接模型判断与理论叙事的重要桥梁。
### 5.7 文字化解释与管理建议生成实现
为了避免预测模块停留在“预测几小时”的简单输出,本文在预测结果链路中进一步加入 JD-R 快照、机制说明和干预建议生成逻辑。系统会根据工作要求、工作资源、个人资源、平衡度、倦怠风险和工作投入等指标,构建当前个体的 JD-R 快照;随后结合局部 SHAP 贡献判断本次结果更接近健康损耗路径、资源缓冲不足路径还是事件触发型波动;最后根据高加班、高通勤、夜班、健康问题和家庭照护等因素生成面向“减要求、增资源、补个人资源”的建议。
这一功能使预测页面不再只是模型演示页,而成为个体风险识别和机制落地页面。对于论文而言,这部分内容尤其重要,因为它直接回答了“预测模块的意义是什么”这一问题。
### 5.8 时序融合深度学习模型实现
传统机器学习模型能够较好地处理结构化特征,但对员工历史缺勤事件序列的时间依赖挖掘有限。为此,本文在 `deep_learning_model.py` 中实现了基于时序注意力融合的深度学习模型。该模型以 8 步历史缺勤事件窗口为输入,将 15 个事件上下文序列特征与 13 个静态属性特征分别编码,再通过门控融合得到最终预测结果。
虽然在系统展示层,深度学习模型主要作为候选模型之一参与比较,但它在研究层面体现了本文对复杂时序模式的进一步探索,也增强了论文的方法完整性。
### 5.9 员工画像模块实现
员工画像模块采用 K-Means 对员工缺勤行为进行聚类分析并通过群体雷达图、散点图和文字说明展示不同群体的差异特征。聚类输入主要包括年龄、司龄、加班、通勤、BMI 和缺勤水平。为增强结果可读性系统对聚类结果进行了业务化命名避免使用“群体1”“群体2”这类无意义标签。
通过员工画像,系统能够从群体层面补充对缺勤风险的理解。例如,一些群体可能表现为高加班高通勤型,另一些群体则表现为成熟稳定低缺勤型。这为企业开展差异化管理提供了更具结构性的参考。
### 5.10 前端页面实现
前端基于 Vue 3 与 Element Plus 实现页面交互和卡片布局,基于 ECharts 完成图表渲染。各页面均采用统一风格的页头说明、数据卡片与图表区域,以便在答辩场景中快速展示模块功能和研究逻辑。
其中预测页面在最近的实现中进一步强化了“预测值—JD-R 快照—机制说明—干预建议”的结构,使其与 JD-R 理论页形成“群体解释 + 个体解释”的互补关系。这样的页面组织方式更符合本文整体叙事主线。
## 第6章 系统测试与结果分析
### 6.1 测试环境
为验证系统的可用性、稳定性与结果有效性,本文在本地完成前后端联调、模型调用、接口响应和图表展示测试。系统软硬件环境如表 6-1 所示。
| 类别 | 环境配置 |
| --- | --- |
| 操作系统 | Windows 11 |
| 后端运行环境 | Python 3.11、Flask 2.3.3 |
| 前端运行环境 | Node.js、Vue 3、Vite、Element Plus |
| 核心算法库 | Scikit-learn、XGBoost、LightGBM、PyTorch |
| 可视化组件 | ECharts |
| 数据集 | 中国企业员工缺勤模拟数据集 |
训练数据集共包含 12000 条缺勤事件记录,覆盖 2575 名员工和 180 家企业。训练阶段采用 9600 条样本作为训练集、2400 条样本作为测试集;传统机器学习模型在 89 个建模特征基础上进行筛选,最终保留 22 个核心特征;深度学习模型采用长度为 8 的时间窗口构建时序样本。
### 6.2 功能测试
功能测试重点检查系统各页面、图表与交互模块是否能够正常加载、切换和展示。测试结果如表 6-2 所示。
| 模块 | 主要测试内容 | 测试结果 |
| --- | --- | --- |
| 数据概览 | 指标卡片、月度趋势、星期分布、原因分布、季节分布 | 通过 |
| 影响因素分析 | 特征重要性、相关性热力图、群体对比切换 | 通过 |
| JD-R 理论分析 | 三维度分布、倦怠与投入、双路径分析、风险分布、贡献解释切换 | 通过 |
| 缺勤预测 | 单次预测、风险等级、模型切换、模型对比、JD-R 解释层展示 | 通过 |
| 员工画像 | 聚类数量切换、雷达图、散点图、群体说明一致性 | 通过 |
从测试结果看,系统各主要功能均能够稳定运行。尤其是在预测页中,系统已经不再只展示预测时长和风险等级,而是进一步展示 JD-R 快照、机制说明和干预建议;在员工画像页中,聚类数量切换后雷达图、散点图和表格名称能够保持一致,满足答辩展示和结果分析需求。
### 6.3 接口测试
系统接口统一采用 `/api` 前缀和 JSON 返回格式,前后端通过 Axios 调用接口并完成数据渲染。主要接口测试情况如表 6-3 所示。
| 接口类别 | 代表接口 | 主要返回内容 | 测试结果 |
| --- | --- | --- | --- |
| 概览接口 | `/api/overview/stats``/api/overview/trend` | 总量统计、趋势与结构分布 | 通过 |
| 分析接口 | `/api/analysis/importance``/api/analysis/correlation` | 特征重要性、相关性和群体对比 | 通过 |
| JD-R 接口 | `/api/jdr/dimensions``/api/jdr/path-analysis` | 三维度分布、双路径分析、风险分布 | 通过 |
| 预测接口 | `/api/predict/single``/api/predict/compare` | 单次预测、模型对比、风险等级 | 通过 |
| SHAP 接口 | `/api/shap/global``/api/shap/local` | 全局解释、局部解释、交互强度 | 通过 |
| 聚类接口 | `/api/cluster/result``/api/cluster/profile``/api/cluster/scatter` | 聚类结果、画像数据、散点数据 | 通过 |
测试结果表明,系统接口结构清晰、边界明确,能够稳定支持前端展示逻辑。对于预测模型对比和聚类数量切换这类存在多请求并发的场景,系统通过前端状态控制和后端独立分析实例避免了结果串页和旧响应覆盖新状态的问题,保证了页面展示的一致性。
### 6.4 缺勤现象与关键因素结果分析
从数据概览结果看,样本共包含 12000 条缺勤事件,平均缺勤时长为 5.8579 小时,中位数为 5.7 小时,高风险缺勤事件占比为 17.35%。这说明样本中的缺勤事件整体以中等时长为主,但仍存在一定比例的长时缺勤风险,需要通过预测和干预模块进行重点识别。
从结构分布看,请假原因并非均匀分散,而是具有明显的集中性。出现频次最高的三类原因分别为职业疲劳 2967 次、身体不适 2806 次和家庭事务 2248 次。这一结果表明,员工缺勤既受到工作负荷和职业耗竭影响,也受到健康因素和家庭事务因素的共同作用。星期分布和月份分布总体较为平稳,但星期一和星期二的缺勤事件数量相对较高,说明周初工作压力、节奏切换和请假安排可能对缺勤行为存在一定影响。
影响因素分析与 SHAP 全局解释进一步揭示了关键变量的重要性。以当前效果较好的树模型解释结果为例,医院证明、临时请假、加班通勤压力指数、连续缺勤、节假日风险标记和健康风险指数均表现出较高重要性。其中,医院证明的重要性达到 0.1316,临时请假的重要性为 0.0659,加班通勤压力指数的重要性为 0.0641,连续缺勤的重要性为 0.0588,说明单次缺勤事件的直接上下文对缺勤时长判断具有较强影响;与此同时,加班通勤压力、健康风险和工作要求指数等变量也表现出稳定贡献,说明缺勤并不只是单一事件触发的结果,而是即时事件因素与长期压力积累共同作用的结果。
这一分析结果为后续 JD-R 理论解释提供了依据。换言之,前面的统计和特征分析主要回答“缺勤受哪些因素影响”,而下一节的 JD-R 结果讨论则进一步回答“这些因素为什么会影响缺勤”。
### 6.5 JD-R 理论解释结果讨论
JD-R 分析结果表明,工作要求、工作资源和个人资源并非孤立存在,而是共同塑造员工的缺勤风险。页面中的倦怠与投入分布图显示,工作倦怠主要集中在中等偏上区间,而工作投入较多集中在较高分区间,这意味着样本中的员工并非处于完全消极状态,而更接近一种“带压投入”的心理状态,即员工在承担一定工作压力的同时,仍保持较强的工作参与感和责任感。
关键相关性结果进一步支持了 JD-R 理论在本文场景中的适用性。图表结果显示,工作要求与工作倦怠之间呈明显正向关系,说明工作要求越高,员工越容易产生心理耗竭;工作资源与工作投入之间呈明显正向关系,说明资源支持能够增强员工的积极参与状态;工作倦怠与缺勤时长呈正向关系,而工作投入和资源维度对缺勤则具有一定缓冲作用。由此可以看出,缺勤行为并非简单由“请假事件”决定,而是工作压力、资源支持和心理状态共同作用的结果。
从理论机制角度看,本文的结果验证了 JD-R 的两条核心路径。一条是健康损耗路径,即高工作要求通过提升倦怠水平进而增加缺勤风险;另一条是激励路径,即工作资源和个人资源通过提升投入、增强恢复能力而降低缺勤倾向。将这一结果与上一节的关键因素分析结合起来可以发现,加班、通勤、夜班和健康风险等变量之所以重要,并不只是因为它们在模型中有较大权重,更因为它们能够在 JD-R 框架下被解释为“压力累积”或“资源不足”的具体表现。
因此JD-R 模块在本文中的作用并不是重复前面的统计分析,而是将前面识别出的关键变量组织为一个可解释的理论框架,从而使论文从“看到了什么因素重要”进一步上升到“这些因素通过何种机制作用于缺勤”的层面。
### 6.6 缺勤预测与模型对比分析
在缺勤预测方面,本文采用多模型并行训练和比较的方式。传统模型包括随机森林、梯度提升树、极端随机树和 XGBoost深度学习部分则采用时序注意力融合网络。模型训练基于 12000 条缺勤事件样本展开,并在特征工程后形成 89 个建模特征,再经过特征选择保留 22 个核心特征用于传统模型训练。深度学习模型在此基础上额外引入长度为 8 的历史事件序列窗口,以增强对时间依赖关系的刻画。
各模型在测试集上的回归性能如表 6-4 所示。
| 模型 | R² | RMSE | MAE |
| --- | --- | --- | --- |
| 随机森林 | 0.7572 | 1.0955 | 0.8096 |
| 梯度提升树 | 0.7694 | 1.0676 | 0.7911 |
| 极端随机树 | 0.7547 | 1.1010 | 0.8147 |
| XGBoost | 0.7717 | 1.0623 | 0.7842 |
| 时序注意力融合网络 | 0.9397 | 0.5457 | 0.4322 |
从表 6-4 可以看出时序注意力融合网络在回归预测上取得了最佳效果说明引入历史缺勤序列和时序上下文后模型能够更充分地刻画员工缺勤行为的时间依赖特征。对于结构化树模型而言XGBoost 在传统模型中表现最佳,其 R² 达到 0.7717RMSE 为 1.0623MAE 为 0.7842,略优于随机森林和梯度提升树。这说明在当前结构化特征场景下,梯度提升类模型对缺勤时长的拟合能力较强。
除回归结果外,系统还对缺勤风险等级进行了分类评估。主要分类模型结果如表 6-5 所示。
| 模型 | 准确率 | 宏平均 F1 |
| --- | --- | --- |
| 随机森林 | 0.7688 | 0.7232 |
| 梯度提升树 | 0.7688 | 0.7267 |
| XGBoost | 0.7712 | 0.7323 |
分类结果表明,各模型在低、中、高风险三级分类任务上均具有较稳定表现,其中 XGBoost 的分类效果略优。这一结果与回归任务中的表现基本一致,说明模型不仅能够预测缺勤时长,也能够较稳定地识别风险等级。页面中的模型对比功能正是基于这一点展开,其意义并不在于简单罗列不同模型的预测值,而在于帮助使用者理解不同算法在同一输入下的判断差异,并选择更适合解释或展示的模型。
需要指出的是,本文在系统实现上采用了“最佳预测模型 + 可解释树模型”并行保留的思路。也就是说,深度学习模型负责提升总体预测精度,而树模型则承担 SHAP 解释与机制转译任务,从而在预测效果与解释能力之间取得平衡。
### 6.7 SHAP 可解释结果与文字化解释分析
SHAP 全局解释结果表明,医院证明、临时请假、加班通勤压力指数、连续缺勤、节假日风险标记和健康风险指数等特征对预测结果具有较强影响。进一步从维度聚合结果看,当前单次缺勤时长预测在直接信号上更容易受到“事件上下文”影响,例如是否提供医院证明、是否临时请假、是否连续缺勤和请假原因类别等,这与本文的预测目标是“单次缺勤事件时长”而非“长期组织绩效”相一致。换言之,事件层面的直接触发因素在短期预测中天然具有较强解释力。
但这并不意味着 JD-R 维度在预测中没有价值。相反,工作要求、健康风险和 JD-R 平衡度等变量虽然在全局重要性中不如事件上下文那样靠前,却为解释“为什么这一事件会演化为较高风险缺勤”提供了结构性依据。比如,当局部 SHAP 结果显示加班通勤压力指数、夜班安排和健康风险共同推高缺勤时长时,系统可以进一步将其解释为“高工作要求通过健康损耗路径推高了本次缺勤风险”;当支持性资源和个人资源对结果产生缓冲作用时,则可以解释为“资源和投入在一定程度上抵消了压力影响”。
基于这一思路,系统在预测结果页中进一步引入了文字化解释层,输出内容包括 JD-R 快照、机制总结和干预建议三部分。其中JD-R 快照用于概括当前个体所处的工作要求、工作资源、个人资源和 JD-R 平衡状态;机制总结用于说明本次预测更接近健康损耗路径还是资源缓冲不足路径;干预建议则从“减要求、增资源、补个人资源”三个方向给出管理提示。其典型表达方式可以概括为:本次预测结果为中高风险,主要由高加班、长通勤、夜班安排和健康风险共同驱动,当前缺勤形成机制更接近健康损耗路径,因此建议优先调整排班压力、增加组织支持并关注员工健康恢复。
这一结果说明,本文的预测模块并不是孤立地“预测一个数”,而是进一步把模型结果转化为可理解、可沟通、可干预的管理语言,实现了理论可解释性与模型可解释性的结合。
### 6.8 系统展示效果分析
从系统整体展示效果来看前端页面已经形成较为清晰的研究链条数据概览负责呈现缺勤现象影响因素分析负责识别关键变量JD-R 页面负责解释缺勤形成机制,预测页面负责给出个体风险判断和文字化解释,员工画像页面负责补充群体切片视角。这样一来,系统不再是若干页面的简单并列,而是围绕“现象发现-机制解释-风险预测-群体分层”逐步展开。
尤其值得强调的是JD-R 页面与预测页面之间已经形成了“群体解释-个体解释”的结构衔接。前者说明总体规律,回答“为什么这类员工更容易缺勤”;后者落到单个样本,回答“这次为什么会预测为较高或较低风险”。这种组织方式使论文的核心价值不再只是展示几张图表,而是通过系统页面把统计分析、理论框架、模型结果和管理建议连接起来。
总体而言,本文构建的系统较好地完成了毕业设计所要求的“系统设计与实现”任务,同时也在论文表达上形成了较完整的研究闭环。与单纯的数据看板或单纯的预测演示相比,本文的系统更能够体现“分析、解释与预测”一体化研究的价值。
## 第7章 总结与展望
### 7.1 研究总结
本文围绕中国企业员工缺勤管理场景,设计并实现了一套集统计分析、理论解释与风险预测于一体的员工缺勤分析系统。本文的主要工作包括:构建包含 12000 条缺勤事件、2575 名员工和 180 家企业的场景化数据集;设计 73 个原始字段与 89 个建模特征,并构建 JD-R 复合指标体系实现数据概览、影响因素分析、JD-R 双路径解释、缺勤预测、SHAP 可解释分析和员工画像等功能模块;将预测结果进一步转化为面向管理场景的文字化机制说明和干预建议。
从实验结果看,本文提出的系统在预测与解释两个层面均取得了较好效果。预测层面,时序注意力融合网络在测试集上达到 0.9397 的 R² 和 0.5457 的 RMSE表现出较强的时序建模能力XGBoost 在传统结构化模型中表现最佳,不仅在回归任务中取得 0.7717 的 R²也在风险分类任务中达到 0.7712 的准确率和 0.7323 的宏平均 F1为后续可解释分析提供了较稳健的基础。解释层面本文通过 JD-R 理论将缺勤形成机制组织为“健康损耗路径”和“激励路径”两条主线,并进一步借助 SHAP 将模型结果翻译为面向管理场景的文字化说明。
与单纯预测系统相比,本文更强调“分析 + 解释 + 预测”的整体主线。数据概览和影响因素分析帮助识别缺勤现象与关键因素JD-R 理论模块解释缺勤形成机制预测模块回答个体风险程度SHAP 则进一步增强模型结果的可解释性。通过这些模块的联动,本文构建了一个更完整、更有说服力的研究框架,也使论文不再停留于页面功能罗列,而是形成了较清晰的研究问题链条和管理应用价值。
### 7.2 存在的不足
尽管本文已经完成较完整的系统实现与论文设计但仍存在一些不足。首先本文使用的是模拟数据集虽然尽量贴近中国企业场景但与真实企业数据相比仍存在差异。其次JD-R 理论变量的构建采用了工程化映射方式,尚未结合真实问卷或长期跟踪数据。再次,深度学习模型的性能和解释性仍有进一步优化空间。最后,当前文字化解释和管理建议仍以规则生成和理论映射为主,尚未形成更细粒度的智能干预推理体系。
### 7.3 后续展望
后续研究可从以下几个方向展开。第一,引入真实企业脱敏数据,对系统进行外部验证和适配。第二,结合更细致的问卷数据或员工画像数据,增强 JD-R 理论变量的真实性。第三,进一步优化时序深度学习模型和多模型融合策略,提高预测稳定性。第四,将文字化解释与管理建议拓展为更完整的智能决策辅助模块,例如加入情景模拟、干预前后对比和持续跟踪机制。第五,将本文研究从缺勤场景扩展到职业健康管理、离职预警和组织行为分析等更广泛的人力资源管理场景中。
## 参考文献
[1] Demerouti E, Bakker A B, Nachreiner F, et al. The job demands-resources model of burnout[J]. Journal of Applied Psychology, 2001, 86(3): 499-512.
[2] Bakker A B, Demerouti E. The Job Demands-Resources model: state of the art[J]. Journal of Managerial Psychology, 2007, 22(3): 309-328.
[3] Xanthopoulou D, Bakker A B, Demerouti E, et al. The role of personal resources in the job demands-resources model[J]. International Journal of Stress Management, 2007, 14(2): 121-141.
[4] Harrison D A, Martocchio J J. Time for absenteeism: A 20-year review of origins, offshoots, and outcomes[J]. Journal of Management, 1998, 24(3): 305-350.
[5] Johns G. Presenteeism in the workplace: A review and research agenda[J]. Journal of Organizational Behavior, 2009, 31(4): 519-542.
[6] Martiniano A, Ferreira R P, Sassi R J, et al. Application of a neuro fuzzy network in prediction of absenteeism at work[C]//7th Iberian Conference on Information Systems and Technologies. 2012: 1-4.
[7] UCI Machine Learning Repository. Absenteeism at work[DB/OL]. https://archive-beta.ics.uci.edu/dataset/445/absenteeism%2Bat%2Bwork, 2026-04-21.
[8] Breiman L. Random forests[J]. Machine Learning, 2001, 45(1): 5-32.
[9] Chen T, Guestrin C. XGBoost: A scalable tree boosting system[C]//Proceedings of the 22nd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining. 2016: 785-794.
[10] Ke G, Meng Q, Finley T, et al. LightGBM: A highly efficient gradient boosting decision tree[C]//Advances in Neural Information Processing Systems 30. 2017: 3146-3154.
[11] Lundberg S M, Lee S-I. A unified approach to interpreting model predictions[OL]. arXiv:1705.07874, 2017.
[12] Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[C]//Advances in Neural Information Processing Systems 30. 2017: 5998-6008.
[13] Lloyd S. Least squares quantization in PCM[J]. IEEE Transactions on Information Theory, 1982, 28(2): 129-137.
## 后续补强建议
当前这份正文初稿已经具备完整章节结构和主要论述逻辑,后续重点可从以下三个方向继续打磨:
1. 补充最终实验截图、模型指标表和接口测试表,使第六章更具论文完整性。
2. 按学校模板进一步整理英文摘要、致谢和图表编号格式。
3. 结合答辩口径,对第一章和第六章中的结论句进行压缩和强化,使其更适合现场表达。

View File

@@ -0,0 +1,272 @@
# 答辩常见问题问答
本文档用于毕业设计答辩准备。回答时建议不要死记硬背整段文字,而是抓住每个问题中的关键词,用自己的语言围绕“研究背景、理论支撑、系统实现、模型训练、结果解释、局限改进”展开。
## 一、选题与研究意义
### 1. 你为什么选择员工缺勤分析这个题目?
我选择这个题目主要是因为员工缺勤是企业人力资源管理中比较常见的问题。传统方式通常停留在考勤统计和事后汇总,难以及时发现高风险群体,也缺少对缺勤原因的解释。这个课题希望通过数据分析、机器学习预测和可解释分析,把缺勤管理从“事后统计”推进到“风险识别和原因解释”。
### 2. 你的系统解决了什么实际问题?
系统主要解决三个问题:第一,帮助企业了解缺勤总体情况,比如趋势、星期分布、请假类型和原因分布;第二,分析哪些因素对缺勤时长和风险影响较大;第三,通过预测模型和解释模块,给出单次缺勤时长、风险等级以及影响原因,为人力资源管理提供辅助决策依据。
### 3. 你的论文创新点在哪里?
创新点主要有三个方面。第一,不只是做缺勤时长预测,而是把“统计分析、理论解释、风险预测、管理建议”串成完整流程。第二,引入 JD-R 工作要求-资源理论,将工作要求、工作资源、个人资源、工作倦怠和工作投入映射为可计算指标。第三,系统同时结合传统机器学习、时序深度学习和 SHAP 可解释分析,使预测结果能够被解释为具体的压力、资源和个人因素贡献。
### 4. 为什么不用简单的考勤统计系统,而要做预测和解释?
简单统计只能回答“发生了什么”,比如某个月缺勤多、某类请假多,但不能回答“为什么会这样”以及“未来风险有多高”。本系统通过模型预测和 SHAP 解释,可以进一步识别个体或群体的风险来源,例如通勤压力、加班强度、健康风险或工作资源不足,从而支持更有针对性的管理干预。
### 5. 你的系统适合哪些使用对象?
主要面向企业 HR、人力资源管理人员、部门主管和组织管理研究人员。HR 可以查看缺勤趋势和风险预测,主管可以了解团队缺勤风险来源,研究人员可以借助 JD-R 理论模块分析工作要求、资源和缺勤之间的关系。
## 二、理论基础与业务逻辑
### 6. 什么是 JD-R 理论?为什么选它?
JD-R 是 Job Demands-Resources即工作要求-资源模型。它认为员工状态受到工作要求和工作资源共同影响:工作要求过高会增加压力和倦怠,工作资源充足可以提升工作投入并缓冲风险。缺勤行为本身与工作压力、健康状态、组织支持等因素有关,所以 JD-R 理论适合用来解释员工缺勤的形成机制。
### 7. 你在系统里如何落地 JD-R 理论?
系统将 JD-R 理论变量转成数据字段和复合指标。工作要求包括加班、通勤、夜班、时间压力、角色模糊和工作家庭冲突等工作资源包括上级支持、同事支持、职业发展机会、组织公平感等个人资源包括自我效能感、心理韧性和乐观程度。然后进一步计算工作要求指数、工作资源指数、个人资源指数、JD-R 平衡度、倦怠风险指数和工作投入指数。
### 8. JD-R 模块和预测模块是什么关系?
预测模块回答“缺勤时长和风险等级是多少”JD-R 模块回答“为什么可能出现这种风险”。也就是说预测模型给出数值结果JD-R 理论帮助解释这个结果背后的机制。两者结合后,系统不只是输出预测值,还能输出压力-资源结构下的原因说明和建议。
### 9. 为什么缺勤可以用工作要求和工作资源解释?
从组织行为角度看,员工缺勤不仅受请假类型影响,也与身体健康、工作压力、岗位安排、家庭负担和组织支持有关。工作要求过高时,员工更容易出现疲劳、倦怠和健康问题;工作资源不足时,员工缺少缓冲压力的条件。这些因素最终可能表现为更高的缺勤时长或缺勤风险。
### 10. 你如何避免论文只是堆砌理论?
我不是只在论文里引用 JD-R 理论,而是在系统代码和功能里进行了落地。具体表现为:数据集中设置了 JD-R 相关变量,特征工程中计算复合指标,前端有 JD-R 理论分析页面,预测结果中也结合 JD-R 维度解释风险来源。因此理论和系统实现是对应的。
## 三、数据集与数据处理
### 11. 你的数据来源是什么?
本项目使用的是面向毕业设计场景构建的中国企业员工缺勤事件模拟数据集,文件为 `backend/data/raw/china_enterprise_absence_events.csv`。数据围绕企业、员工、岗位、健康、通勤、请假事件和 JD-R 变量构建,便于完成系统开发、模型训练和论文实验展示。
### 12. 为什么使用模拟数据?这会不会影响可信度?
由于真实企业考勤和健康数据涉及隐私,获取难度较高,所以本课题采用结构化模拟数据完成系统验证。模拟数据的作用是验证系统流程、算法实现和分析框架是否可行。它确实不能完全替代真实企业数据,因此在论文不足中也说明,后续如果接入真实脱敏数据,可以进一步验证模型的泛化能力和实际应用价值。
### 13. 数据规模是多少?
当前数据集包含约 12000 条缺勤事件,覆盖 2575 名员工、180 家企业,原始字段约 73 个。训练时按 80% 和 20% 划分训练集与测试集,即训练样本约 9600 条,测试样本约 2400 条。
### 14. 目标变量是什么?
目标变量是 `缺勤时长(小时)`。回归模型预测的是具体缺勤小时数,风险分类模型再根据小时数划分低、中、高风险,例如低风险小于 4 小时,中风险约 4 到 8 小时,高风险大于 8 小时。
### 15. 你做了哪些数据预处理?
主要包括去重、缺失值处理、字段标准化、异常值裁剪、类别变量编码、数值特征缩放和特征工程。传统模型使用 `RobustScaler` 做标准化,并用 `SelectKBest` 选择核心特征。深度学习模型则单独构造序列窗口,并保存类别映射和标准化参数,保证训练和预测一致。
### 16. 你做了哪些特征工程?
特征工程包括业务特征和理论特征两类。业务特征如加班通勤压力指数、家庭负担指数、健康风险指数、岗位稳定性指数、缺勤历史强度等。理论特征包括工作要求指数、工作资源指数、个人资源指数、JD-R 平衡度、倦怠风险指数和工作投入指数。这些特征用于增强模型对缺勤机制的表达能力。
### 17. 为什么要做异常值处理?
缺勤时长、通勤时长、加班时长等字段可能存在极端值。如果不处理,模型容易被极端样本影响,导致整体预测不稳定。项目采用分位数裁剪的方式,将数值特征限制在合理范围内,既保留数据分布,又减少异常值干扰。
## 四、模型训练与算法选择
### 18. 你的系统训练了哪些模型?
系统训练了多种回归模型包括随机森林、梯度提升树、极端随机树、XGBoost以及一个基于 PyTorch 的时序注意力融合深度学习模型。除此之外,还训练了风险分类模型,用于输出低、中、高风险等级。
### 19. 为什么要训练多个模型?
多个模型可以进行效果对比,也能提高系统展示和实验分析的完整性。传统树模型适合结构化特征,稳定性较好,也便于做特征重要性和 SHAP 解释;深度学习模型适合利用员工历史缺勤事件的时序信息。通过对比可以说明不同模型在本任务中的表现差异。
### 20. 传统机器学习模型的训练流程是什么?
首先读取并清洗数据,然后按目标变量分层切分训练集和测试集;接着进行异常值裁剪、特征工程、类别编码、标准化和特征选择;然后使用 `RandomizedSearchCV` 对模型进行参数搜索;最后在测试集上计算 R2、MSE、RMSE 和 MAE并保存模型和训练元数据。
### 21. 你的深度学习模型是什么结构?
项目里接口名称叫 `lstm_mlp`,但实际实现是“时序注意力融合网络”,内部使用 Transformer 编码 8 步历史缺勤事件,再结合静态特征网络进行门控融合,最后输出缺勤时长预测值。它不是传统 LSTM而是基于 Transformer 的时序融合回归模型。
### 22. 深度学习模型为什么使用 8 步历史窗口?
缺勤行为具有一定历史依赖比如近期缺勤次数、连续缺勤、请假类型和加班通勤压力会影响下一次缺勤风险。8 步窗口可以保留最近一段缺勤事件信息,同时又不会让序列过长导致训练复杂度过高。对于毕业设计数据规模来说,这是一个兼顾信息量和训练效率的设置。
### 23. 深度学习模型使用了哪些输入特征?
深度模型把特征分为序列特征和静态特征。序列特征包括缺勤月份、星期几、是否节假日前后、请假类型、请假原因、是否临时请假、是否连续缺勤、加班、通勤、夜班、慢性病、加班通勤压力指数和缺勤历史强度等。静态特征包括行业、婚姻状态、岗位、岗位级别、年龄、司龄、子女数量、班次、绩效、BMI、健康风险、家庭负担和岗位稳定性等。
### 24. 深度学习模型的训练参数是什么?
模型使用 PyTorch 实现,优化器为 `AdamW`,学习率约为 `9e-4`,损失函数为 `SmoothL1Loss`,默认训练 80 个 epochbatch size 为 128并使用 `OneCycleLR` 学习率调度和早停机制。目标变量使用 `log1p` 变换,以降低长尾缺勤时长对训练的影响。
### 25. 为什么使用 SmoothL1Loss 而不是 MSE
MSE 对异常值非常敏感,少量较大的缺勤时长可能会放大损失,影响模型稳定性。`SmoothL1Loss` 在误差较小时接近平方损失,在误差较大时更接近绝对误差,对异常值更稳健,适合缺勤时长这类可能存在长尾分布的任务。
### 26. 模型评价指标有哪些?
回归模型主要使用 R2、MSE、RMSE 和 MAE。R2 反映模型解释目标变量波动的能力,越接近 1 越好RMSE 和 MAE 反映预测误差,越小越好。风险分类模型使用准确率、宏平均精确率、宏平均召回率、宏平均 F1 和混淆矩阵。
### 27. 当前哪个模型效果最好?
从当前保存的训练结果看,深度学习模型 `lstm_mlp` 效果最好R2 约为 0.9397RMSE 约为 0.5457MAE 约为 0.4322。传统模型中 XGBoost 和梯度提升树表现较好,但整体低于深度学习模型。
### 28. 为什么深度学习模型效果更好?
传统模型主要基于单条结构化样本建模,而深度学习模型额外利用了员工历史缺勤事件序列。缺勤行为具有时间关联性,例如连续缺勤、近期请假类型、通勤和加班压力变化都会影响当前缺勤。深度模型通过 8 步窗口和注意力机制捕捉这些时序关系,因此在当前数据集上表现更好。
### 29. 训练中如何防止过拟合?
主要采取了几种措施:训练集和测试集分离;深度模型中使用 dropout、权重衰减、早停机制和验证集监控传统模型使用交叉验证和随机参数搜索此外还使用特征选择和异常值处理减少噪声特征和极端值对模型的影响。
### 30. 你的模型有没有数据泄露风险?
项目中在传统模型训练时删除了员工编号和企业编号等标识字段,避免模型直接记忆个体或企业。训练集和测试集先划分,再基于训练集拟合编码器、缩放器和异常值边界,并应用到测试集。深度模型也保存训练阶段的类别映射和标准化参数,预测阶段复用这些参数,尽量避免训练信息泄露。
## 五、可解释性与 SHAP
### 31. 为什么要引入 SHAP
单纯预测一个缺勤时长并不能让管理者信服也不知道该如何干预。SHAP 可以解释每个特征对模型输出的正向或负向贡献,帮助用户理解为什么预测结果较高或较低。这样系统输出不仅有结果,还有原因。
### 32. SHAP 和特征重要性有什么区别?
特征重要性更多是全局层面的说明整体上哪些特征重要SHAP 既可以做全局解释,也可以做局部解释。局部解释可以针对某一个预测样本说明具体是哪几个因素推高或降低了缺勤风险。因此 SHAP 更适合解释单次预测结果。
### 33. 为什么深度模型效果最好,但解释时还要用树模型?
树模型的 SHAP 解释更成熟、计算效率也更高。深度模型主要承担高精度预测任务,树模型承担可解释分析任务。系统在预测时可以选择深度模型输出结果,同时使用可解释树模型辅助说明影响因素,这样兼顾预测效果和解释性。
### 34. 你的解释结果如何和 JD-R 理论结合?
系统会把影响因素按 JD-R 维度理解,例如加班、通勤、夜班属于工作要求,组织支持和职业发展属于工作资源,心理韧性和自我效能属于个人资源。这样 SHAP 不只是列出单个字段,而是可以转化为“工作要求较高、资源不足、个人资源不足”等机制说明。
## 六、系统设计与实现
### 35. 系统采用什么架构?
系统采用前后端分离架构。后端使用 Flask 提供接口负责数据处理、模型预测、JD-R 分析、SHAP 解释和聚类分析;前端使用 Vue 3、Element Plus 和 ECharts负责页面展示、表单输入和图表可视化。
### 36. 后端主要模块有哪些?
后端主要包括接口层、服务层和核心算法层。接口层在 `backend/api` 中,负责暴露 HTTP 接口;服务层在 `backend/services` 中,负责组织业务逻辑;核心算法层在 `backend/core` 中,负责数据生成、特征工程、模型训练、聚类和 SHAP 分析。
### 37. 前端主要实现了哪些页面?
前端主要包括数据概览、影响因素分析、JD-R 理论分析、缺勤预测、员工画像等页面。数据概览展示趋势和分布影响因素分析展示特征重要性和相关性JD-R 页面展示理论维度;预测页面展示预测结果、模型对比和解释;员工画像页面展示聚类分群。
### 38. 系统接口有哪些?
系统接口包括数据概览接口、影响因素分析接口、预测接口、聚类接口、JD-R 接口和 SHAP 接口。例如预测模块包含 `/api/predict/models``/api/predict/model-info``/api/predict/single``/api/predict/compare``/api/predict/risk-classify`
### 39. 模型是实时训练还是离线训练?
模型采用离线训练、在线推理的方式。训练脚本 `backend/core/train_model.py` 负责生成并保存模型文件,系统运行时直接加载已保存模型进行预测。这样可以提高接口响应速度,也更符合实际系统部署方式。
### 40. 预测时输入一条数据,系统如何处理?
系统先将前端输入转换成模型需要的字段格式,然后进行特征工程、类别编码和标准化。如果选择传统模型,就使用保存好的缩放器和特征选择结果生成输入特征;如果选择深度模型,就构造当前样本的序列窗口和静态特征,再调用 PyTorch 模型输出缺勤时长。最后系统根据预测小时数给出风险等级和解释结果。
## 七、聚类与员工画像
### 41. 为什么要做员工画像?
预测是针对个体样本的,员工画像则帮助企业从群体层面理解不同类型员工的缺勤特征。例如有些群体可能是高加班高通勤型,有些是健康风险较高型,有些是工作资源不足型。通过聚类画像,管理者可以制定更有针对性的群体干预策略。
### 42. 你使用了什么聚类方法?
系统使用 K-Means 聚类。K-Means 适合处理结构化数值特征,结果也比较容易解释。聚类后系统会结合群体中心、风险水平和特征分布生成群体画像,并通过散点图、雷达图等方式展示。
### 43. 聚类结果如何解释?
聚类结果不是简单给出类别编号,而是结合每一类员工在加班、通勤、健康风险、家庭负担、工作要求和资源等维度上的差异,命名和解释不同群体。例如可以解释为“高压力高缺勤风险群体”“稳定低风险群体”或“健康风险偏高群体”。
## 八、结果、局限与改进
### 44. 你的系统最终达到了什么效果?
系统实现了缺勤数据统计、趋势分析、影响因素挖掘、JD-R 理论解释、缺勤时长预测、风险等级评估、SHAP 解释和员工画像等功能。模型训练结果显示,深度学习模型在当前数据集上取得了较好的预测效果,系统功能也能支撑论文中的分析、解释和预测主线。
### 45. 你认为项目最大的价值是什么?
最大的价值是把缺勤分析从单纯统计扩展为“可解释的风险预测”。系统不仅能告诉用户某个员工可能缺勤多久,还能说明影响风险的主要因素,并从 JD-R 理论角度解释工作要求、资源和个人因素如何作用于缺勤。
### 46. 你的项目有哪些不足?
主要不足有三点。第一数据为模拟数据虽然结构完整但还需要真实企业脱敏数据进一步验证。第二JD-R 变量的测量在真实场景中通常需要问卷或长期跟踪,当前项目中是通过数据字段模拟表达。第三,系统工程层面还可以继续增强,例如增加权限管理、数据库持久化、报表导出和在线模型更新。
### 47. 如果后续继续改进,你会怎么做?
后续可以从三个方向改进。第一,引入真实企业脱敏数据,验证模型泛化能力。第二,完善 JD-R 变量测量方式,例如结合问卷量表和长期行为数据。第三,增强系统部署能力,比如加入数据库、用户权限、定期训练、模型版本管理和报表导出功能。
### 48. 你的系统是否能直接用于企业生产环境?
目前系统更适合作为毕业设计原型和分析框架验证。要进入企业生产环境,还需要接入真实数据源、完善权限和隐私保护、进行更多数据验证,并建立模型更新和监控机制。但从功能流程上看,系统已经具备数据分析、预测、解释和展示的基本闭环。
### 49. 如果老师质疑模型效果过高,你怎么回答?
我会说明当前数据是模拟数据,变量之间存在设计好的业务规律,因此模型效果可能高于真实企业数据。我的重点不是声称该模型已经能直接替代真实企业决策,而是验证分析框架、特征工程、模型训练和系统实现流程是可行的。后续如果接入真实脱敏数据,需要重新训练和评估模型。
### 50. 如果老师问你的工作量体现在哪里?
工作量主要体现在五个方面:第一,构建和整理缺勤事件数据集;第二,完成后端数据处理、特征工程、模型训练和可解释分析;第三,实现前端多个可视化页面;第四,将 JD-R 理论转化为可计算指标和系统模块;第五,完成论文文档、接口设计、系统架构和实验分析,使系统和论文内容保持一致。
## 九、现场答辩高频追问
### 51. 你的题目中“解释”具体指什么?
这里的解释包含两层含义。第一是理论解释,即用 JD-R 理论说明缺勤和工作要求、资源、倦怠、投入之间的关系。第二是模型解释,即用 SHAP 分析具体特征对预测结果的贡献。两者结合后,系统能从理论和算法两个角度解释缺勤风险。
### 52. 为什么不用单一模型?
单一模型难以同时兼顾预测效果、稳定性和解释性。深度模型预测效果较好,但解释较复杂;树模型解释性较强,也适合做特征重要性和 SHAP分类模型适合输出风险等级。因此系统采用多模型组合更符合实际分析需要。
### 53. 你的系统和普通机器学习预测系统有什么区别?
普通预测系统往往只输出预测结果。本系统除了预测缺勤时长还包含数据概览、影响因素分析、JD-R 理论解释、SHAP 局部解释和员工画像。它更强调从现象到机制再到预测和建议的完整链条。
### 54. 你如何保证前后端数据一致?
前端输入字段会在后端通过 `build_prediction_dataframe` 转换为统一的中文业务字段,然后复用训练时的特征工程、编码器、缩放器和特征列表。模型训练和模型推理使用同一套字段规范和处理流程,从而保证前后端数据格式一致。
### 55. 如果某些输入字段缺失怎么办?
系统在预测输入中设置了默认值,并在特征对齐时对缺失字段补 0 或使用默认业务值。这样可以减少接口调用失败,但在真实企业场景中,仍然建议保证核心字段完整,否则预测可信度会下降。
### 56. 你怎么看模型预测在 HR 管理中的伦理问题?
模型预测只能作为辅助决策,不能直接作为惩罚员工或评价员工的唯一依据。缺勤风险可能与健康、家庭和工作压力有关,企业应该用预测结果做关怀、排班优化和资源支持,而不是简单标签化员工。真实部署时还需要保护隐私、控制权限和保证数据合规。
### 57. 答辩时如果只能用一分钟介绍模型,你怎么说?
我的系统采用多模型训练策略。传统模型包括随机森林、梯度提升树、极端随机树和 XGBoost用于结构化特征预测和解释深度学习部分使用 PyTorch 构建时序注意力融合模型,将员工最近 8 次缺勤事件作为序列输入,并融合年龄、岗位、健康、家庭负担等静态特征,预测缺勤时长。实验结果显示,深度模型在当前数据集上效果最好,同时系统使用树模型和 SHAP 提供可解释分析。
### 58. 答辩时如果只能用一分钟介绍系统,你怎么说?
本系统是一个面向企业员工缺勤管理的分析、解释与预测系统。后端使用 Flask、Pandas、Scikit-learn 和 PyTorch前端使用 Vue 3、Element Plus 和 ECharts。系统包含数据概览、影响因素分析、JD-R 理论分析、缺勤预测、SHAP 解释和员工画像六类功能。它的核心不是单纯预测缺勤时长,而是形成“现象发现、因素识别、理论解释、风险预测和管理建议”的完整流程。
## 十、答辩回答技巧
### 59. 回答模型问题时的推荐结构
可以按“输入、处理、模型、输出、评价”五步回答。例如:输入是员工缺勤事件和静态属性;处理包括清洗、特征工程、编码和标准化;模型包括传统机器学习和时序深度学习;输出是缺勤时长和风险等级;评价指标包括 R2、RMSE、MAE 和分类 F1。
### 60. 回答不足问题时的推荐结构
不要只说“系统还不完善”。建议按“数据、模型、工程”三层回答。数据层面是真实企业数据不足;模型层面是需要更多真实场景验证和持续更新;工程层面是还可以增加权限管理、数据库、报表导出和线上部署能力。
### 61. 回答创新点时的推荐结构
建议回答三点即可:第一,结合 JD-R 理论解释员工缺勤机制;第二,融合传统机器学习、时序深度学习和 SHAP 可解释分析;第三,实现了从数据概览到预测解释再到员工画像的完整可视化系统。
### 62. 回答真实应用问题时的推荐结构
先肯定系统具备应用原型价值,再说明生产环境需要补充条件。可以回答:系统已经具备数据分析、预测和解释闭环,但真实部署需要接入企业脱敏数据、完善权限和隐私保护、持续监控模型效果,并让预测结果只作为辅助决策依据。

View File

@@ -0,0 +1,670 @@
from __future__ import annotations
import re
from dataclasses import dataclass
from pathlib import Path
from docx import Document
from docx.enum.section import WD_SECTION_START
from docx.enum.style import WD_STYLE_TYPE
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK, WD_LINE_SPACING
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Cm, Pt
ROOT = Path(__file__).resolve().parents[2]
SOURCE = ROOT / 'docs' / '11_毕业论文正文初稿.md'
OUTPUT = ROOT / 'docs' / '11_毕业论文正文初稿_河农大格式.docx'
TABLE_TITLES = [
'表 6-1 测试环境',
'表 6-2 功能测试结果',
'表 6-3 主要接口测试情况',
'表 6-4 各模型在测试集上的回归性能',
'表 6-5 主要分类模型结果',
]
CHINESE_TITLE_PLACEHOLDER_DATE = '二〇二六年〇月〇日'
ENGLISH_TITLE_PLACEHOLDER = 'ENGLISH TITLE PLACEHOLDER'
FONT_SONG = '宋体'
FONT_HEI = '黑体'
FONT_KAI = '楷体'
FONT_TNR = 'Times New Roman'
FONT_MONO = 'Courier New'
@dataclass
class Block:
kind: str
text: str | None = None
level: int | None = None
lines: list[str] | None = None
def set_run_fonts(run, east_asia: str, ascii_font: str, size: float | int | None = None, bold: bool | None = None):
if size is not None:
run.font.size = Pt(size)
if bold is not None:
run.bold = bold
run.font.name = ascii_font
r_pr = run._element.get_or_add_rPr()
r_fonts = r_pr.rFonts
if r_fonts is None:
r_fonts = OxmlElement('w:rFonts')
r_pr.insert(0, r_fonts)
r_fonts.set(qn('w:ascii'), ascii_font)
r_fonts.set(qn('w:hAnsi'), ascii_font)
r_fonts.set(qn('w:eastAsia'), east_asia)
r_fonts.set(qn('w:cs'), ascii_font)
def set_style_fonts(style, east_asia: str, ascii_font: str, size: float | int, bold: bool = False):
style.font.size = Pt(size)
style.font.bold = bold
style.font.name = ascii_font
style._element.rPr.rFonts.set(qn('w:ascii'), ascii_font)
style._element.rPr.rFonts.set(qn('w:hAnsi'), ascii_font)
style._element.rPr.rFonts.set(qn('w:eastAsia'), east_asia)
style._element.rPr.rFonts.set(qn('w:cs'), ascii_font)
def configure_document(doc: Document):
for section in doc.sections:
apply_page_layout(section)
normal = doc.styles['Normal']
set_style_fonts(normal, FONT_SONG, FONT_TNR, 10.5, False)
normal.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
normal.paragraph_format.line_spacing = Pt(20)
normal.paragraph_format.first_line_indent = Pt(21)
normal.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
heading1 = doc.styles['Heading 1']
set_style_fonts(heading1, FONT_HEI, FONT_TNR, 14, True)
heading1.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
heading1.paragraph_format.first_line_indent = Pt(0)
heading1.paragraph_format.space_before = Pt(12)
heading1.paragraph_format.space_after = Pt(6)
heading1.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
heading1.paragraph_format.line_spacing = Pt(20)
heading2 = doc.styles['Heading 2']
set_style_fonts(heading2, FONT_HEI, FONT_TNR, 12, True)
heading2.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
heading2.paragraph_format.first_line_indent = Pt(0)
heading2.paragraph_format.space_before = Pt(10)
heading2.paragraph_format.space_after = Pt(4)
heading2.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
heading2.paragraph_format.line_spacing = Pt(20)
heading3 = doc.styles['Heading 3']
set_style_fonts(heading3, FONT_HEI, FONT_TNR, 10.5, True)
heading3.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
heading3.paragraph_format.first_line_indent = Pt(0)
heading3.paragraph_format.space_before = Pt(8)
heading3.paragraph_format.space_after = Pt(3)
heading3.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
heading3.paragraph_format.line_spacing = Pt(20)
heading4 = doc.styles['Heading 4']
set_style_fonts(heading4, FONT_HEI, FONT_TNR, 10.5, True)
heading4.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
heading4.paragraph_format.first_line_indent = Pt(0)
heading4.paragraph_format.space_before = Pt(6)
heading4.paragraph_format.space_after = Pt(3)
heading4.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
heading4.paragraph_format.line_spacing = Pt(20)
for toc_name in ('TOC 1', 'TOC 2', 'TOC 3'):
if toc_name in doc.styles:
toc_style = doc.styles[toc_name]
set_style_fonts(toc_style, FONT_SONG, FONT_TNR, 12, False)
toc_style.paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
toc_style.paragraph_format.line_spacing = 1.5
if 'CodeBlock' not in doc.styles:
code_style = doc.styles.add_style('CodeBlock', WD_STYLE_TYPE.PARAGRAPH)
else:
code_style = doc.styles['CodeBlock']
set_style_fonts(code_style, FONT_MONO, FONT_MONO, 10.5, False)
code_style.paragraph_format.first_line_indent = Pt(0)
code_style.paragraph_format.left_indent = Pt(12)
code_style.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
code_style.paragraph_format.line_spacing = Pt(20)
enable_update_fields_on_open(doc)
def apply_page_layout(section):
section.page_width = Cm(21)
section.page_height = Cm(29.7)
section.top_margin = Cm(2.5)
section.bottom_margin = Cm(2.5)
section.left_margin = Cm(3)
section.right_margin = Cm(3)
def enable_update_fields_on_open(doc: Document):
settings = doc.settings.element
update_fields = settings.find(qn('w:updateFields'))
if update_fields is None:
update_fields = OxmlElement('w:updateFields')
settings.append(update_fields)
update_fields.set(qn('w:val'), 'true')
def add_field(paragraph, instruction: str, placeholder: str | None = None):
begin = OxmlElement('w:fldChar')
begin.set(qn('w:fldCharType'), 'begin')
instr = OxmlElement('w:instrText')
instr.set(qn('xml:space'), 'preserve')
instr.text = instruction
separate = OxmlElement('w:fldChar')
separate.set(qn('w:fldCharType'), 'separate')
end = OxmlElement('w:fldChar')
end.set(qn('w:fldCharType'), 'end')
paragraph._p.append(begin)
paragraph._p.append(instr)
paragraph._p.append(separate)
if placeholder:
run = paragraph.add_run(placeholder)
set_run_fonts(run, FONT_SONG, FONT_TNR, 12, False)
paragraph._p.append(end)
def configure_footer_page_number(section, fmt: str, start: int):
sect_pr = section._sectPr
pg_num_type = sect_pr.find(qn('w:pgNumType'))
if pg_num_type is None:
pg_num_type = OxmlElement('w:pgNumType')
sect_pr.append(pg_num_type)
pg_num_type.set(qn('w:fmt'), fmt)
pg_num_type.set(qn('w:start'), str(start))
section.footer.is_linked_to_previous = False
footer_p = section.footer.paragraphs[0]
footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_field(footer_p, ' PAGE ')
for run in footer_p.runs:
set_run_fonts(run, FONT_TNR, FONT_TNR, 10.5, False)
def add_cover(doc: Document, title: str):
for _ in range(4):
doc.add_paragraph()
title_p = doc.add_paragraph()
title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
title_p.paragraph_format.space_after = Pt(18)
run = title_p.add_run(title)
set_run_fonts(run, FONT_HEI, FONT_TNR, 18, True)
en_title_p = doc.add_paragraph()
en_title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
en_title_p.paragraph_format.space_after = Pt(30)
run = en_title_p.add_run(ENGLISH_TITLE_PLACEHOLDER)
set_run_fonts(run, FONT_TNR, FONT_TNR, 18, True)
for _ in range(4):
doc.add_paragraph()
cover_lines = [
'作 者________________________',
'学 院________________________',
'专 业________________________',
'班 级________________________',
'学 号________________________',
'指导教师________________________',
f'完成日期:{CHINESE_TITLE_PLACEHOLDER_DATE}',
]
for line in cover_lines:
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(8)
run = p.add_run(line)
set_run_fonts(run, FONT_SONG, FONT_TNR, 15, False)
def add_toc_page(doc: Document):
toc_title = doc.add_paragraph()
toc_title.alignment = WD_ALIGN_PARAGRAPH.CENTER
toc_title.paragraph_format.space_before = Pt(16)
toc_title.paragraph_format.space_after = Pt(12)
toc_title.paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
toc_title.paragraph_format.line_spacing = 1.5
run = toc_title.add_run('目 录')
set_run_fonts(run, FONT_HEI, FONT_TNR, 16, True)
toc_p = doc.add_paragraph()
add_field(toc_p, r' TOC \o "1-3" \h \z \u ', '右键更新目录')
doc.add_page_break()
def add_heading_paragraph(doc: Document, text: str, style_name: str, align=WD_ALIGN_PARAGRAPH.LEFT, size: float | int | None = None):
p = doc.add_paragraph(style=style_name)
p.alignment = align
if size is not None:
for run in p.runs:
run.clear()
run = p.add_run(text)
style_east_asia = FONT_HEI
style_ascii = FONT_TNR
if style_name == 'Heading 1':
style_east_asia = FONT_HEI
style_ascii = FONT_TNR
elif style_name == 'Heading 2':
style_east_asia = FONT_HEI
elif style_name == 'Heading 3':
style_east_asia = FONT_HEI
elif style_name == 'Heading 4':
style_east_asia = FONT_HEI
set_run_fonts(run, style_east_asia, style_ascii, size or 10.5, True)
return p
def render_inline(paragraph, text: str, east_asia: str, ascii_font: str, size: float | int, default_bold: bool = False):
token_pattern = re.compile(r'(\*\*.*?\*\*|`[^`]+`)')
parts = token_pattern.split(text)
for part in parts:
if not part:
continue
if part.startswith('**') and part.endswith('**'):
run = paragraph.add_run(part[2:-2])
set_run_fonts(run, east_asia, ascii_font, size, True)
elif part.startswith('`') and part.endswith('`'):
run = paragraph.add_run(part[1:-1])
set_run_fonts(run, FONT_MONO, FONT_MONO, size, False)
else:
run = paragraph.add_run(part)
set_run_fonts(run, east_asia, ascii_font, size, default_bold)
def add_body_paragraph(doc: Document, text: str, indent: bool = True, hanging: bool = False):
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
p.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
p.paragraph_format.line_spacing = Pt(20)
if hanging:
p.paragraph_format.left_indent = Pt(21)
p.paragraph_format.first_line_indent = Pt(-21)
else:
p.paragraph_format.first_line_indent = Pt(21) if indent else Pt(0)
render_inline(p, text, FONT_SONG, FONT_TNR, 10.5, False)
return p
def add_code_paragraph(doc: Document, text: str):
p = doc.add_paragraph(style='CodeBlock')
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
p.paragraph_format.first_line_indent = Pt(0)
run = p.add_run(text)
set_run_fonts(run, FONT_MONO, FONT_MONO, 10.5, False)
return p
def clean_title_text(text: str) -> str:
match = re.match(r'^第(\d+)章\s+(.+)$', text)
if match:
return f'{match.group(1)} {match.group(2)}'
return text
def split_cells(line: str) -> list[str]:
parts = [part.strip() for part in line.strip().strip('|').split('|')]
return parts
def ensure_cell_has_paragraph(cell):
if cell.paragraphs:
p = cell.paragraphs[0]
p.clear()
return p
return cell.add_paragraph()
def set_cell_border(cell, **kwargs):
tc = cell._tc
tc_pr = tc.get_or_add_tcPr()
tc_borders = tc_pr.first_child_found_in('w:tcBorders')
if tc_borders is None:
tc_borders = OxmlElement('w:tcBorders')
tc_pr.append(tc_borders)
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
edge_data = kwargs.get(edge)
if edge_data:
tag = 'w:' + edge
element = tc_borders.find(qn(tag))
if element is None:
element = OxmlElement(tag)
tc_borders.append(element)
for key, value in edge_data.items():
element.set(qn(f'w:{key}'), str(value))
def remove_all_table_borders(table):
tbl_pr = table._tbl.tblPr
tbl_borders = tbl_pr.first_child_found_in('w:tblBorders')
if tbl_borders is None:
tbl_borders = OxmlElement('w:tblBorders')
tbl_pr.append(tbl_borders)
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
element = tbl_borders.find(qn(f'w:{edge}'))
if element is None:
element = OxmlElement(f'w:{edge}')
tbl_borders.append(element)
element.set(qn('w:val'), 'nil')
def apply_three_line_table(table):
remove_all_table_borders(table)
row_count = len(table.rows)
col_count = len(table.columns)
for row in table.rows:
for cell in row.cells:
set_cell_border(
cell,
top={'val': 'nil'},
bottom={'val': 'nil'},
left={'val': 'nil'},
right={'val': 'nil'},
)
for col in range(col_count):
set_cell_border(
table.rows[0].cells[col],
top={'val': 'single', 'sz': 12, 'space': 0, 'color': '000000'},
bottom={'val': 'single', 'sz': 4, 'space': 0, 'color': '000000'},
left={'val': 'nil'},
right={'val': 'nil'},
)
set_cell_border(
table.rows[row_count - 1].cells[col],
bottom={'val': 'single', 'sz': 12, 'space': 0, 'color': '000000'},
left={'val': 'nil'},
right={'val': 'nil'},
)
def add_table_title(doc: Document, title: str):
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.space_before = Pt(6)
p.paragraph_format.space_after = Pt(2)
run = p.add_run(title)
set_run_fonts(run, FONT_HEI, FONT_TNR, 9, True)
def add_markdown_table(doc: Document, lines: list[str], title: str):
add_table_title(doc, title)
header = split_cells(lines[0])
body_lines = lines[2:]
rows = [split_cells(line) for line in body_lines]
table = doc.add_table(rows=len(rows) + 1, cols=len(header))
table.alignment = WD_TABLE_ALIGNMENT.CENTER
table.autofit = True
for col, text in enumerate(header):
cell = table.rows[0].cells[col]
p = ensure_cell_has_paragraph(cell)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
p.paragraph_format.line_spacing = Pt(14)
run = p.add_run(text)
set_run_fonts(run, FONT_SONG, FONT_TNR, 9, True)
for row_idx, row in enumerate(rows, start=1):
for col_idx, text in enumerate(row):
cell = table.rows[row_idx].cells[col_idx]
p = ensure_cell_has_paragraph(cell)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
p.paragraph_format.line_spacing = Pt(14)
run = p.add_run(text)
set_run_fonts(run, FONT_SONG, FONT_TNR, 9, False)
apply_three_line_table(table)
doc.add_paragraph()
def parse_markdown_blocks(lines: list[str]) -> list[Block]:
blocks: list[Block] = []
paragraph_lines: list[str] = []
i = 0
in_code = False
code_lines: list[str] = []
def flush_paragraph():
if paragraph_lines:
blocks.append(Block(kind='paragraph', text=''.join(paragraph_lines).strip()))
paragraph_lines.clear()
while i < len(lines):
line = lines[i]
stripped = line.strip()
if stripped.startswith('```'):
flush_paragraph()
if in_code:
blocks.append(Block(kind='code', lines=code_lines.copy()))
code_lines.clear()
in_code = False
else:
in_code = True
i += 1
continue
if in_code:
code_lines.append(line.rstrip('\n'))
i += 1
continue
if stripped == '':
flush_paragraph()
i += 1
continue
heading_match = re.match(r'^(#{2,4})\s+(.+)$', stripped)
if heading_match:
flush_paragraph()
blocks.append(
Block(
kind='heading',
level=len(heading_match.group(1)),
text=heading_match.group(2).strip(),
)
)
i += 1
continue
if stripped.startswith('|'):
flush_paragraph()
table_lines: list[str] = []
while i < len(lines) and lines[i].strip().startswith('|'):
table_lines.append(lines[i].strip())
i += 1
blocks.append(Block(kind='table', lines=table_lines))
continue
list_match = re.match(r'^(\d+\.\s+|[-*]\s+)(.+)$', stripped)
if list_match:
flush_paragraph()
blocks.append(Block(kind='list_item', text=stripped))
i += 1
continue
paragraph_lines.append(stripped)
i += 1
flush_paragraph()
return blocks
def split_document_blocks(blocks: list[Block]) -> tuple[list[Block], list[Block]]:
abstract_blocks: list[Block] = []
body_blocks: list[Block] = []
in_body = False
for block in blocks:
if block.kind == 'heading' and block.text and re.match(r'^第\d+章\s+', block.text):
in_body = True
if in_body:
body_blocks.append(block)
else:
abstract_blocks.append(block)
return abstract_blocks, body_blocks
def read_source() -> tuple[str, list[Block]]:
text = SOURCE.read_text(encoding='utf-8')
lines = text.splitlines()
if not lines or not lines[0].startswith('# '):
raise ValueError('源 Markdown 缺少一级标题作为论文题目。')
title = lines[0][2:].strip()
content_lines = lines[1:]
cutoff = len(content_lines)
for idx, line in enumerate(content_lines):
if line.strip() == '## 后续补强建议':
cutoff = idx
break
content_lines = content_lines[:cutoff]
blocks = parse_markdown_blocks(content_lines)
return title, blocks
def add_section_heading(doc: Document, text: str, level: int):
if level == 2:
normalized = clean_title_text(text)
p = doc.add_paragraph(style='Heading 1')
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
p.paragraph_format.first_line_indent = Pt(0)
p.clear()
run = p.add_run(normalized)
set_run_fonts(run, FONT_HEI, FONT_TNR, 14, True)
return
if level == 3:
p = doc.add_paragraph(style='Heading 2')
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
p.paragraph_format.first_line_indent = Pt(0)
p.clear()
run = p.add_run(text)
set_run_fonts(run, FONT_HEI, FONT_TNR, 12, True)
return
p = doc.add_paragraph(style='Heading 3')
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
p.paragraph_format.first_line_indent = Pt(0)
p.clear()
run = p.add_run(text)
set_run_fonts(run, FONT_HEI, FONT_TNR, 10.5, True)
def render_blocks(doc: Document, blocks: list[Block], is_abstract: bool = False):
table_index = 0
current_section = '摘要' if is_abstract else ''
for block in blocks:
if block.kind == 'heading':
if is_abstract and block.text == '摘要':
current_section = '摘要'
p = doc.add_paragraph(style='Heading 1')
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.space_before = Pt(16)
p.paragraph_format.space_after = Pt(10)
p.clear()
run = p.add_run('摘 要')
set_run_fonts(run, FONT_HEI, FONT_TNR, 16, True)
else:
heading_text = block.text or ''
current_section = heading_text
if heading_text in ('参考文献', '致谢'):
doc.add_page_break()
p = doc.add_paragraph(style='Heading 1')
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.first_line_indent = Pt(0)
p.paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
p.paragraph_format.line_spacing = 1.5
p.clear()
run = p.add_run(heading_text)
set_run_fonts(run, FONT_HEI, FONT_TNR, 14, True)
else:
add_section_heading(doc, heading_text, block.level or 3)
continue
if block.kind == 'paragraph':
text = block.text or ''
no_indent = text.startswith('**关键词') or text.startswith('关键词:')
is_reference = current_section == '参考文献' and re.match(r'^\[\d+\]', text)
add_body_paragraph(doc, text, indent=not no_indent and not is_reference, hanging=is_reference)
continue
if block.kind == 'list_item':
add_body_paragraph(doc, block.text or '', indent=False)
continue
if block.kind == 'code':
for line in block.lines or []:
add_code_paragraph(doc, line)
if not block.lines:
add_code_paragraph(doc, '')
continue
if block.kind == 'table':
title = TABLE_TITLES[table_index] if table_index < len(TABLE_TITLES) else f'{table_index + 1}'
add_markdown_table(doc, block.lines or [], title)
table_index += 1
def build_document():
title, blocks = read_source()
abstract_blocks, body_blocks = split_document_blocks(blocks)
doc = Document()
configure_document(doc)
add_cover(doc, title)
preface_section = doc.add_section(WD_SECTION_START.NEW_PAGE)
apply_page_layout(preface_section)
configure_footer_page_number(preface_section, 'upperRoman', 1)
add_toc_page(doc)
render_blocks(doc, abstract_blocks, is_abstract=True)
body_section = doc.add_section(WD_SECTION_START.NEW_PAGE)
apply_page_layout(body_section)
configure_footer_page_number(body_section, 'decimal', 1)
render_blocks(doc, body_blocks, is_abstract=False)
doc.save(OUTPUT)
def main():
build_document()
print(f'Generated: {OUTPUT}')
if __name__ == '__main__':
main()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.