def test_readInfectionProbability_invalid_prob(prob): with pytest.raises(ValueError): loaders.readInfectionProbability( pd.DataFrame([{ "Time": 0, "Value": prob }]))
def test_readInfectionProbability_invalid_time(t): with pytest.raises(ValueError): loaders.readInfectionProbability( pd.DataFrame([{ "Date": t, "Value": 1.0 }]))
def test_readInfectionProbability_no_zeroth_time(): with pytest.raises(ValueError): loaders.readInfectionProbability( pd.DataFrame([{ "Time": 3, "Value": 1.0 }]))
def test_readInfectionProbability(): df = pd.DataFrame([("2020-01-01", 0.3), ("2020-02-01", 0.7), ("2020-12-31", 1.0)], columns=["Date", "Value"]) assert loaders.readInfectionProbability(df) == { dt.date(2020, 1, 1): 0.3, dt.date(2020, 2, 1): 0.7, dt.date(2020, 12, 31): 1.0 }
def test_readInfectionProbability_empty(): with pytest.raises(ValueError): loaders.readInfectionProbability(pd.DataFrame())
def test_readInfectionProbability(): df = pd.DataFrame([(0, 0.3), (4, 0.7), (30, 1.0)], columns=["Time", "Value"]) assert loaders.readInfectionProbability(df) == {0: 0.3, 4: 0.7, 30: 1.0}
def createNetworkOfPopulation( compartment_transition_table: pd.DataFrame, population_table: pd.DataFrame, commutes_table: pd.DataFrame, mixing_matrix_table: pd.DataFrame, infectious_states: pd.DataFrame, infection_prob: pd.DataFrame, initial_infections: pd.DataFrame, trials: pd.DataFrame, movement_multipliers_table: pd.DataFrame = None, stochastic_mode: pd.DataFrame = None, random_seed: pd.DataFrame = None) -> NetworkOfPopulation: """Create the network of the population, loading data from files. :param compartment_transition_table: pd.Dataframe specifying the transition rates between infected compartments. :param population_table: pd.Dataframe with the population size in each region by gender and age. :param commutes_table: pd.Dataframe with the movements between regions. :param mixing_matrix_table: pd.Dataframe with the age infection matrix. :param movement_multipliers_table: pd.Dataframe with the movement multipliers. This may be None, in which case no multipliers are applied to the movements. :param infectious_states: States that are considered infectious :param infection_prob: Probability that a given contact will result in an infection :param initial_infections: Initial infections of the population at time 0 :param trials: Number of trials for the model :param stochastic_mode: Use stochastic mode for the model :param random_seed: Random number generator seed used for stochastic mode :return: The constructed network """ infection_prob = loaders.readInfectionProbability(infection_prob) infectious_states = loaders.readInfectiousStates(infectious_states) initial_infections = loaders.readInitialInfections(initial_infections) trials = loaders.readTrials(trials) stochastic_mode = loaders.readStochasticMode(stochastic_mode) random_seed = loaders.readRandomSeed(random_seed) # diseases progression matrix progression = loaders.readCompartmentRatesByAge( compartment_transition_table) # population census data population = loaders.readPopulationAgeStructured(population_table) # Check some requirements for this particular model to work with the progression matrix all_states = set() for states in progression.values(): assert SUSCEPTIBLE_STATE not in states, "progression from susceptible state is not allowed" for state, nextStates in states.items(): for nextState in nextStates: all_states.add(state) all_states.add(nextState) assert state == nextState or nextState != EXPOSED_STATE, \ "progression into exposed state is not allowed other than in self reference" assert (set(infectious_states) - all_states) == set(), \ f"mismatched infectious states and states {infectious_states} {set(progression.keys())}" # people movement's graph graph = loaders.genGraphFromContactFile(commutes_table) # movement multipliers (dampening or heightening) if movement_multipliers_table is not None: movementMultipliers = loaders.readMovementMultipliers( movement_multipliers_table) else: movementMultipliers: Dict[int, loaders.Multiplier] = {} # age-based infection matrix mixingMatrix = loaders.MixingMatrix(mixing_matrix_table) agesInInfectionMatrix = set(mixingMatrix) for age in mixingMatrix: assert agesInInfectionMatrix == set( mixingMatrix[age]), "infection matrix columns/rows mismatch" # Checks across datasets assert agesInInfectionMatrix == set( progression.keys()), "infection matrix and progression ages mismatch" assert agesInInfectionMatrix == {age for region in population.values() for age in region}, \ "infection matrix and population ages mismatch" assert set(graph.nodes()) == set( population.keys()), "regions mismatch between graph and population" state0: Dict[str, Dict[Tuple[str, str], float]] = {} for node in list(graph.nodes()): region = state0.setdefault(node, {}) for age, compartments in progression.items(): region[(age, SUSCEPTIBLE_STATE)] = population[node][age] for compartment in compartments: region[(age, compartment)] = 0 logger.info("Nodes: %s, Ages: %s, States: %s", len(state0), agesInInfectionMatrix, all_states) return NetworkOfPopulation(progression=progression, graph=graph, initialState=state0, mixingMatrix=mixingMatrix, movementMultipliers=movementMultipliers, infectiousStates=infectious_states, infectionProb=infection_prob, initialInfections=initial_infections, trials=trials, stochastic=stochastic_mode, randomState=np.random.default_rng(random_seed))
def createNetworkOfPopulation( compartment_transition_table: pd.DataFrame, population_table: pd.DataFrame, commutes_table: pd.DataFrame, mixing_matrix_table: pd.DataFrame, infectious_states: pd.DataFrame, infection_prob: pd.DataFrame, initial_infections: pd.DataFrame, trials: pd.DataFrame, start_end_date: pd.DataFrame, movement_multipliers_table: pd.DataFrame = None, stochastic_mode: pd.DataFrame = None, ) -> Tuple[NetworkOfPopulation, List[standard_api.Issue]]: """Create the network of the population, loading data from files. :param compartment_transition_table: pd.Dataframe specifying the transition rates between infected compartments. :param population_table: pd.Dataframe with the population size in each region by gender and age. :param commutes_table: pd.Dataframe with the movements between regions. :param mixing_matrix_table: pd.Dataframe with the age infection matrix. :param infectious_states: States that are considered infectious :param infection_prob: Probability that a given contact will result in an infection :param initial_infections: Initial infections of the population at time 0 :param trials: Number of trials for the model :param start_end_date: Starting and ending dates of the model :param movement_multipliers_table: pd.Dataframe with the movement multipliers. This may be None, in which case no multipliers are applied to the movements. :param stochastic_mode: Use stochastic mode for the model :return: A tuple with the constructed network and a list of any issues found. Although it may look convenient, storing this list inside of NetworkOfPopulation is not a good idea, as that may give the functions that receive it the false impression they can just append new issues there """ issues: List[standard_api.Issue] = [] infection_prob = loaders.readInfectionProbability(infection_prob) infectious_states = loaders.readInfectiousStates(infectious_states) initial_infections = loaders.readInitialInfections(initial_infections) trials = loaders.readTrials(trials) start_date, end_date = loaders.readStartEndDate(start_end_date) stochastic_mode = loaders.readStochasticMode(stochastic_mode) # diseases progression matrix progression = loaders.readCompartmentRatesByAge( compartment_transition_table) # population census data population = loaders.readPopulationAgeStructured(population_table) # Check some requirements for this particular model to work with the progression matrix all_states = set() for states in progression.values(): assert SUSCEPTIBLE_STATE not in states, "progression from susceptible state is not allowed" for state, nextStates in states.items(): for nextState in nextStates: all_states.add(state) all_states.add(nextState) assert state == nextState or nextState != EXPOSED_STATE, \ "progression into exposed state is not allowed other than in self reference" assert (set(infectious_states) - all_states) == set(), \ f"mismatched infectious states and states {infectious_states} {all_states}" # people movement's graph graph = loaders.genGraphFromContactFile(commutes_table) # movement multipliers (dampening or heightening) if movement_multipliers_table is not None: movementMultipliers = loaders.readMovementMultipliers( movement_multipliers_table) else: movementMultipliers = {} # age-based infection matrix mixingMatrix = loaders.MixingMatrix(mixing_matrix_table) agesInInfectionMatrix = set(mixingMatrix) for age in mixingMatrix: assert agesInInfectionMatrix == set( mixingMatrix[age]), "infection matrix columns/rows mismatch" # Checks across datasets assert agesInInfectionMatrix == set( progression.keys()), "infection matrix and progression ages mismatch" assert agesInInfectionMatrix == {age for region in population.values() for age in region}, \ "infection matrix and population ages mismatch" disconnected_nodes = set(population.keys()) - set(graph.nodes()) if disconnected_nodes: log_issue( logger, f"These nodes have no contacts in the current network: {','.join(disconnected_nodes)}", IssueSeverity.MEDIUM, issues, ) state0: Dict[str, Dict[Tuple[str, str], float]] = {} for node in list(graph.nodes()): region = state0.setdefault(node, {}) for age, compartments in progression.items(): if node not in population: log_issue( logger, f"Node {node} is not in the population table, assuming population of 0 for all ages", IssueSeverity.MEDIUM, issues, ) pop = 0.0 else: pop = population[node][age] region[(age, SUSCEPTIBLE_STATE)] = pop for compartment in compartments: region[(age, compartment)] = 0 logger.info("Nodes: %s, Ages: %s, States: %s", len(state0), agesInInfectionMatrix, all_states) nop = NetworkOfPopulation( progression=progression, graph=graph, initialState=state0, mixingMatrix=mixingMatrix, movementMultipliers=movementMultipliers, infectiousStates=infectious_states, infectionProb=infection_prob, initialInfections=initial_infections, trials=trials, startDate=start_date, endDate=end_date, stochastic=stochastic_mode, ) return (nop, issues)