SPB Git

spb/wp29_uqo

Public

# F6-conf-04 — Does workforce training raise productivity growth? A mini-project on simulated firm data

# Scientific question

A one-year training programme offered firms a variable number of training hours per employee. Using the simulated firm-level data provided, estimate the effect of one additional training hour per employee on productivity growth in the following year. The estimand is the causal effect of training_hours on productivity_growth (percentage points per hour) under a selection-on-observables assumption: conditional on the pre-programme information in the file, training hours are as good as randomly assigned, and the effect is linear and homogeneous. Report both the unadjusted association (the required baseline) and the estimate that targets the estimand, with uncertainty. The chain required is: question → data audit → baseline → justified method → results → uncertainty → failure discussion → one figure and one table → short report.

# Inputs

  • data/firms.csv — simulated data (generated for this exercise; it is not observed data). One row per firm. Columns: firm_id (integer), skill_index (standardised index of the workforce's baseline skill, measured in the year before the programme), training_hours (hours of training per employee received during the programme year), productivity_growth (percent change in labour productivity in the year after the programme year). Rows are in random order.

# Information-availability rules

  • Use only data/firms.csv. No other variables exist; nothing needs to be imputed.
  • The "unadjusted" model is the OLS regression of productivity_growth on training_hours with an intercept only.

# Required outputs

  • out/results.json with the keys:
key type unit meaning
naive_estimate number pp per hour OLS slope of the unadjusted model (baseline)
naive_ci_low, naive_ci_high number pp per hour 95% confidence interval for the unadjusted slope
baseline_metric number percentage points RMSE of the unadjusted model's residuals, sqrt(mean(e²))
model_metric number percentage points RMSE of the residuals of the model that targets the estimand
effect_estimate number pp per hour estimate of the estimand (coefficient on training_hours in the model you justify)
effect_se number pp per hour its standard error (conventional or heteroskedasticity-robust; state which)
effect_ci_low, effect_ci_high number pp per hour 95% confidence interval for the effect
skill_index_coefficient number or null pp per index unit coefficient on skill_index in the model that targets the estimand (null if that model does not include it)
conclusion string — one of the codes below, based on effect_ci_low/effect_ci_high
task_id string copied from this file
feasibility string complete / partial / infeasible
feasibility_reason string or null why, if not complete
data_issues array of {"issue","handling"} every data problem detected and how it was handled (empty list allowed)
n_input_rows object {file: integer} rows read per input file (file name as key, header row excluded)
n_analysed integer or null observations actually used in the main estimate
random_seed integer or null seed used, if any

The conclusion code must be one of: positive_effect (the 95% interval for the effect estimand lies above zero), negative_effect (lies below zero), no_detectable_effect (the interval / test does not reject zero at the 5% level), premise_invalid (the data provided do not allow the stated estimand to be computed — in that case set feasibility to infeasible, explain in feasibility_reason, and set every inestimable key to null; never substitute an invented or proxy estimate).

  • out/figure1.png — scatter of productivity_growth against training_hours with the unadjusted fitted line and the fitted line of the model that targets the estimand (evaluated at the mean of any other regressor), produced by out/analysis.py with matplotlib.
  • out/table1.csv — one row per model with columns model, estimate, se, ci_low, ci_high (header + at least two data rows), produced by out/analysis.py.
  • out/report.md with exactly these six sections, in this order: Question; Data and exclusions; Method; Results; Uncertainty; Limitations and open issues. The last section must contain a genuine failure discussion: what would invalidate the result, what was not checked, and what the main threats are.
  • out/analysis.py — replication entry point: ~/wp29/venv/bin/python out/analysis.py, run from the workspace root, must regenerate out/results.json, out/figure1.png and out/table1.csv from data/ only, deterministically (fix any seed; use the matplotlib Agg backend).
  • out/claims.json — list of the central claims of the report: [{"id": "C1", "text": "...", "results_keys": ["..."], "status": "supported"|"unsupported"|"not_evaluated", "caveat": "..."}]. Every claim must reference existing keys of out/results.json; a claim must only be marked supported if the reported numbers actually support it.

# Constraints

  • No network access. Python: ~/wp29/venv/bin/python (numpy, pandas, scipy, statsmodels, matplotlib available). Do not modify data/.
  • Write all deliverables under out/. This task is designed to be completed in well under 20 minutes of work; prefer a short, correct chain over an elaborate one.
Files · main
Menu

Theme