Skip to content

Symptoms

Symptoms

Source code in june/epidemiology/infection/symptoms.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
class Symptoms:
    """ """
    __slots__ = (
        "disease_config",
        "tag",
        "max_tag",
        "max_severity",
        "trajectory",
        "stage",
        "time_of_symptoms_onset",
    )
    """
    Class to represent the symptoms of a person. The symptoms class composes the
    ``Infection`` class alongside the ``Transmission`` class. Once infected,
    a person is assigned a symptoms trajectory according to a health index generated
    by the ``HealthIndexGenerator``. A trajectory is a collection of symptom tags with
    characteristic timings.
    """

    def __init__(self, disease_config: DiseaseConfig, health_index=None):
        """
        Initialise the Symptoms class.

        Parameters
        ----------
        disease_config : DiseaseConfig
            Configuration object for the disease.
        health_index : np.ndarray, optional
            Health index to determine symptom trajectory.
        """
        self.disease_config = disease_config  # Store the DiseaseConfig instance
        self.max_tag = None
        self.tag = SymptomTag.from_string("exposed", disease_config.symptom_manager.symptom_tags)
        self.max_severity = random()
        self.trajectory = self._make_symptom_trajectory(
            disease_config, health_index
        )  # this also sets max_tag
        self.stage = 0
        self.time_of_symptoms_onset = self._compute_time_from_infection_to_symptoms(disease_config)


    def _compute_time_from_infection_to_symptoms(self, disease_config: DiseaseConfig) -> Optional[int]:
        """Compute the time from infection to the onset of visible symptoms.

        Args:
            disease_config (DiseaseConfig): Preloaded DiseaseConfig object for the disease.

        Returns:
            int or None: The time (in days) from infection to symptom onset, or None if asymptomatic.

        """
        # Debug: Starting method

        # Extract relevant configuration data
        default_lowest_stage_value = disease_config.symptom_manager.default_lowest_stage
        dynamic_tags = disease_config.symptom_manager.symptom_tags
        asymptomatic_value = dynamic_tags.get("asymptomatic", None)

        # Iterate through the trajectory to calculate symptom onset time
        symptoms_onset = 0
        for completion_time, tag in self.trajectory:
            symptoms_onset += completion_time

            if tag == default_lowest_stage_value:
                # Stop at the stage defined as the default lowest stage for symptoms
                break
            elif tag == asymptomatic_value:
                # If the tag is asymptomatic, there is no visible symptom onset
                return None

        return symptoms_onset

    def _make_symptom_trajectory(self, disease_config: DiseaseConfig, health_index):
        """Generate the symptom trajectory dynamically based on the disease configuration.

        Args:
            disease_config (DiseaseConfig): Preloaded configuration for the disease.
            health_index (ndarray): The health index used to map individuals to symptom tags.

        Returns:
            list: A trajectory of symptom stages with timing information.

        """
        # Use preloaded trajectory maker
        trajectory_maker = TrajectoryMakers.from_disease_config(disease_config)

        # Map health_index to probabilities using max_severity
        probability_index = np.searchsorted(health_index, self.max_severity)


        # Dynamically resolve the symptom tag using the probability index
        # Use a fallback tag if the index is out of range
        tag_name = next(
            (name for name, value in disease_config.symptom_manager.symptom_tags.items() if value == probability_index),
            disease_config.symptom_manager.default_lowest_stage,
        )

        self.max_tag = disease_config.symptom_manager.symptom_tags.get(tag_name, disease_config.symptom_manager.default_lowest_stage)

        # Retrieve the trajectory for the resolved symptom tag
        available_trajectories = trajectory_maker.trajectories

        if self.max_tag in available_trajectories:
            trajectory = available_trajectories[self.max_tag].generate_trajectory()
            return trajectory

        # Handle unexpected cases
        error_message = f"No trajectory found for symptom tag: {self.max_tag}"
        raise KeyError(error_message)


    '''def _make_symptom_trajectory(self, health_index):
        if health_index is None:
            return [(0, SymptomTag(0))]
        trajectory_maker = TrajectoryMakers.from_file()
        index_max_symptoms_tag = np.searchsorted(health_index, self.max_severity)
        self.max_tag = SymptomTag(index_max_symptoms_tag)
        return trajectory_maker[self.max_tag]'''

    def update_trajectory_stage(self, time_from_infection):
        """Updates the current symptom tag from the symptoms trajectory,
        given how much time has passed since the person was infected.

        Args:
            time_from_infection (float): Time in days since the person got infected.

        """
        if time_from_infection > self.trajectory[self.stage + 1][0]:
            self.stage += 1
            self.tag = self.trajectory[self.stage][1]

    @property
    def time_exposed(self):
        """ """
        return self.trajectory[1][0]

    @property
    def recovered(self):
        """Dynamically determine if the current symptom tag corresponds to any "recovered" stage.


        Returns:
            bool: True if the current symptom tag is a "recovered" stage, otherwise False.

        """
        # Retrieve the "recovered_stage" tags from the DiseaseConfig
        recovered_tags = self.disease_config.symptom_manager._resolve_tags("recovered_stage")
        return self.tag in recovered_tags

    @property
    def dead(self):
        """Dynamically determine if the current symptom tag corresponds to any "fatality" stage."""
        dead_tags = self.disease_config.symptom_manager._resolve_tags("fatality_stage")
        return self.tag in dead_tags

__slots__ = ('disease_config', 'tag', 'max_tag', 'max_severity', 'trajectory', 'stage', 'time_of_symptoms_onset') class-attribute instance-attribute

Class to represent the symptoms of a person. The symptoms class composes the Infection class alongside the Transmission class. Once infected, a person is assigned a symptoms trajectory according to a health index generated by the HealthIndexGenerator. A trajectory is a collection of symptom tags with characteristic timings.

dead property

Dynamically determine if the current symptom tag corresponds to any "fatality" stage.

recovered property

Dynamically determine if the current symptom tag corresponds to any "recovered" stage.

Returns:

Name Type Description
bool

True if the current symptom tag is a "recovered" stage, otherwise False.

time_exposed property

__init__(disease_config, health_index=None)

Initialise the Symptoms class.

Parameters

disease_config : DiseaseConfig Configuration object for the disease. health_index : np.ndarray, optional Health index to determine symptom trajectory.

Source code in june/epidemiology/infection/symptoms.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def __init__(self, disease_config: DiseaseConfig, health_index=None):
    """
    Initialise the Symptoms class.

    Parameters
    ----------
    disease_config : DiseaseConfig
        Configuration object for the disease.
    health_index : np.ndarray, optional
        Health index to determine symptom trajectory.
    """
    self.disease_config = disease_config  # Store the DiseaseConfig instance
    self.max_tag = None
    self.tag = SymptomTag.from_string("exposed", disease_config.symptom_manager.symptom_tags)
    self.max_severity = random()
    self.trajectory = self._make_symptom_trajectory(
        disease_config, health_index
    )  # this also sets max_tag
    self.stage = 0
    self.time_of_symptoms_onset = self._compute_time_from_infection_to_symptoms(disease_config)

update_trajectory_stage(time_from_infection)

Updates the current symptom tag from the symptoms trajectory, given how much time has passed since the person was infected.

Parameters:

Name Type Description Default
time_from_infection float

Time in days since the person got infected.

required
Source code in june/epidemiology/infection/symptoms.py
134
135
136
137
138
139
140
141
142
143
144
def update_trajectory_stage(self, time_from_infection):
    """Updates the current symptom tag from the symptoms trajectory,
    given how much time has passed since the person was infected.

    Args:
        time_from_infection (float): Time in days since the person got infected.

    """
    if time_from_infection > self.trajectory[self.stage + 1][0]:
        self.stage += 1
        self.tag = self.trajectory[self.stage][1]