Exemplo n.º 1
0
    def separate(self, date, name="Main", population=None, **kwargs):
        """
        Create a new phase with the change point.
        New phase name will be automatically determined.

        Args:
            date (str): change point, i.e. start date of the new phase
            name (str): scenario name
            population (int): population value of the change point
            kwargs: keyword arguments of PhaseUnit.set_ode()

        Returns:
            covsirphy.Scenario: self
        """
        series = self._ensure_name(name)
        try:
            phase, old = [(self.num2str(i), unit)
                          for (i, unit) in enumerate(series)
                          if date in unit][0]
        except IndexError:
            raise IndexError(f"Phase on @date ({date}) is not registered.")
        try:
            new_pre = PhaseUnit(old.start_date, self.yesterday(date),
                                old.population)
        except ValueError:
            raise ValueError(
                f"{name} scenario cannot be separated on {date} because this date is registered as a start date."
            ) from None
        new_pre.set_ode(**old.to_dict())
        new_fol = PhaseUnit(date, old.end_date, population or old.population)
        new_fol.set_ode(**kwargs)
        self._series_dict[name].replaces(phase, [new_pre, new_fol])
        return self
Exemplo n.º 2
0
    def separate(self, date, population=None, **kwargs):
        """
        Create a new phase with the change point.
        New phase name will be automatically determined.

        Args:
            date (str): change point, i.e. start date of the new phase
            population (int): population value of the change point
            kwargs: keyword arguments of PhaseUnit.set_ode() if update is necessary

        Returns:
            covsirphy.PhaseSeries
        """
        phase, old = self.find_phase(date)
        if date in self.near_change_dates():
            raise ValueError(
                f"Cannot be separated on {date} because this date is too close to registered change dates."
            )
        new_pre = PhaseUnit(old.start_date, self.yesterday(date),
                            old.population)
        setting_dict = old.to_dict()
        setting_dict.update(kwargs)
        new_pre.set_ode(**setting_dict)
        new_fol = PhaseUnit(date, old.end_date, population or old.population)
        new_fol.set_ode(model=old.model, **setting_dict)
        self._series.replaces(phase, [new_pre, new_fol])
        return self._series
Exemplo n.º 3
0
    def combine(self, phases, name="Main", population=None, **kwargs):
        """
        Combine the sequential phases as one phase.
        New phase name will be automatically determined.

        Args:
            phases (list[str]): list of phases
            name (str, optional): name of phase series
            population (int): population value of the start date
            kwargs: keyword arguments to save as phase information

        Raises:
            TypeError: @phases is not a list

        Returns:
            covsirphy.Scenario: self
        """
        series = self._ensure_name(name)
        # Sort and check @phase is a list
        if not isinstance(phases, list):
            raise TypeError("@phases must be a list of phase names.")
        phases = list(set(phases))
        if "last" in phases:
            last_phase = "last"
            phases.remove("last")
            phases = sorted(phases, key=self.str2num, reverse=False)
        else:
            phases = sorted(phases, key=self.str2num, reverse=False)
            last_phase = phases[-1]
        # Setting of the new phase
        start_date = series.unit(phases[0]).start_date
        end_date = series.unit(last_phase).end_date
        population = population or series.unit(last_phase).population
        new_unit = PhaseUnit(start_date, end_date, population)
        new_unit.set_ode(**kwargs)
        # Phases to keep
        kept_units = [
            unit for unit in series if unit < start_date or unit > end_date
        ]
        # Replace units
        self._series_dict[name].replaces(phase=None,
                                         new_list=kept_units + [new_unit],
                                         keep_old=False)
        return self
Exemplo n.º 4
0
    def delete(self, phase="last"):
        """
        Delete a phase. The phase will be combined to the previous phase.

        Args:
            phase (str): phase name, like 0th, 1st, 2nd... or 'last'

        Returns:
            covsirphy.PhaseSeries: self

        Note:
            When @phase is '0th', disable 0th phase. 0th phase will not be deleted.
            When @phase is 'last', the last phase will be deleted.
        """
        if phase == "0th":
            self.disable("0th")
            return self
        if self.unit(phase) == self.unit("last"):
            self._units = self._units[:-1]
            return self
        phase_pre = self.num2str(self.str2num(phase) - 1)
        unit_pre, unit_fol = self.unit(phase_pre), self.unit(phase)
        if unit_pre <= self.last_date and unit_fol >= self.last_date:
            phase_next = self.num2str(self.str2num(phase) + 1)
            unit_next = self.unit(phase_next)
            model = unit_next.model
            param_dict = {
                k: v
                for (k, v) in unit_next.to_dict().items()
                if k in model.PARAMETERS
            }
            unit_new = PhaseUnit(unit_fol.start_date, unit_next.end_date,
                                 unit_next.population)
            unit_new.set_ode(model=model, tau=unit_next.tau, **param_dict)
            return self
        unit_new = PhaseUnit(unit_pre.start_date, unit_fol.end_date,
                             unit_pre.population)
        model = unit_pre.model
        if model is None:
            param_dict = {}
        else:
            param_dict = {
                k: v
                for (k, v) in unit_pre.to_dict().items()
                if k in model.PARAMETERS
            }
        unit_new.set_ode(model=model, tau=unit_pre.tau, **param_dict)
        units = [
            unit for unit in [unit_new, *self._units]
            if unit not in [unit_pre, unit_fol]
        ]
        self._units = sorted(units)
        return self
Exemplo n.º 5
0
    def combine(self, phases, population=None, **kwargs):
        """
        Combine the sequential phases as one phase.
        New phase name will be automatically determined.

        Args:
            phases (list[str]): list of phases
            population (int): population value of the start date
            kwargs: keyword arguments to save as phase information

        Raises:
            TypeError: @phases is not a list

        Returns:
            covsirphy.Scenario: self
        """
        all_phases = self.all_phases()
        if "last" in set(phases):
            phases.remove("last")
            phases = sorted(phases, key=self.str2num, reverse=False)
            last_phase = "last"
        else:
            phases = sorted(phases, key=self.str2num, reverse=False)
            last_phase = phases[-1]
        self._ensure_list(phases, candidates=all_phases, name="phases")
        # Setting of the new phase
        start_date = self._series.unit(phases[0]).start_date
        end_date = self._series.unit(last_phase).end_date
        population = population or self._series.unit(last_phase).population
        new_unit = PhaseUnit(start_date, end_date, population)
        new_unit.set_ode(**kwargs)
        # Phases to keep
        kept_units = [
            unit for unit in self.series
            if unit < start_date or unit > end_date
        ]
        # Replace units
        self._series.replaces(phase=None,
                              new_list=kept_units + [new_unit],
                              keep_old=False)
        return self._series
Exemplo n.º 6
0
    def unit(self, phase="last"):
        """
        Return the unit of the phase.

        Args:
            phase (str): phase name (1st etc.) or "last"

        Returns:
            covsirphy.PhaseUnit: the unit of the phase

        Note:
            When @phase is 'last' and no phases were registered, returns A phase
            with the start/end dates are the previous date of the first date and initial population value.
        """
        if phase == "last":
            if self._units:
                return self._units[-1]
            pre_date = self.yesterday(self.first_date)
            return PhaseUnit(pre_date, pre_date, self.init_population)
        num = self.str2num(phase)
        try:
            return self._units[num]
        except IndexError:
            raise KeyError(f"{phase} phase is not registered.")
Exemplo n.º 7
0
    def add(self,
            end_date=None,
            days=None,
            population=None,
            model=None,
            tau=None,
            **kwargs):
        """
        Add a past phase.

        Args:
            end_date (str): end date of the past phase, like 22Jan2020
            days (int or None): the number of days to add
            population (int or None): population value
            model (covsirphy.ModelBase): ODE model
            tau (int or None): tau value [min], a divisor of 1440 (prioritize the previous value)
            kwargs: keyword arguments of model parameters

        Returns:
            covsirphy.PhaseSeries: self

        Note:
            If @population is None, the previous initial value will be used.
            When addition of past phases was not completed and the new phase is future phase, fill in the blank.
        """
        last_unit = self.unit(phase="last")
        # Basic information
        start_date = self.tomorrow(last_unit.end_date)
        end_date = self._calc_end_date(start_date,
                                       end_date=end_date,
                                       days=days)
        population = self._ensure_population(population
                                             or last_unit.population)
        model = model or last_unit.model
        tau = last_unit.tau or tau
        if model is None:
            param_dict = {}
        else:
            param_dict = {
                k: v
                for (k, v) in {
                    **last_unit.to_dict(),
                    **kwargs
                }.items() if k in model.PARAMETERS
            }
        # Create PhaseUnit
        unit = PhaseUnit(start_date, end_date, population)
        # Add phase if the last date is not included
        if self.last_date not in unit or unit <= self.last_date:
            unit.set_ode(model=model, tau=tau, **param_dict)
            self._units.append(unit)
            return self
        # Fill in the blank of past dates
        filling = PhaseUnit(start_date, self.last_date, population)
        filling.set_ode(model=model, tau=tau, **param_dict)
        target = PhaseUnit(self.tomorrow(self.last_date), end_date, population)
        target.set_ode(model=model, tau=tau, **param_dict)
        # Add new phase
        self._units.extend([filling, target])
        return self