diff --git a/input/reg_eq5d.xlsx b/input/reg_eq5d.xlsx new file mode 100644 index 000000000..744768a83 Binary files /dev/null and b/input/reg_eq5d.xlsx differ diff --git a/src/main/java/simpaths/data/ManagerRegressions.java b/src/main/java/simpaths/data/ManagerRegressions.java index 327ca403d..b0ee9ee3e 100644 --- a/src/main/java/simpaths/data/ManagerRegressions.java +++ b/src/main/java/simpaths/data/ManagerRegressions.java @@ -64,6 +64,9 @@ public static LinearRegression getLinearRegression(RegressionName regression) { case LifeSatisfaction2Females -> { return Parameters.getRegLifeSatisfaction2Females(); } + case HealthEQ5D -> { + return Parameters.getRegEQ5D(); + } case SocialCareS1b -> { return Parameters.getRegCareHoursS1b(); } diff --git a/src/main/java/simpaths/data/Parameters.java b/src/main/java/simpaths/data/Parameters.java index 0cf1db62c..fb7f40eb8 100644 --- a/src/main/java/simpaths/data/Parameters.java +++ b/src/main/java/simpaths/data/Parameters.java @@ -334,6 +334,8 @@ else if(numberOfChildren <= 5) { public static boolean saveImperfectTaxDBMatches = false; public static final int IMPERFECT_THRESHOLD = 5999; + public static String eq5dConversionParameters = "lawrence"; + /////////////////////////////////////////////////////////////////// INITIALISATION OF DATA STRUCTURES ////////////////////////////////// public static Map EUROMODpolicySchedule = new TreeMap(); @@ -472,6 +474,7 @@ else if(numberOfChildren <= 5) { private static MultiKeyCoefficientMap coeffCovarianceDLS2Males; private static MultiKeyCoefficientMap coeffCovarianceDLS2Females; + private static MultiKeyCoefficientMap coeffCovarianceEQ5D; //Education private static MultiKeyCoefficientMap coeffCovarianceEducationE1a; @@ -707,6 +710,8 @@ else if(numberOfChildren <= 5) { private static LinearRegression regLifeSatisfaction2Males; private static LinearRegression regLifeSatisfaction2Females; + private static LinearRegression regHealthEQ5D; + //Education private static BinomialRegression regEducationE1a; private static BinomialRegression regEducationE1b; @@ -972,6 +977,7 @@ public static void loadParameters(Country country, int maxAgeModel, boolean enab int columnsLifeSatisfaction1 = -1; int columnsLifeSatisfaction2Males = -1; int columnsLifeSatisfaction2Females = -1; + int columnsHealthEQ5D = -1; int columnsSocialCareS1a = -1; int columnsSocialCareS1b = -1; int columnsSocialCareS2a = -1; @@ -1132,6 +1138,7 @@ else if(country.equals(Country.UK)) { columnsLifeSatisfaction1 = 30; columnsLifeSatisfaction2Males = 9; columnsLifeSatisfaction2Females = 9; + columnsHealthEQ5D = 8; columnsSocialCareS1a = 17; columnsSocialCareS1b = 18; columnsSocialCareS2a = 32; @@ -1351,6 +1358,8 @@ else if(country.equals(Country.UK)) { coeffCovarianceDLS2Males = ExcelAssistant.loadCoefficientMap("input/reg_health_wellbeing.xlsx", countryString + "_DLS2_Males", 1, columnsLifeSatisfaction2Males); coeffCovarianceDLS2Females = ExcelAssistant.loadCoefficientMap("input/reg_health_wellbeing.xlsx", countryString + "_DLS2_Females", 1, columnsLifeSatisfaction2Females); + loadEQ5DParameters(countryString, columnsHealthEQ5D); + //Life satisfaction // coeffCovarianceDLS1 = ExcelAssistant.loadCoefficientMap("input/reg_lifesatisfaction.xlsx", countryString + "_DLS1", 1, columnsLifeSatisfaction1); @@ -1570,7 +1579,6 @@ else if (country.equals(Country.IT)) { regLifeSatisfaction2Males = new LinearRegression(coeffCovarianceDLS2Males); regLifeSatisfaction2Females = new LinearRegression(coeffCovarianceDLS2Females); - //Education regEducationE1a = new BinomialRegression(RegressionType.Probit, Indicator.class, coeffCovarianceEducationE1a); regEducationE1b = new BinomialRegression(RegressionType.Probit, Indicator.class, coeffCovarianceEducationE1b); regEducationE2a = new OrderedRegression<>(RegressionType.GenOrderedLogit, Education.class, coeffCovarianceEducationE2a); @@ -2075,6 +2083,8 @@ public static void setEmploymentsFurloughedFlex(MultiKeyCoefficientMap employmen public static BinomialRegression getRegEducationE1b() {return regEducationE1b;} public static OrderedRegression getRegEducationE2a() {return regEducationE2a;} + public static LinearRegression getRegEQ5D() { return regHealthEQ5D; }; + public static BinomialRegression getRegPartnershipU1a() {return regPartnershipU1a;} public static BinomialRegression getRegPartnershipU1b() {return regPartnershipU1b;} public static BinomialRegression getRegPartnershipU2b() {return regPartnershipU2b;} @@ -3331,4 +3341,12 @@ private static void safeDelete(String filePath) { throw e; } } + + public static void loadEQ5DParameters(String countryString, int columnsHealthEQ5D) { + + coeffCovarianceEQ5D = ExcelAssistant.loadCoefficientMap("input/reg_eq5d.xlsx", countryString + "_EQ5D_" + eq5dConversionParameters, 1, columnsHealthEQ5D); + + regHealthEQ5D = new LinearRegression(coeffCovarianceEQ5D); + + } } \ No newline at end of file diff --git a/src/main/java/simpaths/data/RegressionName.java b/src/main/java/simpaths/data/RegressionName.java index 07b9e2a1d..3d1605086 100644 --- a/src/main/java/simpaths/data/RegressionName.java +++ b/src/main/java/simpaths/data/RegressionName.java @@ -41,6 +41,8 @@ public enum RegressionName { LifeSatisfaction2Males(RegressionType.Linear), LifeSatisfaction2Females(RegressionType.Linear), + HealthEQ5D(RegressionType.Linear), + RMSE(RegressionType.Linear), SocialCareS1a(RegressionType.Probit), diff --git a/src/main/java/simpaths/model/Person.java b/src/main/java/simpaths/model/Person.java index 1a88bcba0..995f32883 100644 --- a/src/main/java/simpaths/model/Person.java +++ b/src/main/java/simpaths/model/Person.java @@ -135,6 +135,9 @@ public class Person implements EventListener, IDoubleSource, IIntSource, Weight, private Integer dls; //life satisfaction - score 1-7 @Transient private Double dls_temp; @Transient private Integer dls_lag1; //life satisfaction - score 1-7 lag 1 + @Column(name="he_eq5d") + private Double he_eq5d; + @Column(name="dhh_owned") private Boolean dhhOwned; // Person is a homeowner, true / false @Transient private Boolean receivesBenefitsFlag_L1; // Lag(1) of whether person receives benefits @Transient private Boolean receivesBenefitsFlag; // Does person receive benefits @@ -667,6 +670,7 @@ public enum Processes { Fertility, GiveBirth, Health, + HealthEQ5D, HealthMentalHM1, //Predict level of mental health on the GHQ-12 Likert scale (Step 1) HealthMentalHM2, //Modify the prediction from Step 1 by applying increments / decrements for exposure HealthMentalHM1HM2Cases, //Case-based prediction for psychological distress, Steps 1 and 2 together @@ -756,6 +760,9 @@ public void onEvent(Enum type) { case HealthMentalHM1HM2Cases -> { healthMentalHM1HM2Cases(); } + case HealthEQ5D -> { + healthEQ5D(); + } case InSchool -> { // log.debug("In Education for person " + this.getKey().getId()); inSchool(); @@ -1030,6 +1037,22 @@ protected void lifeSatisfaction2() { } } + private void healthEQ5D() { + + double eq5dPrediction; + eq5dPrediction = Parameters.getRegEQ5D().getScore(this, Person.DoublesVariables.class); + if (eq5dPrediction > 1) { + he_eq5d = 1.0; + } + else if (eq5dPrediction < -0.594) { + he_eq5d = -0.594; + } + else { + he_eq5d = eq5dPrediction; + } + + } + /* Case-based measure of psychological distress, Steps 1 and 2 modelled together */ @@ -2253,6 +2276,7 @@ public enum DoublesVariables { Dehmf_c3_Low, Dehsp_c3_Low_L1, //Partner's education == Low at lag(1) Dehsp_c3_Medium_L1, //Partner's education == Medium at lag(1) + He_eq5d, //EQ5D quality of life score Dgn, //Gender: returns 1 if male Dgn_baseline, Dgn_Dag, @@ -2294,8 +2318,17 @@ public enum DoublesVariables { Dls_L1, //Life satisfaction status lag(1) Dhe_mcs, //Mental well-being status Dhe_mcs_L1, //Mental well-being status lag(1) + Dhe_mcs_sq, //MCS score squared + Dhe_mcs_times_pcs, //MCS times PCS + Dhe_mcs_c_times_pcs_c, //Centralised MCS times PCS + Dhe_mcs_c, //MCS centralised by subtracting population mean + Dhe_mcs_c_sq, //Square of centralised MCS Dhe_pcs, //Physical well-being status Dhe_pcs_L1, //Physical well-being status lag(1) + Dhe_pcs_sq, //PCS score squared + Dhe_pcs_cb, //PCS score cubed + Dhe_pcs_c, //MCS centralised by subtracting population mean + Dhe_pcs_c_sq, //Square of centralised MCS Dhmghq_L1, Dlltsd, //Long-term sick or disabled Dlltsd_L1, //Long-term sick or disabled lag(1) @@ -2860,6 +2893,26 @@ public double getDoubleValue(Enum variableID) { return dhe_mcs_lag1; } else return 0.; } + case Dhe_mcs_sq -> { + // Used to calculate he_eq5d in regHealthEQ5D + return dhe_mcs * dhe_mcs; + } + case Dhe_mcs_c -> { + // Used to calculate he_eq5d in regHealthEQ5D + return dhe_mcs - 51.5; + } + case Dhe_mcs_c_sq -> { + // Used to calculate he_eq5d in regHealthEQ5D + return Math.pow(dhe_mcs - 51.5, 2); + } + case Dhe_mcs_times_pcs -> { + // Used to calculate he_eq5d in regHealthEQ5D + return dhe_mcs * dhe_pcs; + } + case Dhe_mcs_c_times_pcs_c -> { + // Used to calculate he_eq5d in regHealthEQ5D + return (dhe_mcs - 51.5) * (dhe_pcs - 49.9); + } case Dhe_pcs -> { return dhe_pcs; } @@ -2868,6 +2921,22 @@ public double getDoubleValue(Enum variableID) { return dhe_pcs_lag1; } else return 0.; } + case Dhe_pcs_sq -> { + // Used to calculate he_eq5d in regHealthEQ5D + return dhe_pcs * dhe_pcs; + } + case Dhe_pcs_cb -> { + // Used to calculate he_eq5d in regHealthEQ5D + return dhe_pcs * dhe_pcs * dhe_pcs; + } + case Dhe_pcs_c -> { + // Used to calculate he_eq5d in regHealthEQ5D + return dhe_pcs - 49.9; + } + case Dhe_pcs_c_sq -> { + // Used to calculate he_eq5d in regHealthEQ5D + return Math.pow(dhe_pcs - 49.9, 2); + } case Dls -> { return dls; } @@ -2933,6 +3002,9 @@ public double getDoubleValue(Enum variableID) { case Dehsp_c3_Low_L1 -> { return (Education.Low.equals(dehsp_c3_lag1)) ? 1. : 0.; } + case He_eq5d -> { + return getHe_eq5d(); + } case Dhhtp_c4_CoupleChildren_L1 -> { return (Dhhtp_c4.CoupleChildren.equals(getDhhtp_c4_lag1())) ? 1.0 : 0.0; } @@ -4110,6 +4182,15 @@ public double getDhe_pcs() { return val; } + + public void setDhe_mcs(Double dhe_mcs) { + this.dhe_mcs = dhe_mcs; + } + + public void setDhe_pcs(Double dhe_pcs) { + this.dhe_pcs = dhe_pcs; + } + public void populateSocialCareReceipt(SocialCareReceiptState state) { if (SocialCareReceiptState.NoFormal.equals(state)) { needSocialCare = Indicator.True; @@ -5122,4 +5203,12 @@ public Long getIdFather() { public boolean getToBePartnered() {return toBePartnered;} public static void setPersonIdCounter(long id) {personIdCounter=id;} + + public Double getHe_eq5d() { + return he_eq5d; + } + + public void setHe_eq5d(Double he_eq5d) { + this.he_eq5d = he_eq5d; + } } diff --git a/src/main/java/simpaths/model/SimPathsModel.java b/src/main/java/simpaths/model/SimPathsModel.java index 45b550267..d48751e57 100644 --- a/src/main/java/simpaths/model/SimPathsModel.java +++ b/src/main/java/simpaths/model/SimPathsModel.java @@ -588,6 +588,8 @@ public void buildSchedule() { yearlySchedule.addCollectionEvent(persons, Person.Processes.HealthPCS2); yearlySchedule.addCollectionEvent(persons, Person.Processes.LifeSatisfaction2); + addCollectionEventToAllYears(persons, Person.Processes.HealthEQ5D); + // mortality (migration) and population alignment at year's end addCollectionEventToAllYears(persons, Person.Processes.ConsiderMortality); addEventToAllYears(Processes.PopulationAlignment); diff --git a/src/test/java/simpaths/model/PersonTest.java b/src/test/java/simpaths/model/PersonTest.java new file mode 100644 index 000000000..bb1be9b24 --- /dev/null +++ b/src/test/java/simpaths/model/PersonTest.java @@ -0,0 +1,110 @@ +package simpaths.model; + +import org.junit.jupiter.api.*; +import simpaths.data.Parameters; +import simpaths.model.enums.Country; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("Person class") +public class PersonTest { + + static Person testPerson; + + @BeforeAll + static void setup() { + testPerson = new Person(true); + } + + @Nested + @DisplayName("EQ5D process") + class Eq5dTests { + + @Nested + @DisplayName("With eq5dConversionParameters set to 'lawrence'") + class WithLawrenceParameters { + + @BeforeEach + public void setupLawrenceCoefficients() { + + Parameters.eq5dConversionParameters = "lawrence"; + Parameters.loadEQ5DParameters("UK", 8); + + } + + @Test + @DisplayName("Calculates low score correctly using Lawrence and Fleishman coefficients") + public void calculatesLowScoreCorrectly() { + + + testPerson.setDhe_mcs(1.); + testPerson.setDhe_pcs(1.); + + testPerson.onEvent(Person.Processes.HealthEQ5D); + + assertEquals(-0.594, testPerson.getHe_eq5d()); + + } + + @Test + @DisplayName("Calculates high score correctly using Lawrence and Fleishman coefficients") + public void calculatesHighScoreCorrectly() { + + + testPerson.setDhe_mcs(100.); + testPerson.setDhe_pcs(100.); + + testPerson.onEvent(Person.Processes.HealthEQ5D); + + assertEquals(1, testPerson.getHe_eq5d()); + + } + + } + + + @Nested + @DisplayName("With eq5dConversionParameters set to 'franks'") + class WithFranksParameters { + + @BeforeEach + public void setupFranksCoefficients() { + + Parameters.eq5dConversionParameters = "franks"; + Parameters.loadEQ5DParameters("UK", 8); + + } + + + @Test + @DisplayName("Calculates low score correctly using Franks coefficients") + public void calculatesLowScoreCorrectly() { + + testPerson.setDhe_mcs(1.); + testPerson.setDhe_pcs(1.); + + testPerson.onEvent(Person.Processes.HealthEQ5D); + + assertEquals(-0.594, testPerson.getHe_eq5d()); + + } + + @Test + @DisplayName("Calculates high score correctly using Franks coefficients") + public void calculatesHighScoreCorrectly(){ + + testPerson.setDhe_mcs(100.); + testPerson.setDhe_pcs(100.); + + testPerson.onEvent(Person.Processes.HealthEQ5D); + + // The maximum possible value given by the Franks coefficients + assertEquals(0.9035601, testPerson.getHe_eq5d()); + + } + + } + } + + +}