Step 6: Primary Education Fate Refined Model
Model Description
At this step we add the refined “fate” module for school outcome. It adjusts the overall probabilities from the base module to progress between levels by a set of relative factors (odds ratios) by individual characteristics. In the current implementation parent’s education is introduced as relative factor. The added module allows to easily expand the list of distinguished population groups. Users are also given a choice if and how the refined model is used. One option is calibrating the aggregate outcomes by sex and district to the base model for all years of birth. The second option calibrates the model just for one birth cohort from which onward the refined module is used. All simulated future trends are then driven entirely by composition effects.
The new EducRefinedFate.mpp Module
This module allows to add relative factors (odds ratios) to the modeling of education outcomes. Currently mother’s education by sex is introduced. The relative factors are used to modify the base probabilities of school progressions. Aggregated outcomes by sex are calibrated in order to match the base model’s outcome. This calibration is performed either (1) for all years of birth, or (2) just once. In the first case, aggregate outcomes by sex are identical, but the model picks different children entering and graduating school, accounting for the relative differences (odds ratios) by mother’s education. In the second case, the calibration is done for a selected year of birth. For all following cohorts, parameters are frozen and all trends result from composition effects due to the changing educational composition of mothers. The module only affects persons born in the simulation as mother’s education is unknown for others.
Parameters:
Model selection: the 3 choices are (1) Use Base Model, (2) Use the refined model calibrated for all years of birth, (3) Use the refined model calibrated once.
Odds for progressing from low to medium by parents education
Odds for progressing from medium to high education by parents education
The first birth cohort from which onwards the refined model is used, which can be any year beginning from the starting year of the simulation.
The module can be added or removed from the model without requiring modification in other modules. The only exception is the requirement to initialize parents’ education in the Start() function in PersonCore.mpp.
In its core, this module consists of two functions.
The function SetEduc1AdjustmentFactors() calculates the required adjustment factors (in the form of log odds by district and sex) to be applied in addition to the relative factors in order to get probabilities further broken down by mother’s education. The calculations are performed once at the end of each relevant calendar year as at this point in time the educational composition of mothers of all born in this year is known. Adjustment factors are either calculated once or updated each year according to the user’s model selection.
The function AdjustEducOne() applies the adjustments at the Person level at the first end of the year after birth. Thereby this module modifies the three “fate” states introduced in the base model, namely the two individual progression probabilities educ_prob1 and educ_prob2, as well as random outcome educ_fate based on the new probabilities.
How to change or expand the list of relative factors
The list of population groups is declared in the classification educ_group. The classification can be changed or expanded whereby a person can belong only to one of the groups. (For example, when adding an additional dimension ethnicity, all combinations of ethnicity and mother’s education have to be listed)
The individual group membership is a derived state educ_group. When changing the levels of educ_group, the calculation of the derived state has to be updated accordingly.
No other code changes are required
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets
////////////////////////////////////////////////////////////////////////////////////////////////////
actor_set Person asSimBornAge0[sex][educ_group]
filter is_alive && person_type == PT_CHILD && integer_age == 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
classification EDUC_GROUP //EN Population Groups
{
EG_00, //EN Parents education low
EG_01, //EN Parents education medium
EG_02 //EN Parents education high
};
classification EDUC_MODEL //EN Education Model Selection
{
EM_BASE, //EN Use base model
EM_REFINED_ALIGNALL, //EN Use refined model aligned to base for all birth cohorts
EM_REFINED_ALIGNONCE //EN Use refined model aligned to base once
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
double EducProg1Odds[EDUC_GROUP][SEX]; //EN Odds of progressing low->medium
double EducProg2Odds[EDUC_GROUP][SEX]; //EN Odds of progressing medium->high
EDUC_MODEL EducModel; //EN Model Selection
int EducFirstCohortRefinedModel; //EN First birth cohort to apply refined model
};
parameter_group PG_EducRefinedFate //EN Education Fate - Refined Model
{
EducProg1Odds, EducProg2Odds,
EducFirstCohortRefinedModel
};
parameter_group PG_SchoolFate //EN Education Fate
{
EducModel,
PG_EducBaseFate,
PG_EducRefinedFate
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Globals //EN Actor Globals
{
double alignment_educ_medium[SEX]; //EN Education alignment low to medium
double alignment_educ_high[SEX]; //EN Education alignment medium to high
};
actor Clock
{
void SetEducAdjustmentFactors(); //EN Function calculating calibration factors
hook SetEducAdjustmentFactors, EndYearClock; //EN Hook to clock yearend
double AdjustedProbability(double dProb, double dLogOddEduc, double dLogOddAdjust);
};
actor Person
{
EDUC_LEVEL3 educ_parents = { EL3_LOW }; //EN Parents education
logical educ_parents_is_known = { FALSE }; //EN Parents education is known
//EN Education risk group
EDUC_GROUP educ_group = (educ_parents == EL3_LOW) ? EG_00 :
(educ_parents == EL3_MEDIUM) ? EG_01 : EG_02;
void AdjustEduc(); hook AdjustEduc, YearEnd;
void SetEducParents(); hook SetEducParents, SetAliveEvent;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::SetEducParents()
{
EDUC_LEVEL3 cEducParents = EL3_LOW;
if (lParent)
{
cEducParents = lParent->educ_fate;
educ_parents_is_known = TRUE;
if (lParent->lSpouse && lParent->lSpouse->educ_fate > lParent->educ_fate)
{
cEducParents = lParent->lSpouse->educ_fate;
}
}
educ_parents = cEducParents;
}
void Clock::SetEducAdjustmentFactors()
{
if (clock_year >= MIN(SIM_YEAR_RANGE) && (
(EducModel == EM_REFINED_ALIGNALL && clock_year >= EducFirstCohortRefinedModel) ||
(EducModel == EM_REFINED_ALIGNONCE && clock_year == EducFirstCohortRefinedModel)))
{
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
// calculate total population for given sex
double nTotalPop = 0.0;
for (int dGroup = 0; dGroup < SIZE(EDUC_GROUP); dGroup++)
{
nTotalPop = nTotalPop + asSimBornAge0[nSex][dGroup]->Count();
}
// Run Adjustment for school entry
double dFactorEntry = 0.0;
if (nTotalPop > 0.0)
{
int nIterations = 0;
double dResultProb = 10.0;
double dTargetProb = EducProg1[nSex][RANGE_POS(YOB_EDUC_PROG1, clock_year)];
double dLower = -10.0;
double dUpper = 10.0;
double dCenter = 0.0;
while (abs(dResultProb - dTargetProb) > 0.0001 && nIterations < 10000)
{
nIterations++;
dCenter = (dLower + dUpper) / 2.0;
dResultProb = 0.0;
for (int nGroup = 0; nGroup < SIZE(EDUC_GROUP); nGroup++)
{
dResultProb = dResultProb + (asSimBornAge0[nSex][nGroup]->Count() / nTotalPop) *
AdjustedProbability(dTargetProb, log(EducProg1Odds[nGroup][nSex]), dCenter);
}
if (dTargetProb > dResultProb) dLower = dCenter;
else dUpper = dCenter;
}
dFactorEntry = dCenter;
}
// set factor
asGlobals->Item(0)->alignment_educ_medium[nSex] = dFactorEntry;
// Run Adjustment for medium high progression
double dFactorGrad = 0.0;
if (nTotalPop > 0.0)
{
int nIterations = 0;
double dResultProb = 10.0;
double dTargetProb = EducProg2[nSex][RANGE_POS(YOB_EDUC_PROG2, clock_year)];
double dLower = -10.0;
double dUpper = 10.0;
double dCenter = 0.0;
while (abs(dResultProb - dTargetProb) > 0.0001 && nIterations < 10000)
{
nIterations++;
dCenter = (dLower + dUpper) / 2.0;
dResultProb = 0.0;
for (int nGroup = 0; nGroup < SIZE(EDUC_GROUP); nGroup++)
{
dResultProb = dResultProb + (asSimBornAge0[nSex][nGroup]->Count() / nTotalPop) *
AdjustedProbability(dTargetProb, log(EducProg2Odds[nGroup][nSex]), dCenter);
}
if (dTargetProb > dResultProb) dLower = dCenter;
else dUpper = dCenter;
}
dFactorGrad = dCenter;
}
// set factor
asGlobals->Item(0)->alignment_educ_high[nSex] = dFactorGrad;
}
}
}
double Clock::AdjustedProbability(double dProb, double dLogOddEduc, double dLogOddAdjust)
{
if (dProb >= 0.9999) dProb = 0.9999;
double dExp = exp(log(dProb / (1 - dProb)) + dLogOddEduc + dLogOddAdjust);
return dExp / (1 + dExp);
}
void Person::AdjustEduc()
{
// Adjustment for selection of refined model aligned to base for all birth cohorts
if (person_type == PT_CHILD && integer_age == 0 && calendar_year >= MIN(SIM_YEAR_RANGE) &&
calendar_year >= EducFirstCohortRefinedModel && EducModel == EM_REFINED_ALIGNALL)
{
educ_prob1 = lClock->AdjustedProbability(
educ_prob1,
log(EducProg1Odds[educ_group][sex]),
asGlobals->Item(0)->alignment_educ_medium[sex]);
educ_prob2 = lClock->AdjustedProbability(
educ_prob2,
log(EducProg2Odds[educ_group][sex]),
asGlobals->Item(0)->alignment_educ_high[sex]);
EDUC_LEVEL3 eolFate = EL3_LOW;
if (RandUniform(10) < educ_prob1) eolFate = EL3_MEDIUM;
if (eolFate == EL3_MEDIUM && RandUniform(11) < educ_prob2) eolFate = EL3_HIGH;
educ_fate = eolFate;
}
else if (person_type == PT_CHILD && integer_age == 0 && calendar_year >= MIN(SIM_YEAR_RANGE) &&
calendar_year >= EducFirstCohortRefinedModel && EducModel == EM_REFINED_ALIGNONCE)
{
educ_prob1 = lClock->AdjustedProbability(
EducProg1[sex][RANGE_POS(YOB_EDUC_PROG1, EducFirstCohortRefinedModel)],
log(EducProg1Odds[educ_group][sex]),
asGlobals->Item(0)->alignment_educ_medium[sex]);
educ_prob2 = lClock->AdjustedProbability(
EducProg2[sex][RANGE_POS(YOB_EDUC_PROG2, EducFirstCohortRefinedModel)],
log(EducProg2Odds[educ_group][sex]),
asGlobals->Item(0)->alignment_educ_high[sex]);
EDUC_LEVEL3 eolFate = EL3_LOW;
if (RandUniform(12) < educ_prob1) eolFate = EL3_MEDIUM;
if (eolFate == EL3_MEDIUM && RandUniform(13) < educ_prob2) eolFate = EL3_HIGH;
educ_fate = eolFate;
}
}
table Person PareducTabTest //EN Parents education
{ {
value_out(educ_parents_is_known) / unit
}
*year_of_birth
};