def assign_cv_outcome(self, person, years=1, manualStrokeMIProbability=None): return Outcome( OutcomeType.STROKE, False) if np.random.random() < self._stroke_rate else None
def assign_cv_outcome(self, person, years=1, manualStrokeMIProbability=None): return (Outcome(OutcomeType.MI, np.random.random() < self._fatality_rate) if np.random.random() < self._mi_rate else None)
def setUp(self): self.joe = Person( 42, NHANESGender.MALE, NHANESRaceEthnicity.NON_HISPANIC_BLACK, 140, 90, 5.5, 50, 200, 25, 90, 150, 45, 0, Education.COLLEGEGRADUATE, SmokingStatus.NEVER, AlcoholCategory.NONE, 0, 0, 0, 0, initializeAFib, ) self.joe_with_mi = copy.deepcopy(self.joe) self.joe_with_mi.add_outcome_event(Outcome(OutcomeType.MI, False)) self.joe_with_stroke = copy.deepcopy(self.joe) self.joe_with_stroke.add_outcome_event(Outcome(OutcomeType.STROKE, False)) self._population_dataframe = init_vectorized_population_dataframe( [self.joe, self.joe_with_mi, self.joe_with_stroke], with_base_gcp=True, ) self._always_positive_repository = AlwaysPositiveOutcomeRepository() self._always_negative_repository = AlwaysNegativeOutcomeRepository() self.cvDeterminer = CVOutcomeDetermination(self._always_positive_repository)
def create_or_rollback_events_to_correct_calibration( self, treatment_outcome_standard, treatedRisks, untreatedRisks, outcomeType, fatalityDetermination, recalibration_pop): modelEstimatedRR = treatedRisks.mean() / untreatedRisks.mean() # use the delta between that effect and the calibration standard to recalibrate the pop. delta = modelEstimatedRR - treatment_outcome_standard[outcomeType] eventsForPeople = [ person.has_outcome_during_wave(self._currentWave, outcomeType) for _, person in recalibration_pop.iteritems() ] numberOfEventStatusesToChange = abs( int( round(delta * pd.Series(eventsForPeople).sum() / modelEstimatedRR))) nonEventsForPeople = [not item for item in eventsForPeople] # key assumption: "treatment" is applied to a population as opposed to individuals within a population # analyses can be setup either way...build two populations and then set different treatments # or build a ur-population adn then set different treamtents within them # this is, i thikn, the first time where a coding decision is tied to one of those structure. # it would not, i think, be hard to change. but, just spelling it out here. # if negative, the model estimated too few events, if positive, too mnany if delta < 0: if numberOfEventStatusesToChange > 0: new_events = recalibration_pop.loc[nonEventsForPeople].sample( n=numberOfEventStatusesToChange, replace=False, weights=pd.Series( untreatedRisks).loc[nonEventsForPeople].values) for i, event in new_events.iteritems(): event.add_outcome_event( Outcome(outcomeType, fatalityDetermination(event))) # redtag - two problems here...1. rolling back events in people that may not have events # 2. probably usign the wrong weights...need to roll back inversely proportionately to the likeliood of an event, riht? elif delta > 0: if numberOfEventStatusesToChange > pd.Series( eventsForPeople).sum(): numberOfEventStatusesToChange = pd.Series( eventsForPeople).sum() if numberOfEventStatusesToChange > 0: events_to_rollback = recalibration_pop.loc[ eventsForPeople].sample( n=numberOfEventStatusesToChange, replace=False, weights=pd.Series( 1 - untreatedRisks).loc[eventsForPeople].values) for i, event in events_to_rollback.iteritems(): event.rollback_most_recent_event(outcomeType)
def test_fatal_mi_secondary_prob(self): self.cvDeterminer.mi_secondary_case_fatality = 1.0 self.cvDeterminer.mi_case_fatality = 0.0 joeClone = copy.deepcopy(self.joe) self.assertEqual(self.cvDeterminer._will_have_fatal_mi(joeClone, 0.0), 0) joeClone._outcomes[OutcomeType.MI] = (joeClone._age, Outcome(OutcomeType.MI, False)) # even though the passed fatality rate is zero, it shoudl be overriden by the # secondary rate given that joeclone had a prior MI self.assertEqual(self.cvDeterminer._will_have_fatal_mi(joeClone, 0.0), 1)
def assign_outcome_for_person(self, outcome_model_repository, person, years=1, manualStrokeMIProbability=None): cvRisk = outcome_model_repository.get_risk_for_person( person, OutcomeModelType.CARDIOVASCULAR, years=1) if person._stroke or person._mi: cvRisk = cvRisk * self.secondary_prevention_multiplier if self._will_have_cvd_event(cvRisk): if self._will_have_mi(person, outcome_model_repository, manualStrokeMIProbability): if self._will_have_fatal_mi(person): return Outcome(OutcomeType.MI, True) else: return Outcome(OutcomeType.MI, False) else: if self._will_have_fatal_stroke(person): return Outcome(OutcomeType.STROKE, True) else: return Outcome(OutcomeType.STROKE, False)
def get_outcome(self, person, mi, fatal, vectorized): if vectorized: person.miNext = mi person.strokeNext = not mi person.deadNext = fatal person.miFatal = mi and fatal person.strokeFatal = not mi and fatal person.ageAtFirstMI = ( person.age if (person.ageAtFirstMI is None) or (np.isnan(person.ageAtFirstMI)) else person.ageAtFirstMI ) person.ageAtFirstStroke = ( person.age if (person.ageAtFirstStroke is None) or (np.isnan(person.ageAtFirstStroke)) else person.ageAtFirstStroke ) return person else: return Outcome(OutcomeType.MI if mi else OutcomeType.STROKE, fatal)
def update_person(self, person, x): if person.is_dead(): raise Exception(f"Trying to update a dead person: {person}") for rf in self._riskFactors: attr = getattr(person, "_" + rf) attr.append(x[rf + str(self._currentWave)]) for treatment in self._treatments: attr = getattr(person, "_" + treatment) attr.append(x[treatment + str(self._currentWave)]) # advance outcomes - this will add CV eath for outcomeName, outcomeType in { "stroke": OutcomeType.STROKE, "mi": OutcomeType.MI, "dementia": OutcomeType.DEMENTIA, }.items(): if x[outcomeName + "Next"]: fatal = False if outcomeName == "dementia" else x[outcomeName + "Fatal"] # only one dementia event per person if outcomeName == "dementia" and person._dementia: break else: person.add_outcome_event(Outcome(outcomeType, fatal)) person._gcp.append(x.gcp) person._qalys.append(x.qalyNext) person._bpMedsAdded.append(x.bpMedsAddedNext) # add non CV death to person objects if x.nonCVDeathNext: person._alive.append(False) # only advance age in survivors if not x.deadNext: person._age.append(person._age[-1] + 1) person._alive.append(True) return person
def get_dementia(self, person): return Outcome(OutcomeType.DEMENTIA, False)
def assign_cv_outcome(self, person, years=1, manualStrokeMIProbability=None): return Outcome(OutcomeType.MI, 0)
def __init__( self, age: int, gender: NHANESGender, raceEthnicity: NHANESRaceEthnicity, sbp: int, dbp: int, a1c: float, hdl: int, totChol: int, bmi: float, ldl: int, trig: int, waist: int, # Waist circumference in cm anyPhysicalActivity: int, education: Education, smokingStatus: SmokingStatus, alcohol: AlcoholCategory, antiHypertensiveCount: int, statin: int, otherLipidLoweringMedicationCount: int, creatinine: float, initializeAfib: Callable, initializationRepository=None, selfReportStrokeAge=None, selfReportMIAge=None, randomEffects=None, **kwargs, ) -> None: # building in manual bounds on extreme values self._lowerBounds = {"sbp": 60, "dbp": 20} self._upperBounds = {"sbp": 300, "dbp": 180} self._gender = gender self._raceEthnicity = raceEthnicity self._alive = [True] self._age = [age] self._sbp = [self.apply_bounds("sbp", sbp)] self._dbp = [self.apply_bounds("dbp", dbp)] self._a1c = [a1c] self._hdl = [hdl] self._ldl = [ldl] self._trig = [trig] self._totChol = [totChol] self._bmi = [bmi] self._waist = [waist] self._anyPhysicalActivity = [anyPhysicalActivity] self._alcoholPerWeek = [alcohol] self._education = education # TODO : change smoking status into a factor that changes over time self._smokingStatus = smokingStatus self._antiHypertensiveCount = [antiHypertensiveCount] self._statin = [statin] self._otherLipidLoweringMedicationCount = [ otherLipidLoweringMedicationCount ] self._creatinine = [creatinine] # outcomes is a dictionary of arrays. each element in the dictionary represents # a differnet outcome type each element in the array is a tuple representting # the age of the patient at the time of an event (element zero). and the outcome # (element one).multiple events can be accounted for by having multiple # elements in the array. self._outcomes = { OutcomeType.MI: [], OutcomeType.STROKE: [], OutcomeType.DEMENTIA: [] } self._selfReportStrokePriorToSim = 0 self._selfReportMIPriorToSim = 0 # a variable to track changes in BP meds compared to the baseline self._bpMedsAdded = [0] # convert events for events prior to simulation if selfReportStrokeAge is not None and selfReportStrokeAge > 1: self._selfReportStrokeAge = (selfReportStrokeAge if selfReportStrokeAge <= self._age[-1] else self._age[-1]) self._selfReportStrokePriorToSim = 1 self._outcomes[OutcomeType.STROKE].append( (-1, Outcome(OutcomeType.STROKE, False))) if selfReportMIAge is not None and selfReportMIAge > 1: self._selfReportMIAge = (selfReportMIAge if selfReportMIAge <= self._age[-1] else self._age[-1]) self._selfReportMIPriorToSim = 1 self._outcomes[OutcomeType.MI].append( (-1, Outcome(OutcomeType.MI, False))) for k, v in kwargs.items(): setattr(self, k, v) if initializeAfib is not None: self._afib = [initializeAfib(self)] else: self._afib = [False] # for outcome mocels that require random effects, store in this dictionary self._randomEffects = {"gcp": 0} if randomEffects is not None: self._randomEffects.update(randomEffects) # lucianatag: for this and GCP, this approach is a bit inelegant. the idea is to have classees that can be swapped out # at the population level to change the behavior about how people change over time. # but, when we instantiate a person, we don't want to keep a refernce tot the population. # is the fix just to have the population create people (such that the repository/strategy/model classes can be assigned from within # the population) self._qalys = [] self._gcp = [] if initializationRepository is not None: initializers = initializationRepository.get_initializers() for initializerName, method in initializers.items(): attr = getattr(self, initializerName) attr.append(method(self)) self._bpTreatmentStrategy = None
def get_dementia(self, person): if npRand.uniform(size=1) < self.get_risk_for_person( person, OutcomeModelType.DEMENTIA): return Outcome(OutcomeType.DEMENTIA, False) else: return None
def __init__( self, age: int, gender: NHANESGender, raceEthnicity: NHANESRaceEthnicity, sbp: int, dbp: int, a1c: float, hdl: int, totChol: int, bmi: float, ldl: int, trig: int, waist: int, # Waist circumference in cm anyPhysicalActivity: int, education: Education, smokingStatus: SmokingStatus, alcohol: AlcoholCategory, antiHypertensiveCount: int, statin: int, otherLipidLoweringMedicationCount: int, initializeAfib: Callable, selfReportStrokeAge=None, selfReportMIAge=None, **kwargs, ) -> None: # building in manual bounds on extreme values self._lowerBounds = {"sbp": 60, "dbp": 20} self._upperBounds = {"sbp": 300, "dbp": 180} self._gender = gender self._raceEthnicity = raceEthnicity self._alive = [True] self._age = [age] self._sbp = [self.apply_bounds("sbp", sbp)] self._dbp = [self.apply_bounds("dbp", dbp)] self._a1c = [a1c] self._hdl = [hdl] self._ldl = [ldl] self._trig = [trig] self._totChol = [totChol] self._bmi = [bmi] self._waist = [waist] self._anyPhysicalActivity = [anyPhysicalActivity] self._alcoholPerWeek = [alcohol] self._education = education # TODO : change smoking status into a factor that changes over time self._smokingStatus = smokingStatus self._antiHypertensiveCount = [antiHypertensiveCount] self._statin = [statin] self._otherLipidLoweringMedicationCount = [ otherLipidLoweringMedicationCount ] # outcomes is a dictionary of arrays. each element in the dictionary represents # a differnet outcome type each element in the array is a tuple representting # the age of the patient at the time of an event (element zero). and the outcome # (element one).multiple events can be accounted for by having multiple # elements in the array. self._outcomes = {OutcomeType.MI: [], OutcomeType.STROKE: []} self._selfReportStrokePriorToSim = 0 self._selfReportMIPriorToSim = 0 # convert events for events prior to simulation if selfReportStrokeAge is not None and selfReportStrokeAge > 1: self._selfReportStrokePriorToSim = 1 self._outcomes[OutcomeType.STROKE].append( (-1, Outcome(OutcomeType.STROKE, False))) if selfReportMIAge is not None and selfReportMIAge > 1: self._selfReportMIPriorToSim = 1 self._outcomes[OutcomeType.MI].append( (-1, Outcome(OutcomeType.MI, False))) for k, v in kwargs.items(): setattr(self, k, v) if initializeAfib is not None: self._afib = [initializeAfib(self)] else: self._afib = [False] self._gcp = [] # for outcome mocels that require random effects, store in this dictionary self._randomEffects = dict() self._bpTreatmentStrategy = None
def assign_cv_outcome(self, person, years=1, manualStrokeMIProbability=None): return Outcome(OutcomeType.STROKE, 1) if person._age[-1] > 50 else None
def assign_cv_outcome(self, person): return Outcome(OutcomeType.MI, False)