示例#1
0
def get_doubling_time(sim,
                      series=None,
                      interval=None,
                      start_day=None,
                      end_day=None,
                      moving_window=None,
                      exp_approx=False,
                      max_doubling_time=100,
                      eps=1e-3,
                      verbose=None):
    '''
    Method to calculate doubling time
    Can be used in various ways:
        1. get_doubling_time(sim, interval=[3,30]) returns the doubling time over the given interval (single float)
        2. get_doubling_time(sim, interval=[3,30], moving_window=3) returns doubling times calculated over moving windows (array)
    Instead of an interval, can pass in the start and end days (as integers - TODO, change this to accept dates)
    Can pass in a series or the name of a result
    '''

    # Set verbose level
    if verbose is None:
        verbose = sim['verbose']

    # Validate inputs: series
    if series is None or isinstance(series, str):
        if not sim.results_ready:
            raise Exception(
                f"Results not ready, cannot calculate doubling time")
        else:
            if series is None or series not in sim.reskeys:
                sc.printv(
                    f"Series not supplied or not found in results; defaulting to use cumulative exposures",
                    1, verbose)
                series = 'cum_infections'
            series = sim.results[series].values
    else:
        series = sc.promotetoarray(series)

    # Validate inputs: interval
    if interval is not None:
        if len(interval) != 2:
            sc.printv(
                f"Interval should be a list/array/tuple of length 2, not {len(interval)}. Resetting to length of series.",
                1, verbose)
            interval = [0, len(series)]
        start_day, end_day = interval[0], interval[1]

    if len(series) < end_day:
        sc.printv(
            f"End day {end_day} is after the series ends ({len(series)}). Resetting to length of series.",
            1, verbose)
        end_day = len(series)
    int_length = end_day - start_day

    # Deal with moving window
    if moving_window is not None:
        if not sc.isnumber(moving_window):
            sc.printv(
                f"Moving window should be an integer; ignoring and calculating single result",
                1, verbose)
            doubling_time = get_doubling_time(sim,
                                              series=series,
                                              start_day=start_day,
                                              end_day=end_day,
                                              moving_window=None,
                                              exp_approx=exp_approx)

        else:
            if not isinstance(moving_window, int):
                sc.printv(
                    f"Moving window should be an integer; recasting {moving_window} the nearest integer... ",
                    1, verbose)
                moving_window = int(moving_window)
            if moving_window < 2:
                sc.printv(
                    f"Moving window should be greater than 1; recasting {moving_window} to 2",
                    1, verbose)
                moving_window = 2

            doubling_time = []
            for w in range(int_length - moving_window + 1):
                this_start = start_day + w
                this_end = this_start + moving_window
                this_doubling_time = get_doubling_time(sim,
                                                       series=series,
                                                       start_day=this_start,
                                                       end_day=this_end,
                                                       exp_approx=exp_approx)
                doubling_time.append(this_doubling_time)

    # Do calculations
    else:
        if not exp_approx:
            try:
                import statsmodels.api as sm
            except ModuleNotFoundError as E:
                errormsg = f'Could not import statsmodels ({E}), falling back to exponential approximation'
                print(errormsg)
                exp_approx = True
        if exp_approx:
            if series[start_day] > 0:
                r = series[end_day] / series[start_day]
                if r > 1:
                    doubling_time = int_length * np.log(2) / np.log(r)
                    doubling_time = min(
                        doubling_time,
                        max_doubling_time)  # Otherwise, it's unbounded
            else:
                raise ValueError(
                    f"Can't calculate doubling time with exponential approximation when initial value is zero."
                )
        else:

            if np.any(series[start_day:end_day]
                      ):  # Deal with zero values if possible
                nonzero = np.nonzero(series[start_day:end_day])[0]
                if len(nonzero) >= 2:
                    exog = sm.add_constant(np.arange(len(nonzero)))
                    endog = np.log2((series[start_day:end_day])[nonzero])
                    model = sm.OLS(endog, exog)
                    doubling_rate = model.fit().params[1]
                    if doubling_rate > eps:
                        doubling_time = 1.0 / doubling_rate
                    else:
                        doubling_time = max_doubling_time
                else:
                    raise ValueError(
                        f"Can't calculate doubling time for series {series[start_day:end_day]}. Check whether series is growing."
                    )
            else:
                raise ValueError(
                    f"Can't calculate doubling time for series {series[start_day:end_day]}. Check whether series is growing."
                )

    return doubling_time
示例#2
0
def set_prognoses(sim, popdict):
    '''
    Determine the prognosis of an infected person: probability of being aymptomatic, or if symptoms develop, probability
    of developing severe symptoms and dying, based on their age
    '''

    # Initialize input and output
    by_age = sim['prog_by_age']
    ages = sc.promotetoarray(popdict['age'])  # Ensure it's an array
    n = len(ages)
    prognoses = sc.objdict()

    prog_pars = cvpars.get_default_prognoses(by_age=by_age)

    # If not by age, same value for everyone
    if not by_age:

        prognoses.symp_prob = sim[
            'rel_symp_prob'] * prog_pars.symp_prob * np.ones(n)
        prognoses.severe_prob = sim[
            'rel_severe_prob'] * prog_pars.severe_prob * np.ones(n)
        prognoses.crit_prob = sim[
            'rel_crit_prob'] * prog_pars.crit_prob * np.ones(n)
        prognoses.death_prob = sim[
            'rel_death_prob'] * prog_pars.death_prob * np.ones(n)

    # Otherwise, calculate probabilities of symptoms, severe symptoms, and death by age
    else:
        # Conditional probabilities of severe symptoms (given symptomatic) and death (given severe symptoms)
        severe_if_sym = np.array(
            [
                sev / sym if sym > 0 and sev / sym > 0 else 0
                for (sev,
                     sym) in zip(prog_pars.severe_probs, prog_pars.symp_probs)
            ]
        )  # Conditional probabilty of developing severe symptoms, given symptomatic
        crit_if_severe = np.array(
            [
                crit / sev if sev > 0 and crit / sev > 0 else 0
                for (crit,
                     sev) in zip(prog_pars.crit_probs, prog_pars.severe_probs)
            ]
        )  # Conditional probabilty of developing critical symptoms, given severe
        death_if_crit = np.array([
            d / c if c > 0 and d / c > 0 else 0
            for (d, c) in zip(prog_pars.death_probs, prog_pars.crit_probs)
        ])  # Conditional probabilty of dying, given critical

        symp_probs = sim[
            'rel_symp_prob'] * prog_pars.symp_probs  # Overall probability of developing symptoms
        severe_if_sym = sim[
            'rel_severe_prob'] * severe_if_sym  # Overall probability of developing severe symptoms (https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf)
        crit_if_severe = sim[
            'rel_crit_prob'] * crit_if_severe  # Overall probability of developing critical symptoms (derived from https://www.cdc.gov/mmwr/volumes/69/wr/mm6912e2.htm)
        death_if_crit = sim[
            'rel_death_prob'] * death_if_crit  # Overall probability of dying (https://www.imperial.ac.uk/media/imperial-college/medicine/sph/ide/gida-fellowships/Imperial-College-COVID19-NPI-modelling-16-03-2020.pdf)

        # Calculate prognosis for each person
        symp_prob, severe_prob, crit_prob, death_prob = [], [], [], []
        age_cutoffs = prog_pars.age_cutoffs
        for age in ages:
            # Figure out which probability applies to a person of the specified age
            ind = next((ind for ind, val in enumerate(
                [True if age < cutoff else False
                 for cutoff in age_cutoffs]) if val), -1)
            this_symp_prob = symp_probs[
                ind]  # Probability of developing symptoms
            this_severe_prob = severe_if_sym[
                ind]  # Probability of developing severe symptoms
            this_crit_prob = crit_if_severe[
                ind]  # Probability of developing critical symptoms
            this_death_prob = death_if_crit[
                ind]  # Probability of dying after developing critical symptoms
            symp_prob.append(this_symp_prob)
            severe_prob.append(this_severe_prob)
            crit_prob.append(this_crit_prob)
            death_prob.append(this_death_prob)

        # Return output
        prognoses.symp_prob = symp_prob
        prognoses.severe_prob = severe_prob
        prognoses.crit_prob = crit_prob
        prognoses.death_prob = death_prob

    popdict.update(prognoses)  # Add keys to popdict

    return