Skip to content

HR

A workforce-analytics warehouse for a mid-size multinational. Employees belong to departments, work out of regional offices, hold a job level that changes over time (SCD2), and are assigned to one or more projects through a many-to-many bridge. Performance reviews carry trajectory-aligned narrative text; compensation changes get a CDC audit trail; review-season, fiscal-close, and summer dips drive the seasonality channel.

What it produces

Table Type Grain Rows (default) Notes
dim_date dim per_period 24 24 monthly periods, Jan 2023 – Dec 2024
dim_employee dim per_entity (+ SCD2 + geo) 104–416 104 employees, expanded by SCD2 job-level moves
dim_manager dim per_reference 5 manager pool with span sizes 5..15
dim_project dim per_reference 6 project name, type, budget band
fct_performance fact per_entity_per_period 2,496 performance, engagement, training + bucket + narrative
fct_compensation fact per_entity_per_period (CDC) 2,496 comp + attrition risk; CDC for restated comp
fct_attendance fact per_entity_per_period 2,496 absence rate only
evt_training_completion event variable (proportional) ~10,000 scaled 4× engagement
evt_attrition event variable (threshold) 5–25 attrition_risk above 0.7 for 3 consecutive periods
bridge_employee_project bridge static M:N 100–400 each employee assigned to 1–4 projects

104 entities across six segments: new_hire_ramp (18), top_performer (22), core_team (28), disengaging (16), burnout_cohort (10), comeback (10).

Features exercised

  • Noise presetslightly_messy (sigma 0.03, outliers 0.01, MCAR 0.005).
  • Seasonality — annual review cycle Mar–Apr (+0.20), fiscal-year close Nov–Dec (+0.15), summer dip Jul–Aug (-0.20).
  • Causal lagabsence_rate follows engagement with delay 1.
  • Correlationsengagement driven_by performance_score, engagement opposes attrition_risk, absence_rate related attrition_risk, plus explicit compensation 0.35 performance_score.
  • SCD2job_level on dim_employee tracking performance_score with thresholds [0.30, 0.60, 0.85] (ic / senior / lead / principal).
  • Bridgebridge_employee_project (M:N), trajectory-driven by performance_score. Higher performers carry more concurrent projects.
  • Manager hierarchy via reference dimdim_manager is a static lookup (self-FK bridges are not supported; the reference-dim approach is the canonical hierarchy idiom).
  • CDCfct_compensation carries audit columns for restated compensation cycles.
  • Geo bundleoffice_country, office_country_code, office_region, office_city on dim_employee.
  • Bucket columnreview_outcome on fct_performance with four bands: improvement_plan / meets / exceeds / top_talent.
  • Narrative columnreview_notes on fct_performance with per-segment lexicons.
  • Multi-phase archetypesflat > growth @ 6, flat > decline @ 12, growth > spike_then_crash > flat @ 6 @ 14, decline > flat > growth @ 6 @ 14.
  • Lifecycle stagesnew_hire / established / disengaging / exited on attrition_risk with enforce_order: true (monotonic cursor, no jumping back).
  • Quality injection — null injection on engagement (4%), late arrivals on attendance (2%), duplicate rows on training events (1%).
  • Events — one proportional (evt_training_completion) and one threshold (evt_attrition).

Use it

from plotsim import load_template, generate_tables, write_tables

config = load_template("hr")
tables = generate_tables(config)
write_tables(tables, config, output_dir="./output")
plotsim template hr -o hr.yaml
plotsim run hr.yaml -o ./output

Common customizations

Tighten the noise preset

Default is slightly_messy. To produce cleaner data for a teaching demo:

from plotsim.types import NoiseConfig

config = load_template("hr")
config = config.model_copy(update={"noise": NoiseConfig()})  # zero noise
noise: clean   # preset shorthand

Loosen the lifecycle monotonicity

Default enforce_order: true means an employee that hits disengaging can't slide back to established. To allow stateless free-mode (each period independently picks the highest band satisfied):

# Re-author with create() — lifecycle is immutable on the loaded config.
lifecycle={
    "track": "attrition_risk",
    "enforce_order": False,                # changed from True
    "stages": [
        {"new_hire":     0.0},
        {"established":  0.15},
        {"disengaging":  0.4},
        {"exited":       0.7},
    ],
}
lifecycle:
  track: attrition_risk
  enforce_order: false   # changed from true
  stages:
    - { new_hire:    0.0 }
    - { established: 0.15 }
    - { disengaging: 0.4 }
    - { exited:      0.7 }

To allow hysteretic demotion under enforce_order (cursor moves back after N consecutive sub-threshold periods), add downgrade_delay: N.

Add entity features for an attrition model

from plotsim.types import EntityFeaturesConfig

config = config.model_copy(update={
    "entity_features": EntityFeaturesConfig(enabled=True),
})
entity_features: true

The output adds _entity_features.csv — one row per employee with per-metric aggregates, ready for a flat-features ML pipeline.

Where it shines

  • Attrition-prediction training pipelines — attrition_risk and the evt_attrition table provide a target and an event-rate signal; the entity-features companion compresses the time series into per-employee rows.
  • Performance-review NLP — review_notes carries archetype-driven text suitable for classifying narrative tone against performance level.
  • Manager-span analysis — dim_manager and the project bridge exercise SQL joins that mix reference dims, sub-entity grouping, and many-to-many.