feat: 添加 JD-R 理论分析模块与 SHAP 可解释性分析功能
- 后端新增 JD-R(工作要求-资源)理论维度数据生成,包含工作要求、工作资源、
个人资源、中介变量共 16 个新特征列
- 新增 JD-R 分析服务与 API(维度统计、倦怠投入分析、双路径中介分析、
分组轮廓、风险分布)
- 新增 SHAP 可解释性分析模块(全局重要性、局部解释、特征交互、依赖图)
- 预测服务增加风险分类模型加载与概率预测能力
- 前端新增 JD-R 分析页面(JDRAnalysis.vue),含雷达图、散点图、路径分析等可视化
- 预测页面增加风险概率展示与 SHAP 特征解释
- 路由与导航菜单同步更新
This commit is contained in:
@@ -387,16 +387,181 @@ def generate_dataset(output_path=None, sample_count=12000, random_state=None):
|
||||
return df
|
||||
|
||||
|
||||
def ensure_dataset():
|
||||
if not os.path.exists(config.RAW_DATA_PATH):
|
||||
generate_dataset(config.RAW_DATA_PATH)
|
||||
return
|
||||
def enrich_with_jdr_columns(df):
|
||||
"""为现有数据追加 JD-R(工作要求-资源)理论维度列。
|
||||
|
||||
try:
|
||||
df = pd.read_csv(config.RAW_DATA_PATH)
|
||||
validate_dataset(df)
|
||||
except Exception:
|
||||
在已有的员工/事件属性基础上,合成 16 个新列:
|
||||
- 工作要求:工作自主性、情绪劳动强度、时间压力感知、角色模糊度、工作家庭冲突
|
||||
- 工作资源:上级支持、同事支持、技能多样性、职业发展机会、参与决策、组织公平感
|
||||
- 个人资源:自我效能感、心理韧性、乐观程度
|
||||
- 中介变量:工作倦怠、工作投入
|
||||
"""
|
||||
rng = np.random.default_rng(config.RANDOM_STATE + 100)
|
||||
df = df.copy()
|
||||
n = len(df)
|
||||
|
||||
# ── 辅助:条件性 Likert 生成 ──
|
||||
def likert(mean_offset, std=0.8, low=1.0, high=5.0):
|
||||
return np.clip(rng.normal(mean_offset, std, size=n), low, high)
|
||||
|
||||
# ── 预提取列 ──
|
||||
overtime = df['月均加班时长'].values
|
||||
commute = df['通勤时长分钟'].values
|
||||
night = df['是否夜班岗位'].values
|
||||
children = df['子女数量'].values
|
||||
married_arr = (df['婚姻状态'] == '已婚').astype(int).values
|
||||
tenure = df['司龄年数'].values
|
||||
team_size = df['团队人数'].values
|
||||
manager_span = df['直属上级管理跨度'].values
|
||||
exercise = df['每周运动频次'].values
|
||||
sleep = df['近30天睡眠时长均值'].values
|
||||
chronic = df['是否慢性病史'].values
|
||||
perf_a = (df['绩效等级'] == 'A').astype(int).values
|
||||
perf_ab = df['绩效等级'].isin(['A', 'B']).astype(int).values
|
||||
level_map = {'初级': 0, '中级': 1, '高级': 2, '主管': 3, '经理及以上': 4}
|
||||
level_vals = df['岗位级别'].map(level_map).fillna(1).values
|
||||
industry_vals = df['所属行业'].values
|
||||
employment_type = df['用工类型'].values
|
||||
job_family = df['岗位序列'].values
|
||||
company_scale_map = {
|
||||
'100人以下': 0, '100-499人': 1, '500-999人': 2, '1000-4999人': 3, '5000人及以上': 4
|
||||
}
|
||||
scale_vals = df['企业规模'].map(company_scale_map).fillna(1).values
|
||||
|
||||
formal_employee = (df['用工类型'] == '正式员工').astype(int).values
|
||||
edu_map = {'中专及以下': 0, '大专': 1, '本科': 2, '硕士': 3, '博士': 4}
|
||||
edu_vals = df['最高学历'].map(edu_map).fillna(2).values
|
||||
|
||||
# ── 工作要求维度 (5 列) ──
|
||||
df['工作自主性'] = likert(
|
||||
3.2 + level_vals * 0.25
|
||||
+ np.isin(industry_vals, ['互联网', '金融服务']).astype(int) * 0.3
|
||||
- night * 0.4
|
||||
).round(1)
|
||||
|
||||
df['情绪劳动强度'] = likert(
|
||||
2.8
|
||||
+ np.isin(job_family, ['客服坐席', '销售业务']).astype(int) * 0.6
|
||||
+ np.isin(industry_vals, ['医药健康', '零售连锁']).astype(int) * 0.3
|
||||
).round(1)
|
||||
|
||||
df['时间压力感知'] = likert(
|
||||
3.0 + overtime * 0.02 + commute * 0.01
|
||||
+ np.isin(industry_vals, ['互联网', '金融服务']).astype(int) * 0.2
|
||||
).round(1)
|
||||
|
||||
df['角色模糊度'] = likert(
|
||||
2.5
|
||||
+ np.isin(employment_type, ['劳务派遣', '外包驻场']).astype(int) * 0.5
|
||||
- tenure * 0.05
|
||||
).round(1)
|
||||
|
||||
df['工作家庭冲突'] = likert(
|
||||
2.6 + overtime * 0.02 + children * 0.3 + married_arr * 0.3
|
||||
).round(1)
|
||||
|
||||
# ── 工作资源维度 (6 列) ──
|
||||
df['上级支持'] = likert(
|
||||
3.4 - manager_span * 0.02 + level_vals * 0.2
|
||||
).round(1)
|
||||
|
||||
df['同事支持'] = likert(
|
||||
3.3 + team_size * 0.02
|
||||
+ np.isin(job_family, ['管理', '专业技术']).astype(int) * 0.2
|
||||
).round(1)
|
||||
|
||||
df['技能多样性'] = likert(
|
||||
3.0
|
||||
+ np.isin(job_family, ['专业技术', '管理']).astype(int) * 0.5
|
||||
- np.isin(job_family, ['生产操作']).astype(int) * 0.3
|
||||
).round(1)
|
||||
|
||||
df['职业发展机会'] = likert(
|
||||
3.1
|
||||
+ np.isin(industry_vals, ['互联网', '金融服务']).astype(int) * 0.4
|
||||
+ scale_vals * 0.1
|
||||
).round(1)
|
||||
|
||||
df['参与决策'] = likert(
|
||||
2.8 + level_vals * 0.35
|
||||
).round(1)
|
||||
|
||||
df['组织公平感'] = likert(
|
||||
3.3 + formal_employee * 0.4 + perf_ab * 0.3
|
||||
).round(1)
|
||||
|
||||
# ── 个人资源维度 (3 列) ──
|
||||
df['自我效能感'] = likert(
|
||||
3.3 + perf_a * 0.4 + perf_ab * 0.2 + tenure * 0.03 + edu_vals * 0.08
|
||||
).round(1)
|
||||
|
||||
df['心理韧性'] = likert(
|
||||
3.2 + exercise * 0.1 + sleep * 0.15 + tenure * 0.02
|
||||
).round(1)
|
||||
|
||||
df['乐观程度'] = likert(
|
||||
3.3 + perf_ab * 0.3 - chronic * 0.3 + married_arr * 0.15
|
||||
).round(1)
|
||||
|
||||
# ── 中介变量 (2 列) ──
|
||||
# 工作倦怠 (1-7):健康损伤过程 — 高需求→高倦怠
|
||||
df['工作倦怠'] = np.clip(
|
||||
rng.normal(3.0, 0.8, size=n)
|
||||
+ overtime * 0.015 + night * 0.3 + commute * 0.008
|
||||
+ df['情绪劳动强度'].values * 0.25
|
||||
+ df['时间压力感知'].values * 0.25
|
||||
+ df['工作家庭冲突'].values * 0.2
|
||||
+ df['角色模糊度'].values * 0.15
|
||||
- df['工作自主性'].values * 0.2
|
||||
- df['上级支持'].values * 0.15
|
||||
- df['自我效能感'].values * 0.2
|
||||
- df['心理韧性'].values * 0.15,
|
||||
1.0, 7.0
|
||||
).round(1)
|
||||
|
||||
# 工作投入 (1-7):激励过程 — 高资源→高投入
|
||||
df['工作投入'] = np.clip(
|
||||
rng.normal(3.5, 0.8, size=n)
|
||||
+ df['工作自主性'].values * 0.2
|
||||
+ df['上级支持'].values * 0.2
|
||||
+ df['同事支持'].values * 0.15
|
||||
+ df['技能多样性'].values * 0.15
|
||||
+ df['职业发展机会'].values * 0.15
|
||||
+ df['参与决策'].values * 0.1
|
||||
+ df['组织公平感'].values * 0.1
|
||||
+ df['自我效能感'].values * 0.2
|
||||
+ df['心理韧性'].values * 0.15
|
||||
+ df['乐观程度'].values * 0.15
|
||||
- df['工作倦怠'].values * 0.2,
|
||||
1.0, 7.0
|
||||
).round(1)
|
||||
|
||||
# JD-R 数据版本标记
|
||||
df['_jdr_version'] = config.JDR_DATA_VERSION
|
||||
|
||||
return df
|
||||
|
||||
|
||||
def ensure_dataset():
|
||||
needs_regenerate = not os.path.exists(config.RAW_DATA_PATH)
|
||||
|
||||
if not needs_regenerate:
|
||||
try:
|
||||
df = pd.read_csv(config.RAW_DATA_PATH)
|
||||
validate_dataset(df)
|
||||
except Exception:
|
||||
needs_regenerate = True
|
||||
|
||||
if needs_regenerate:
|
||||
generate_dataset(config.RAW_DATA_PATH)
|
||||
df = pd.read_csv(config.RAW_DATA_PATH)
|
||||
|
||||
# 检查是否需要 JD-R 数据丰富
|
||||
jdr_columns = ['工作自主性', '上级支持', '自我效能感', '工作倦怠', '工作投入']
|
||||
if not all(col in df.columns for col in jdr_columns):
|
||||
df = enrich_with_jdr_columns(df)
|
||||
os.makedirs(os.path.dirname(config.RAW_DATA_PATH), exist_ok=True)
|
||||
df.to_csv(config.RAW_DATA_PATH, index=False, encoding='utf-8-sig')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user