analyze_temporal.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. """分析考勤数据中的人员-项目-时间分布,验证时间建模方案。"""
  2. from __future__ import annotations
  3. import re
  4. from pathlib import Path
  5. import pandas as pd
  6. HR_ROOT = Path(r"E:\CODE\knowledge_agent\data\20260529_申勤提供数据\人事部")
  7. MONTH_PAT = re.compile(r"(\d{1,2})\s*月")
  8. def _month_from_sheet(sh: str, default: int | None = None) -> int | None:
  9. m = MONTH_PAT.search(sh)
  10. return int(m.group(1)) if m else default
  11. def _header_info(df: pd.DataFrame) -> tuple[str, int]:
  12. """从表头前几行中提取 项目名 与 年份。"""
  13. project = ""
  14. year = 2026
  15. for r in range(min(3, len(df))):
  16. for c in range(min(12, df.shape[1])):
  17. v = df.iat[r, c]
  18. if pd.isna(v):
  19. continue
  20. s = str(v)
  21. m = re.search(r"项目[::]\s*(.+)", s)
  22. if m and not project:
  23. project = m.group(1).strip()
  24. ym = re.search(r"20(\d{2})\s*年", s)
  25. if ym:
  26. year = 2000 + int(ym.group(1))
  27. return project, year
  28. def main() -> None:
  29. # employee -> {project: set(months)}
  30. emp_proj: dict[str, dict[str, set[int]]] = {}
  31. # project -> {month: employee count}
  32. proj_month: dict[str, dict[int, set[str]]] = {}
  33. total_rows = 0
  34. for p in sorted(HR_ROOT.rglob("*.xls")):
  35. try:
  36. xls = pd.ExcelFile(p)
  37. except Exception: # noqa: BLE001
  38. continue
  39. for sh in xls.sheet_names:
  40. try:
  41. df = pd.read_excel(xls, sheet_name=sh, header=None, dtype=str)
  42. except Exception: # noqa: BLE001
  43. continue
  44. if df.shape[0] < 5 or df.shape[1] < 4:
  45. continue
  46. project, year = _header_info(df)
  47. if not project:
  48. project = p.name
  49. month = _month_from_sheet(sh)
  50. sub = df.iloc[4:].copy()
  51. sub.columns = range(sub.shape[1])
  52. sub = sub[sub[3].notna()]
  53. for _, row in sub.iterrows():
  54. emp_id = str(row[1]).strip() if pd.notna(row[1]) else ""
  55. name = str(row[3]).strip()
  56. key = emp_id or f"{project}-{name}"
  57. total_rows += 1
  58. emp_proj.setdefault(key, {}).setdefault(project, set()).add(month or -1)
  59. proj_month.setdefault(project, {}).setdefault(month or -1, set()).add(key)
  60. print(f"考勤记录总数: {total_rows}")
  61. print(f"考勤文件涉及的唯一员工(按工号/姓名+项目): {len(emp_proj)}")
  62. multi = {k: v for k, v in emp_proj.items() if len(v) > 1}
  63. print(f"\n== 跨多个项目的人员数: {len(multi)} ==")
  64. for k, v in sorted(multi.items())[:20]:
  65. detail = ", ".join(f"{pj}({sorted(ms)})" for pj, ms in v.items())
  66. print(f" {k}: {detail}")
  67. print(f"\n== 项目 × 月份 覆盖(人数) ==")
  68. for pj, months in sorted(proj_month.items()):
  69. cells = ", ".join(f"{m}月:{len(s)}人" for m, s in sorted(months.items()))
  70. print(f" {pj}: {cells}")
  71. all_months = {m for pj in proj_month.values() for m in pj}
  72. print(f"\n涉及月份: {sorted(all_months)}")
  73. if __name__ == "__main__":
  74. main()