From 73f25edfa6309c343382cd5944cf842d499d1cdf Mon Sep 17 00:00:00 2001 From: brmnjsh Date: Tue, 24 Oct 2023 20:06:20 +0000 Subject: [PATCH 1/4] add rasch functionality to form generation --- app/lib/irt/item_information_function.py | 5 +++++ app/lib/irt/item_response_function.py | 3 +++ app/lib/irt/models/rasch.py | 2 +- app/services/form_generation_service.py | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/lib/irt/item_information_function.py b/app/lib/irt/item_information_function.py index cd9953f..6d1f509 100644 --- a/app/lib/irt/item_information_function.py +++ b/app/lib/irt/item_information_function.py @@ -1,6 +1,7 @@ import logging from lib.irt.models.three_parameter_logistic import ThreeParameterLogistic +from lib.irt.models.rasch import Rasch from lib.errors.item_generation_error import ItemGenerationError @@ -20,6 +21,10 @@ class ItemInformationFunction(): return (self.model_data.a_param * q * (p - self.model_data.c_param)**2) / (p * ( (1 - self.model_data.c_param)**2)) + elif self.model_data.model == 'RASCH': + p = Rasch(self.model_data, kwargs).result() + q = 1 - p + return p * q else: # potentially error out raise ItemGenerationError( diff --git a/app/lib/irt/item_response_function.py b/app/lib/irt/item_response_function.py index 6cd8ced..9b5999d 100644 --- a/app/lib/irt/item_response_function.py +++ b/app/lib/irt/item_response_function.py @@ -1,4 +1,5 @@ from lib.irt.models.three_parameter_logistic import ThreeParameterLogistic +from lib.irt.models.rasch import Rasch from lib.errors.item_generation_error import ItemGenerationError @@ -10,5 +11,7 @@ class ItemResponseFunction(): def calculate(self, **kwargs): if self.model_data.model == '3PL': return ThreeParameterLogistic(self.model_data, kwargs).result() + elif self.model_data.model == 'RASCH': + return Rasch(self.model_data, kwargs).result() else: raise ItemGenerationError("irt model not supported or provided") diff --git a/app/lib/irt/models/rasch.py b/app/lib/irt/models/rasch.py index 46ed222..33690e5 100644 --- a/app/lib/irt/models/rasch.py +++ b/app/lib/irt/models/rasch.py @@ -5,7 +5,7 @@ from lib.irt.models.base import * class Rasch(Base): def result(self): - return 0.0 + return (1 / (1 + self.e(-1 * (self.theta - self.b_param)))) @classmethod def ability_estimate(self, items) -> float: diff --git a/app/services/form_generation_service.py b/app/services/form_generation_service.py index d913200..966e047 100644 --- a/app/services/form_generation_service.py +++ b/app/services/form_generation_service.py @@ -72,6 +72,7 @@ class FormGenerationService(Base): current_drift = 0 # FF Tokyo Drift # adding an element of randomness to the items and bundles used + # may need to change impl based on limit of items available selected_items = self.solver_run.select_items_by_percent(30) selected_bundles = self.solver_run.select_bundles_by_percent( 30) From 433879c205e6d20221917526f111f78bb4c9e3ac Mon Sep 17 00:00:00 2001 From: brmnjsh Date: Tue, 24 Oct 2023 21:05:01 +0000 Subject: [PATCH 2/4] log what irt model is used during form generation --- app/services/form_generation_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/form_generation_service.py b/app/services/form_generation_service.py index 966e047..b7207b5 100644 --- a/app/services/form_generation_service.py +++ b/app/services/form_generation_service.py @@ -89,7 +89,7 @@ class FormGenerationService(Base): upBound=1, cat='Binary') - logging.info(f'Generating Solution for Form {form_number}') + logging.info(f'Generating Solution for Form {form_number} using the {self.solver_run.irt_model.model} IRT model') while current_drift <= Target.max_drift(): drift_percent = current_drift / 100 From 2984f18f9f86d18f9f8edf40f47569381f927d03 Mon Sep 17 00:00:00 2001 From: brmnjsh Date: Thu, 26 Oct 2023 15:09:58 +0000 Subject: [PATCH 3/4] linking white paper and lower case irt model selection --- app/lib/irt/item_information_function.py | 2 +- app/lib/irt/item_response_function.py | 2 +- app/lib/irt/models/rasch.py | 3 +++ app/services/form_generation_service.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/lib/irt/item_information_function.py b/app/lib/irt/item_information_function.py index 6d1f509..acd1b3e 100644 --- a/app/lib/irt/item_information_function.py +++ b/app/lib/irt/item_information_function.py @@ -21,7 +21,7 @@ class ItemInformationFunction(): return (self.model_data.a_param * q * (p - self.model_data.c_param)**2) / (p * ( (1 - self.model_data.c_param)**2)) - elif self.model_data.model == 'RASCH': + elif self.model_data.model == 'rasch': p = Rasch(self.model_data, kwargs).result() q = 1 - p return p * q diff --git a/app/lib/irt/item_response_function.py b/app/lib/irt/item_response_function.py index 9b5999d..81a20f1 100644 --- a/app/lib/irt/item_response_function.py +++ b/app/lib/irt/item_response_function.py @@ -11,7 +11,7 @@ class ItemResponseFunction(): def calculate(self, **kwargs): if self.model_data.model == '3PL': return ThreeParameterLogistic(self.model_data, kwargs).result() - elif self.model_data.model == 'RASCH': + elif self.model_data.model == 'rasch': return Rasch(self.model_data, kwargs).result() else: raise ItemGenerationError("irt model not supported or provided") diff --git a/app/lib/irt/models/rasch.py b/app/lib/irt/models/rasch.py index 33690e5..96090b5 100644 --- a/app/lib/irt/models/rasch.py +++ b/app/lib/irt/models/rasch.py @@ -5,6 +5,9 @@ from lib.irt.models.base import * class Rasch(Base): def result(self): + # contains the primary Rasch function, determining the probably of an inidividual + # that an individual at a certain theta would get a particular question correct + # https://edres.org/irt/baker/chapter6.pdf return (1 / (1 + self.e(-1 * (self.theta - self.b_param)))) @classmethod diff --git a/app/services/form_generation_service.py b/app/services/form_generation_service.py index b7207b5..bfb191e 100644 --- a/app/services/form_generation_service.py +++ b/app/services/form_generation_service.py @@ -1,6 +1,6 @@ import json, random, io, logging -from pulp import LpProblem, LpVariable, LpMinimize, LpMaximize, LpAffineExpression, LpConstraint, LpStatus, lpSum +from pulp import LpProblem, LpVariable, LpMinimize, LpStatus, lpSum from lib.application_configs import ApplicationConfigs from helpers import aws_helper, tar_helper, csv_helper, service_helper, solver_helper From 91d7b94bea6ad4f6ff104846667d7f619abe2d85 Mon Sep 17 00:00:00 2001 From: brmnjsh Date: Thu, 26 Oct 2023 16:58:25 +0000 Subject: [PATCH 4/4] optional params to accomodate different irt models --- app/models/irt_model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/irt_model.py b/app/models/irt_model.py index 2f79776..670be6f 100644 --- a/app/models/irt_model.py +++ b/app/models/irt_model.py @@ -1,11 +1,11 @@ from pydantic import BaseModel -from typing import Dict +from typing import Dict, Optional class IRTModel(BaseModel): - a_param: float + a_param: Optional[float] = None b_param: Dict = {"schema_bson_id": str, "field_bson_id": str} - c_param: float + c_param: Optional[float] = None model: str def formatted_b_param(self):