Wolfram Research

Job Training Efficacy

Source Notebook

Data examining the efficacy of job training programs on increasing earnings

Details

These are actual and augmented subsets of the well studied dataset developed by Professor Robert Lalonde for his paper “Evaluating the Econometric Evaluations of Training Programs,” American Economic Review, Vol. 76, pp. 604-620. The paper examined the effects of certain job training programs operated between 1975 and 1977 on the earnings of its participants. The sets were compiled by Professor Gary King as part of his "cem" package for the R language, which deals with "coarsened exact matching," a technique he developed for use in causal inference.
The order of the variables in the default Dataset from left to right is: treatment indicator (1 if treated, 0 if not treated), age, education, Black (1 if Black, 0 otherwise), married (1 if married, 0 otherwise), nodegree (1 if no degree, 0 otherwise), re74 (earnings in 1974), re75 (earnings in 1975), re78 (earnings in 1978), Hispanic (1 if Hispanic, 0 otherwise), u74 (1 if the person was unemployed in 1974, 0 otherwise) and u75 (1 if the person was unemployed in 1975 and 0 otherwise). The default Dataset contains 722 rows.
The "Lelonde" variant contains the same variables as the default but makes 10% of the data missing and adds a variable q1 which is the fictituous answer to the questionarie on “Agreement on this job training program.” Presumably this fictitious question can be used to study the effects of "collider bias," a problem that can arise in causal inference when variables effected by both the "treatment" and the "outcome" are controlled for.
The "MatchIt Lalonde" variant is taken from the MatchIt package available in the R language. It excluded the unemployment variables and contains only 614 rows. (The criteria on which certain rows were excluded from the original data is not clear). It also uses a single variable "race" to encode whether the person is Black, Hispanic or other (which is assumed to be White).
Also available is the "DW" (Dehejia-Wahba) 445-row subset of the data discussed in this paper: Rajeev Dehejia and Sadek Wahba, “Causal Effects in Non-Experimental Studies: Reevaluating the Evaluation of Training Programs,” Journal of the American Statistical Association, Vol. 94, No. 448 (December 1999), pp. 1053-1062. This paper claimed that "propensity scores" methods succeed in estimating the treatment impact of the the job training program studied originally by Professor Lalonde.

(12 columns, 722 rows)

Examples

Basic Examples (1) 

Retrieve the data:

In[1]:=
jobs = ResourceData[\!\(\*
TagBox["\"\<Job Training Efficacy\>\"",
#& ,
BoxID -> "ResourceTag-Job Training Efficacy-Input",
AutoDelete->True]\)]
Out[1]=

Scope & Additional Elements (3) 

Retrieve the "Lelonde" variant of the data:

In[2]:=
ResourceData[\!\(\*
TagBox["\"\<Job Training Efficacy\>\"",
#& ,
BoxID -> "ResourceTag-Job Training Efficacy-Input",
AutoDelete->True]\), "Lelonde"]
Out[2]=

Retrieve the "DW" variant of the data:

In[3]:=
ResourceData[\!\(\*
TagBox["\"\<Job Training Efficacy\>\"",
#& ,
BoxID -> "ResourceTag-Job Training Efficacy-Input",
AutoDelete->True]\), "DW"]
Out[3]=

Retrieve the "MatchIt Lalonde" variant:

In[4]:=
ResourceData[\!\(\*
TagBox["\"\<Job Training Efficacy\>\"",
#& ,
BoxID -> "ResourceTag-Job Training Efficacy-Input",
AutoDelete->True]\), "MatchIt Lalonde"]
Out[4]=

Visualizations (2) 

Show the distribution of wages based on race:

In[5]:=
ResourceFunction[
ResourceObject[
Association[
    "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator",
      "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
      "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(Switch[{#black, #hispanic}, {1,
        0}, "Black", {0, 1}, "Hispanic", _, "White or other"] &) -> (#re78 &), Histogram[#, PlotRange -> {{0, 35000}, All}, ImageSize -> 500] &][
 Normal@jobs]
Out[5]=

Show the distribution of wages based on treatment (without regard to covariates):

In[6]:=
KeySort@ResourceFunction[
ResourceObject[
Association[
     "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator", "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(If[#treated == 1, "treated", "not treated"] &) -> (#re78 &), Histogram[#, PlotRange -> {{0, 35000}, All}, ImageSize -> 500] &][
  Normal@jobs]
Out[6]=

Analysis (5) 

Perform a linear regression on the data to see what effect job training may have had:

In[7]:=
jobslmf = Query[LinearModelFit[#, {1, black, hispanic, nodegree, age, education, married, re74, re75, u74, u75, treatment}, {black, hispanic, nodegree, age, education, married, re74, re75, u74, u75, treatment}, NominalVariables -> {black, hispanic, nodegree, married, u74, u75, treatment}] &, KeyTake[{"black", "hispanic", "nodegree", "age", "education", "married", "re74", "re75", "u74", "u75", "treated", "re78"}] /* Values][jobs]
Out[7]=

Examine the parameters and the adjusted R2. It appears that this form of the model does not have much predictive value. Not receiving job training lowered wages by $824, but one can not be certain that the result is statistically significant:

In[8]:=
jobslmf[{"ParameterTable", "AdjustedRSquared"}]
Out[8]=

Perform a logistic regression to determine what factors affected whether one received "treatment" (i.e. was enrolled in the job training program):

In[9]:=
jobslomf = Query[LogitModelFit[#, {1, black, hispanic, nodegree, age, education,
       married, re74, re75, u74, u75}, {black, hispanic, nodegree, age, education, married, re74, re75, u74, u75}, NominalVariables -> {black, hispanic, nodegree, married, u74, u75}] &, KeyTake[{"black", "hispanic", "nodegree", "age", "education", "married", "re74", "re75", "u74", "u75", "treated"}] /* Values][
  jobs]
Out[9]=

The only statistically significant factor in determining treatment is the absence of a high school degree. The very low Cragg Uhler Pseudo R2 suggests that the model does not have much explanatory power:

In[10]:=
jobslomf[{"ParameterTable", "CraggUhlerPseudoRSquared"}]
Out[10]=

See if a probit model performs any better; it does not:

In[11]:=
(jobspromf = Query[ProbitModelFit[#, {1, black, hispanic, nodegree, age, education, married, re74, re75, u74, u75}, {black, hispanic, nodegree, age, education, married, re74, re75, u74, u75}, NominalVariables -> {black, hispanic, nodegree, married, u74, u75}] &, KeyTake[{"black", "hispanic", "nodegree", "age", "education", "married", "re74", "re75", "u74", "u75", "treated"}] /* Values][jobs])[{"ParameterTable", "CraggUhlerPseudoRSquared"}]
Out[11]=

Split the data into training and test set:

In[12]:=
{training, test} = TakeDrop[RandomSample[jobs], 500];

Run a classifier on the training set:

In[13]:=
cl = Query[Classify[#, TrainingProgressReporting -> None] &, KeyTake[{"black", "hispanic", "nodegree", "age", "education", "married", "re74", "re75", "u74", "u75", "treated"}] /* Values /* (Most@# -> Last@# &)][training]
Out[13]=

Create a classifier measurements object using the classifier just built and the test data:

In[14]:=
cmo = Query[ClassifierMeasurements[cl, #] &, KeyTake[{"black", "hispanic", "nodegree", "age", "education", "married", "re74", "re75", "u74", "u75", "treated"}] /* Values /* (Most@# -> Last@# &)][test]
Out[14]=

Assess classifier performance. The machine learning classifier does not perform particularly well:

In[15]:=
cmo[{"Accuracy", "CohenKappa", "AreaUnderROCCurve" -> 1}]
Out[15]=

A look at the probabilities of being treated shows the classifier is extremely uncertain in its results; the range of probabilities is extremely small:

In[16]:=
Histogram[cmo["Probabilities"]]
Out[16]=

Use the "MatchIt Lalonde" data and compare the mean values of various covariates among the treated and untreated groups:

In[17]:=
lalonde = ResourceData[\!\(\*
TagBox["\"\<Job Training Efficacy\>\"",
#& ,
BoxID -> "ResourceTag-Job Training Efficacy-Input",
AutoDelete->True]\), "MatchIt Lalonde"];
In[18]:=
ResourceFunction[
ResourceObject[
Association[
    "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator",
      "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
      "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(If[#treat == 1, "Treated", "Control"] &) -> KeyDrop[{"treat", "re78", "race"}], Merge[Mean /* N]][lalonde]
Out[18]=

Generate an anomaly detector function of the covariates of the treated population:

In[19]:=
adfTreated = Query[Select[#treat == 1 &] /* AnomalyDetection, KeyDrop[{"treat", "re78"}]][lalonde]
Out[19]=

Use the anomaly detector function on the untreated (control) population but set the AcceptanceThreshold to be extremely high so that most members are treated as anomalous, which will bring the number of persons in the remaining untreated population down to about the number of persons in the treated population:

In[20]:=
nonanomalousControl = Query[Select[#treat == 0 && Not[adfTreated[KeyDrop[#, {"treat", "re78"}], AcceptanceThreshold -> 0.175]] &]][lalonde] // ResourceFunction[
ResourceObject[
Association[
     "Name" -> "FormatDataset", "ShortName" -> "FormatDataset", "UUID" -> "76670bca-1587-4e7e-9e89-5b698a30759d", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Format a dataset using a given set of option values", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$66a3086203b4405b88cdb0de8a5c3128`FormatDataset", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/70389ad6-7dbc-48c8-b898-72c65c00f14e"]], ResourceSystemBase -> Automatic]][MaxItems -> 5]
Out[20]=

Join the treated persons with the non-anomalous members of the control group:

In[21]:=
matched = Join[Query[Select[#treat == 1 &]][lalonde], nonanomalousControl] // ResourceFunction[
ResourceObject[
Association[
     "Name" -> "FormatDataset", "ShortName" -> "FormatDataset", "UUID" -> "76670bca-1587-4e7e-9e89-5b698a30759d", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Format a dataset using a given set of option values", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$66a3086203b4405b88cdb0de8a5c3128`FormatDataset", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/70389ad6-7dbc-48c8-b898-72c65c00f14e"]], ResourceSystemBase -> Automatic]][MaxItems -> 5]
Out[21]=

And now compare the mean values of their covariates, which are now considerably more similar to each other:

In[22]:=
ResourceFunction[
ResourceObject[
Association[
    "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator",
      "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
      "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(If[#treat == 1, "Treated", "Control"] &) -> KeyDrop[{"treat", "re78", "race"}], Merge[Mean /* N]][matched]
Out[22]=

Compare the mean earnings in 1978 among the treated and untreated in the matched groups; the treated group has income about $800 higher even though their demographics are, on average, about the same:

In[23]:=
ResourceFunction[
ResourceObject[
Association[
    "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator",
      "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
      "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(If[#treat == 1, "treated", "control"] &) -> (#re78 &), Mean][matched]
Out[23]=

The increase in median income is much smaller, suggesting that the increased income among the treated may come for a few high earners:

In[24]:=
ResourceFunction[
ResourceObject[
Association[
    "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator",
      "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
      "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(If[#treat == 1, "treated", "control"] &) -> (#re78 &), Median][matched]
Out[24]=

Compare the mean and median income in 1978 by race among the matched individuals:

In[25]:=
ResourceFunction[
ResourceObject[
Association[
    "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator",
      "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
      "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(#race &) -> (#re78 &), Association["mean" -> Mean[#], "median" -> Median[#]] &][matched]
Out[25]=

The preceding example established what was "normal" by looking at the treatment data and then eliminated rows of the control data that looked anomalous by that standard. This use of the treatment data as the baseline is standard in the literature, but perhaps somewhat arbitrary. An alternative approach would be to take only treatment data that was "normal" by looking at the control data and taking only control data that was "normal" by looking at the treatment data. Such a method could use symmetric "acceptance thresholds." Compute an anomaly detection function on the control data:

In[26]:=
adfControl = Query[Select[#treat == 0 &] /* AnomalyDetection, KeyDrop[{"treat", "re78"}]][lalonde]
Out[26]=
In[27]:=
nonanomalousTreated = Query[Select[#treat == 1 && Not[adfControl[KeyDrop[#, {"treat", "re78"}], AcceptanceThreshold -> 0.1]] &]][lalonde] // ResourceFunction[
ResourceObject[
Association[
     "Name" -> "FormatDataset", "ShortName" -> "FormatDataset", "UUID" -> "76670bca-1587-4e7e-9e89-5b698a30759d", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Format a dataset using a given set of option values", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$66a3086203b4405b88cdb0de8a5c3128`FormatDataset", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/70389ad6-7dbc-48c8-b898-72c65c00f14e"]], ResourceSystemBase -> Automatic]][MaxItems -> 5]
Out[27]=
In[28]:=
nonanomalousControl2 = Query[Select[#treat == 0 && Not[adfTreated[KeyDrop[#, {"treat", "re78"}], AcceptanceThreshold -> 0.1]] &]][lalonde] // ResourceFunction[
ResourceObject[
Association[
     "Name" -> "FormatDataset", "ShortName" -> "FormatDataset", "UUID" -> "76670bca-1587-4e7e-9e89-5b698a30759d", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Format a dataset using a given set of option values", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$66a3086203b4405b88cdb0de8a5c3128`FormatDataset", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/70389ad6-7dbc-48c8-b898-72c65c00f14e"]], ResourceSystemBase -> Automatic]][MaxItems -> 5]
Out[28]=
In[29]:=
matched2 = Join[nonanomalousTreated, nonanomalousControl2] // ResourceFunction[
ResourceObject[
Association[
     "Name" -> "FormatDataset", "ShortName" -> "FormatDataset", "UUID" -> "76670bca-1587-4e7e-9e89-5b698a30759d", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Format a dataset using a given set of option values", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$66a3086203b4405b88cdb0de8a5c3128`FormatDataset", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/70389ad6-7dbc-48c8-b898-72c65c00f14e"]], ResourceSystemBase -> Automatic]][MaxItems -> 5]
Out[29]=

Use of this alternative matching method results in the average treatment effect being smaller than in the prior example:

In[30]:=
ResourceFunction[
ResourceObject[
Association[
    "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator",
      "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
      "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(If[#treat == 1, "treated", "control"] &) -> (#re78 &), Mean][matched2]
Out[30]=

The increase in median income caused by the treatment appears more robust against changes in the matching methodology:

In[31]:=
ResourceFunction[
ResourceObject[
Association[
    "Name" -> "MapReduceOperator", "ShortName" -> "MapReduceOperator",
      "UUID" -> "856f4937-9a4c-44a9-88ae-cfc2efd4698f", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Like an operator form of GroupBy, but where one also specifies a reducer function to be applied", "RepositoryLocation" -> URL[
      "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$ad7fe533436b4f8294edfa758a34ac26`MapReduceOperator", "FunctionLocation" -> CloudObject[
      "https://www.wolframcloud.com/obj/6d981522-1eb3-4b54-84f6-55667fb2e236"]], ResourceSystemBase -> Automatic]][(If[#treat == 1, "treated", "control"] &) -> (#re78 &), Median][matched2]
Out[31]=

Make a distribution chart showing the difference in the distribution of incomes between those not receiving job training and those doing so. The chart suggests that the gains in earnings come for a few people in the treated group earning what were high amounts of money:

In[32]:=
DistributionChart[
 Normal[Query[
    GroupBy[If[#treat == 1, "treated", "control"] &] /* KeyTake[{"control", "treated"}], All, #re78 &][matched2]], ChartLegends -> {"control", "treated"}, ChartStyle -> {Red, Blue}, PlotLabel -> "Distribution of 1978 earnings\namong treated and untreated groups after matching"]
Out[32]=

The "fundamental problem of causal inference" (https://en.wikipedia.org/wiki/Rubin_causal_model) is said to be that we can observe only the treated outcome or the untreated outcome on the individual, but not both. The problem is essentially one of missing data. But Wolfram Language can impute missing values, which, as described in these lecture notes and this journal article, suggests a direct approach to causal inference. First, create a new dataset with missings:

In[33]:=
lalondeCounterfactual = Query[RandomSample, Association[KeyDrop[#, {"treat", "re78"}], "treatedRE78" -> If[#treat == 1, #re78, Missing[]], "untreatedRE78" -> If[#treat == 0, #re78, Missing[]]] &][
   lalonde] // ResourceFunction[
ResourceObject[
Association[
     "Name" -> "FormatDataset", "ShortName" -> "FormatDataset", "UUID" -> "76670bca-1587-4e7e-9e89-5b698a30759d", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Format a dataset using a given set of option values", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$66a3086203b4405b88cdb0de8a5c3128`FormatDataset", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/70389ad6-7dbc-48c8-b898-72c65c00f14e"]], ResourceSystemBase -> Automatic]][MaxItems -> 5]
Out[33]=

Then use SynthesizeMissingValues to "solve" the fundamental problem of causal inference:

In[34]:=
lalondeSynthesized = Query[SynthesizeMissingValues][lalondeCounterfactual] // ResourceFunction[
ResourceObject[
Association[
     "Name" -> "FormatDataset", "ShortName" -> "FormatDataset", "UUID" -> "76670bca-1587-4e7e-9e89-5b698a30759d", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Format a dataset using a given set of option values", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$66a3086203b4405b88cdb0de8a5c3128`FormatDataset", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/70389ad6-7dbc-48c8-b898-72c65c00f14e"]], ResourceSystemBase -> Automatic]][MaxItems -> 10]
Out[34]=

Now find the mean earnings when the population is "treated" with job training and when it is not. This method suggests that job training causes a loss of earnings rather than the generally accepted notion that it results in a gain. This finding suggests that direct use of missing value imputation must be explored further before it is used as an accepted algorithm for making causal inferences:

In[35]:=
Normal@Query[Mean, {#treatedRE78, #untreatedRE78} &][
  lalondeSynthesized]
Out[35]=

We can see if using RandomSampling as the EvaluationStrategy instead of the default ModeFinding helps:

In[36]:=
lalondeSynthesizedMultiple = Join @@ Table[
    Query[SynthesizeMissingValues[#, TrainingProgressReporting -> None, Method -> <|"LearningMethod" -> "Multinormal", "EvaluationStrategy" -> "RandomSampling"|>] &][
     lalondeCounterfactual], 20] // ResourceFunction[
ResourceObject[
Association[
     "Name" -> "FormatDataset", "ShortName" -> "FormatDataset", "UUID" -> "76670bca-1587-4e7e-9e89-5b698a30759d", "ResourceType" -> "Function", "Version" -> "1.0.0", "Description" -> "Format a dataset using a given set of option values", "RepositoryLocation" -> URL[
       "https://www.wolframcloud.com/obj/resourcesystem/api/1.0"], "SymbolName" -> "FunctionRepository`$66a3086203b4405b88cdb0de8a5c3128`FormatDataset", "FunctionLocation" -> CloudObject[
       "https://www.wolframcloud.com/obj/70389ad6-7dbc-48c8-b898-72c65c00f14e"]], ResourceSystemBase -> Automatic]][MaxItems -> 10]
Out[36]=

We fine that it does not solve the problem. So, missing value imputation, although theoretically promising as a vehicle for causal inference, has tricky and presently unresolved implementation issues:

In[37]:=
Normal@Query[Mean, {#treatedRE78, #untreatedRE78} &][
  lalondeSynthesizedMultiple]
Out[37]=

Seth J. Chandler, "Job Training Efficacy" from the Wolfram Data Repository (2021) 

License Information

Used with consent of Professor Gary King

Data Resource History

Source Metadata

Publisher Information