ML
Staffing Model
Source: hotelmind-ml/docs/models/staffing.md
Model Card: Staff Optimization
Purpose
Predict the required number of present staff for a department/branch/date (Reception, Kitchen, Housekeeping), to support shift-planning decisions.
⚠️ Trained entirely on synthetic data — see Known Assumptions below before using this model's output for any real decision.
Input Features
FEATURE_COLS (src/pipelines/staffing_pipeline.py):
| Feature | Source |
|---|---|
| month, quarter, day_of_week, is_weekend, is_holiday | src/features/calendar_features.py |
| scheduled_employees | direct input (planned headcount) |
| present_employees_lag_7, present_employees_rolling_mean_7 | src/features/time_series_features.py, grouped by department_id |
| department_name | ordinal-encoded categorical |
Target Variable
present_employees (int) — synthetic headcount, see below.
Algorithm
Single shared GradientBoostingRegressor (StaffingRegressionModel,
n_estimators=250, max_depth=4, learning_rate=0.05, random_state=42) across
all 3 departments, with department_name as an encoded feature — one model
rather than three, since departments share the same schema/grain (unlike
Restaurant's per-meal split).
OR-Tools shift scheduler (src/models/staffing/or_tools_scheduler.py)
remains an intentional NotImplementedError scaffold — this model predicts
a headcount, not a shift assignment; converting the two is explicitly
out of scope.
Training Dataset
data/features/staff_features.parquet — 4,815 rows (1,605 dates × 3
departments), generated by
src/pipelines/synthetic_data.py::generate_staffing_daily from
data/raw/staffing_daily_synthetic.csv. Chronological 80/20 train/test
split.
Evaluation Metrics
| Model | MAE | RMSE | MAPE |
|---|---|---|---|
| Regression | 0.73 | 0.86 | 6.5% |
Source: reports/latest_staffing.json. The low MAPE reflects the synthetic
generator's low intrinsic noise (headcounts are small integers with limited
variance), not real-world predictive strength.
Strengths
- Single shared model across departments is simpler to maintain than
per-department models, and the encoded
department_namefeature lets it still differentiate department-specific patterns. - Fast to train and predict (small feature set, small target range).
Limitations
- No real staff-attendance data exists anywhere in this project. Every value this model was trained on is fabricated.
required_staffis a single point estimate — no confidence interval is produced (unlike Occupancy's Prophet model).departmentis not validated against the known department list at the API layer — an unrecognized name silently encodes as-1viaOrdinalEncoder(handle_unknown="use_encoded_value")rather than raising a clear error (seedocs/api/staff.md).
Known Assumptions
- Synthetic generation formula:
present_employees ≈ ratio[department] * occupied_rooms * day_of_week_multiplier+ small integer noise, deterministic vianp.random.default_rng(seed=42)(seereports/final_phase4/known_limitations.mditems 1 and 10). - Department staffing ratios (
DEPARTMENT_STAFF_RATIOinsynthetic_data.py) are fixed constants, not derived from any real staffing policy.
Future Improvements
- Replace the synthetic seed with real staff-attendance data if a workforce management data source is ever integrated.
- Implement the OR-Tools scaffold to convert
required_staffcounts into actual shift assignments. - Add department-name validation at the API layer.