def birth(self): # construct fertility lookup for the current timestep, means a left join with zero missing values (male or too young/old) fert = pd.DataFrame({ "age": self.fert.age, "gender": np.full(len(self.fert), False), "fertility_rate": self.fert[str(int(neworder.timeline.time()))].values }) frates = pd.merge(self.pp, fert, how='left').fillna(0.0)["fertility_rate"].values self.pp["__baby"] = neworder.hazard(frates * neworder.timestep) # clone mothers (hh id persists) newborns = self.pp[self.pp["__baby"] == True].copy() newborns.mother_id = newborns.id newborns.id = range(self.iditer, self.iditer + len(newborns)) self.iditer = self.iditer + len(newborns) newborns.partner_id = UNSET newborns.dur_in_couple = UNSET newborns.civilstate = RelationshipStatus.SINGLE.value newborns.age = 0 newborns.agegroup_civilstate = 0 #newborns.workstate = 0 newborns.gender = neworder.hazard(0.51, len(newborns)) #neworder.log(newborns) self.pp = self.pp.append(newborns)
def births(self, deltat): # First consider only females females = self.data[self.data.DC1117EW_C_SEX == 2].copy() # Now map the appropriate fertility rate to each female # might be a more efficient way of generating this array rates = females.join( self.fertility, on=["NewEthpop_ETH", "DC1117EW_C_SEX", "DC1117EW_C_AGE"])["Rate"].values # Then randomly determine if a birth occurred (neworder callback) h = neworder.hazard(rates * deltat) # The babies are a clone of the new mothers, with with changed PID, reset age and randomised gender (keeping location and ethnicity) newborns = females[h == 1].copy() newborns.PID = range(self.counter, self.counter + len(newborns)) newborns.Age = neworder.ustream( len(newborns)) # born within the last 12 months newborns.DC1117EW_C_AGE = 1 # this is 0-1 in census category # NOTE: do not convert to pd.Series here to stay as this has its own index which conflicts with the main table newborns.DC1117EW_C_SEX = neworder.hazard(0.5, len(newborns)) + 1 # Finally append newborns to main population and adjust counter self.data = self.data.append(newborns) self.counter = self.counter + len(newborns)
def test(): t = test_.Test() # Exp.value = p +/- 1/sqrt(N) h = neworder.hazard(0.2, 10000) t.check(isinstance(h, np.ndarray)) t.check(len(h) == 10000) t.check(abs(np.mean(h) - 0.2) < 0.01) hv = neworder.hazard(np.array([0.1, 0.2, 0.3, 0.4, 0.5])) t.check(isinstance(hv, np.ndarray)) t.check(len(hv) == 5) # Exp.value = 1/p +/- 1/sqrt(N) s = neworder.stopping(0.1, 10000) t.check(isinstance(s, np.ndarray)) t.check(len(s) == 10000) t.check(abs(np.mean(s) / 10 - 1.0) < 0.03) sv = neworder.stopping(np.array([0.1, 0.2, 0.3, 0.4, 0.5])) t.check(isinstance(sv, np.ndarray)) t.check(len(sv) == 5) # Non-homogeneous Poisson process (time-dependent hazard) nhpp = neworder.first_arrival(np.array([0.1, 0.2, 0.3, 0.4, 0.5]), 1.0, 10) t.check(isinstance(nhpp, np.ndarray)) t.check(len(nhpp) == 10) return not t.any_failed
def migrations(self, deltat): # internal immigrations: # - assign the rates to the incumbent popultion appropriately by age,sex,ethnicity # - randomly sample this population, clone and append in_rates = self.data.join(self.in_migration, on=["LAD", "NewEthpop_ETH", "DC1117EW_C_SEX", "DC1117EW_C_AGE"])["Rate"].values # in-migration should be sampling from the whole population ex-LAD, instead do an approximation by scaling up the LAD population # NOTE this is wrong for a number of reasons esp. as it cannot sample category combinations that don't already exist in the LAD h_in = neworder.hazard(in_rates * deltat) incoming = self.data[h_in == 1].copy() # Append incomers to main population and adjust counter # Assign a new id incoming.PID = range(self.counter, self.counter + len(incoming)) incoming.Area = incoming.LAD # assign a new random fractional age based on census age category incoming.Age = incoming.DC1117EW_C_AGE - neworder.ustream(len(incoming)).tolist() self.data = self.data.append(incoming, sort=False) self.counter = self.counter + len(incoming) # internal emigration out_rates = self.data.join(self.out_migration, on=["LAD", "NewEthpop_ETH", "DC1117EW_C_SEX", "DC1117EW_C_AGE"])["Rate"] h_out = neworder.hazard(out_rates.values * deltat) # remove outgoing migrants self.data = self.data[h_out!=1] intl_in_rates = self.data.join(self.immigration, on=["LAD", "NewEthpop_ETH", "DC1117EW_C_SEX", "DC1117EW_C_AGE"])["Rate"] h_intl_in = neworder.hazard(intl_in_rates.values * deltat) intl_incoming = self.data[h_intl_in == 1].copy() intl_incoming.PID = range(self.counter, self.counter + len(intl_incoming)) intl_incoming.Area = "INTL" #self.lad # assign a new random fractional age based on census age category intl_incoming.Age = intl_incoming.DC1117EW_C_AGE - neworder.ustream(len(intl_incoming)).tolist() self.data = self.data.append(intl_incoming) self.counter = self.counter + len(intl_incoming) # international emigrtion intl_out_rates = self.data.join(self.emigration, on=["LAD", "NewEthpop_ETH", "DC1117EW_C_SEX", "DC1117EW_C_AGE"])["Rate"] h_intl_out = neworder.hazard(intl_out_rates.values * deltat) # remove outgoing migrants self.data = self.data[h_intl_out!=1] # record net migration self.in_out = (h_in.sum(), h_out.sum(), h_intl_in.sum(), h_intl_out.sum())
def deaths(self, deltat): # neworder.log("deaths({:.3f})".format(deltat)) # Map the appropriate mortality rate to each female # might be a more efficient way of generating this array rates = self.data.join(self.mortality, on=["LAD", "NewEthpop_ETH", "DC1117EW_C_SEX", "DC1117EW_C_AGE"])["Rate"] # Then randomly determine if a birth occurred h = neworder.hazard(rates.values * deltat) # Finally remove deceased from table self.data = self.data[h!=1]
def death(self): # construct mortality lookup for the current timestep, means an easy join mort = pd.DataFrame({ "age": self.mmort.age, "gender": np.full(len(self.mmort), True), "mortality_rate": self.mmort[str(int(neworder.timeline.time()))].values }) mort = mort.append( pd.DataFrame({ "age": self.fmort.age, "gender": np.full(len(self.fmort), False), "mortality_rate": self.fmort[str(int(neworder.timeline.time()))].values })) # join the lookup with the people and get each person's mortality rate mrates = pd.merge(self.pp, mort, how='left')["mortality_rate"].values # scale by timestep self.pp["__dead"] = neworder.hazard(mrates * neworder.timestep) # record ids of deceased to unlink dead_ids = self.pp[self.pp["__dead"] == True].id.values # unlink dead mothers self.pp.loc[self.pp.mother_id.isin(dead_ids), "mother_id"] = UNSET # unlink dead partners self.pp.loc[self.pp.partner_id.isin(dead_ids), "civilstate"] = RelationshipStatus.WIDOW.value self.pp.loc[self.pp.partner_id.isin(dead_ids), "partner_id"] = UNSET #neworder.log("Avg age of men = %f" % np.mean(self.pp[self.pp.gender == True].age)) neworder.log("Avg age of dead men = %f" % np.mean(self.pp[(self.pp["__dead"] == True) & (self.pp.gender == True)].age)) #neworder.log("Avg age of women = %f" % np.mean(self.pp[self.pp.gender == False].age)) neworder.log("Avg age of dead women = %f" % np.mean(self.pp[(self.pp["__dead"] == True) & (self.pp.gender == False)].age)) neworder.log( "widows=%d" % len(self.pp[self.pp.civilstate == RelationshipStatus.WIDOW.value])) # remove deceased self.pp = self.pp[self.pp["__dead"] == False]
def divorce(self): drate = pd.DataFrame({ "agegrp": self.drate.agegrp, "divorce_rate": self.drate[str(int(neworder.timeline.time()))].values }) neworder.log(drate) # TODO need to map agegrp to age to merge drates = pd.merge(self.pp, drate, how='left').fillna(0.0)["fertility_rate"].values self.pp["__divorce"] = neworder.hazard(drates * neworder.timestep) neworder.log(self.pp)