emod-api 3.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- emod_api/__init__.py +1 -0
- emod_api/campaign.py +170 -0
- emod_api/channelreports/__init__.py +0 -0
- emod_api/channelreports/channels.py +433 -0
- emod_api/channelreports/icj_to_csv.py +65 -0
- emod_api/channelreports/plot_icj_means.py +149 -0
- emod_api/channelreports/plot_prop_report.py +205 -0
- emod_api/channelreports/utils.py +326 -0
- emod_api/config/__init__.py +0 -0
- emod_api/config/default_from_schema.py +16 -0
- emod_api/config/default_from_schema_no_validation.py +177 -0
- emod_api/config/from_overrides.py +135 -0
- emod_api/demographics/__init__.py +0 -0
- emod_api/demographics/age_distribution.py +163 -0
- emod_api/demographics/base_input_file.py +28 -0
- emod_api/demographics/calculators.py +159 -0
- emod_api/demographics/demographic_exceptions.py +54 -0
- emod_api/demographics/demographics.py +249 -0
- emod_api/demographics/demographics_base.py +752 -0
- emod_api/demographics/demographics_overlay.py +41 -0
- emod_api/demographics/fertility_distribution.py +235 -0
- emod_api/demographics/implicit_functions.py +112 -0
- emod_api/demographics/mortality_distribution.py +227 -0
- emod_api/demographics/node.py +456 -0
- emod_api/demographics/overlay_node.py +16 -0
- emod_api/demographics/properties_and_attributes.py +737 -0
- emod_api/demographics/service/__init__.py +0 -0
- emod_api/demographics/service/grid_construction.py +143 -0
- emod_api/demographics/service/service.py +55 -0
- emod_api/demographics/susceptibility_distribution.py +170 -0
- emod_api/demographics/updateable.py +58 -0
- emod_api/legacy/__init__.py +0 -0
- emod_api/legacy/plotAllCharts.py +230 -0
- emod_api/migration/__init__.py +0 -0
- emod_api/migration/__main__.py +22 -0
- emod_api/migration/migration.py +782 -0
- emod_api/multidim_plotter.py +80 -0
- emod_api/schema_to_class.py +440 -0
- emod_api/serialization/__init__.py +0 -0
- emod_api/serialization/census_and_mod_pop.py +48 -0
- emod_api/serialization/dtk_file_support.py +61 -0
- emod_api/serialization/dtk_file_tools.py +1378 -0
- emod_api/serialization/dtk_file_utility.py +141 -0
- emod_api/serialization/serialized_population.py +205 -0
- emod_api/spatialreports/__init__.py +0 -0
- emod_api/spatialreports/__main__.py +67 -0
- emod_api/spatialreports/plot_spat_means.py +99 -0
- emod_api/spatialreports/spatial.py +210 -0
- emod_api/utils/__init__.py +26 -0
- emod_api/utils/distributions/__init__.py +0 -0
- emod_api/utils/distributions/base_distribution.py +38 -0
- emod_api/utils/distributions/bimodal_distribution.py +64 -0
- emod_api/utils/distributions/constant_distribution.py +58 -0
- emod_api/utils/distributions/demographic_distribution_flag.py +16 -0
- emod_api/utils/distributions/distribution_type.py +15 -0
- emod_api/utils/distributions/dual_constant_distribution.py +68 -0
- emod_api/utils/distributions/dual_exponential_distribution.py +75 -0
- emod_api/utils/distributions/exponential_distribution.py +63 -0
- emod_api/utils/distributions/gaussian_distribution.py +69 -0
- emod_api/utils/distributions/log_normal_distribution.py +61 -0
- emod_api/utils/distributions/poisson_distribution.py +59 -0
- emod_api/utils/distributions/uniform_distribution.py +70 -0
- emod_api/utils/distributions/weibull_distribution.py +69 -0
- emod_api/utils/str_enum.py +6 -0
- emod_api/weather/__init__.py +0 -0
- emod_api/weather/weather.py +428 -0
- emod_api-3.0.2.dist-info/METADATA +131 -0
- emod_api-3.0.2.dist-info/RECORD +71 -0
- emod_api-3.0.2.dist-info/WHEEL +5 -0
- emod_api-3.0.2.dist-info/licenses/LICENSE +21 -0
- emod_api-3.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""emod-api spatial report module. Exposes SpatialReport and SpatialNode objects."""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SpatialNode(object):
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
Class representing a single node of a spatial report.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, node_id: int, data):
|
|
16
|
+
|
|
17
|
+
self._id = node_id
|
|
18
|
+
self._data = data
|
|
19
|
+
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def id(self) -> int:
|
|
24
|
+
"""Node ID"""
|
|
25
|
+
return self._id
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def data(self):
|
|
29
|
+
"""Time series data for this node."""
|
|
30
|
+
return self._data
|
|
31
|
+
|
|
32
|
+
def __getitem__(self, item: int) -> float:
|
|
33
|
+
"""index into node data by time step"""
|
|
34
|
+
return self._data[item]
|
|
35
|
+
|
|
36
|
+
def __setitem__(self, key: int, value: float) -> None:
|
|
37
|
+
"""index into node data by time step"""
|
|
38
|
+
self._data[key] = value
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
NUM_STEPS_INDEX = 0
|
|
43
|
+
NUM_NODES_INDEX = 1
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SpatialReport(object):
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
Class for reading (and, optionally, writing) spatial reports in EMOD/DTK format.
|
|
50
|
+
"Filtered" reports will have start > 0 and/or reporting interval > 1.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, filename: str = None, node_ids: list[int] = None, data: np.array = None, start: int = 0, interval: int = 1):
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
Args:
|
|
57
|
+
filename: file from which to read data
|
|
58
|
+
node_ids: list of node ids, must be integer values
|
|
59
|
+
data: NumPy array of data, shape must be (#values, #nodes)
|
|
60
|
+
start: time step of first sample (used with filtered reports)
|
|
61
|
+
interval: # of time steps between samples (used with filtered reports)
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
if isinstance(filename, str):
|
|
65
|
+
self._from_file(filename)
|
|
66
|
+
else:
|
|
67
|
+
self._from_node_ids_and_data(node_ids, data, start, interval)
|
|
68
|
+
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def data(self) -> np.array:
|
|
73
|
+
"""Returns full 2 dimensional NumPy array with report data. Shape is (#values, #nodes)."""
|
|
74
|
+
return self._data
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def node_ids(self) -> list[int]:
|
|
78
|
+
"""Returns list of node IDs (integers) for nodes in the report."""
|
|
79
|
+
return self._node_ids
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def nodes(self) -> dict[int, SpatialNode]:
|
|
83
|
+
"""Returns dictionary of SpatialNodes keyed on node ID."""
|
|
84
|
+
return self._nodes
|
|
85
|
+
|
|
86
|
+
# index into report by node id
|
|
87
|
+
def __getitem__(self, item: int) -> SpatialNode:
|
|
88
|
+
return self._nodes[item]
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def node_count(self) -> int:
|
|
92
|
+
"""Number of nodes in the report."""
|
|
93
|
+
return self.data.shape[NUM_NODES_INDEX]
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def time_steps(self) -> int:
|
|
97
|
+
"""Number of samples in the report."""
|
|
98
|
+
return self.data.shape[NUM_STEPS_INDEX]
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def start(self) -> int:
|
|
102
|
+
"""Time step of first sample."""
|
|
103
|
+
return self._start
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def interval(self) -> int:
|
|
107
|
+
"""Interval, in time steps, between samples."""
|
|
108
|
+
return self._interval
|
|
109
|
+
|
|
110
|
+
def write_file(self, filename: str):
|
|
111
|
+
|
|
112
|
+
"""Save current nodes and timeseries data to given file."""
|
|
113
|
+
|
|
114
|
+
with open(filename, "wb") as file:
|
|
115
|
+
np.array([self.node_count], dtype=np.uint32).tofile(file)
|
|
116
|
+
np.array([self.time_steps], dtype=np.uint32).tofile(file)
|
|
117
|
+
if self.start != 0 or self.interval != 1:
|
|
118
|
+
np.array([self.start], dtype=np.float32).tofile(file)
|
|
119
|
+
np.array([self.interval], dtype=np.float32).tofile(file)
|
|
120
|
+
np.array([self.node_ids], dtype=np.uint32).tofile(file)
|
|
121
|
+
self.data.tofile(file)
|
|
122
|
+
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
def _from_file(self, filename: str):
|
|
126
|
+
"""
|
|
127
|
+
Read binary spatial report file.
|
|
128
|
+
#nodes,
|
|
129
|
+
#time steps,
|
|
130
|
+
node ids (#nodes values),
|
|
131
|
+
data (#nodes x #time steps values)
|
|
132
|
+
"""
|
|
133
|
+
# File format:
|
|
134
|
+
# number of nodes - uint32 * 1
|
|
135
|
+
# number of time steps - uint32 * 1
|
|
136
|
+
# OPTIONAL:
|
|
137
|
+
# starting time step - float32 * 1 (integral value in reality)
|
|
138
|
+
# time step interval - float32 * 1 (integral value in reality)
|
|
139
|
+
# node ids - uint32 * number of nodes
|
|
140
|
+
# data - (float32 * number of nodes) * number of time_steps
|
|
141
|
+
|
|
142
|
+
file_size = Path(filename).stat().st_size
|
|
143
|
+
|
|
144
|
+
with open(filename, "rb") as file:
|
|
145
|
+
num_nodes = np.fromfile(file, dtype=np.uint32, count=1)[0]
|
|
146
|
+
num_time_steps = np.fromfile(file, dtype=np.uint32, count=1)[0]
|
|
147
|
+
|
|
148
|
+
simple_size = (2 + num_nodes + (num_nodes * num_time_steps)) * 4 # num_nodes, num_time_steps, node_ids, and data
|
|
149
|
+
filtered_size = simple_size + 8 # include starting time step and time step interval
|
|
150
|
+
|
|
151
|
+
if file_size == simple_size:
|
|
152
|
+
self._start = 0
|
|
153
|
+
self._interval = 1
|
|
154
|
+
elif file_size == filtered_size:
|
|
155
|
+
self._start = int(np.fromfile(file, dtype=np.float32, count=1)[0])
|
|
156
|
+
self._interval = int(np.fromfile(file, dtype=np.float32, count=1)[0])
|
|
157
|
+
assert self.start >= 0
|
|
158
|
+
assert self.interval >= 1
|
|
159
|
+
else:
|
|
160
|
+
raise RuntimeError(f"Unexpected file size {file_size}, expected {simple_size} (standard spatial report) or {filtered_size} (filtered spatial report).")
|
|
161
|
+
|
|
162
|
+
node_ids = np.fromfile(file, dtype=np.uint32, count=num_nodes)
|
|
163
|
+
data = np.fromfile(file, dtype=np.float32, count=num_nodes * num_time_steps)
|
|
164
|
+
|
|
165
|
+
# let us index data[step, node]
|
|
166
|
+
data = data.reshape((num_time_steps, num_nodes))
|
|
167
|
+
self._from_node_ids_and_data(node_ids, data, self._start, self._interval)
|
|
168
|
+
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
def _from_node_ids_and_data(self, node_ids: list, data: np.array, start: int, interval: int) -> None:
|
|
172
|
+
|
|
173
|
+
assert _is_iterable(node_ids), "node_ids must be specified and iterable"
|
|
174
|
+
concrete = list(node_ids)
|
|
175
|
+
assert len(concrete) > 0, "node_ids must not be empty"
|
|
176
|
+
assert all(map(lambda i: _isinteger(i), concrete)), "node_ids must be integers"
|
|
177
|
+
assert len(set(concrete)) == len(concrete), "node_ids must be unique"
|
|
178
|
+
self._node_ids = sorted(concrete)
|
|
179
|
+
assert data.dtype is np.dtype("float32"), "data must be np.float32"
|
|
180
|
+
assert data.shape[1] == len(
|
|
181
|
+
self._node_ids
|
|
182
|
+
), "data shape must be (#values, #nodes)"
|
|
183
|
+
self._data = data
|
|
184
|
+
|
|
185
|
+
self._node_id_to_index_map = {
|
|
186
|
+
node_ids[n]: n for n in range(data.shape[NUM_NODES_INDEX])
|
|
187
|
+
}
|
|
188
|
+
self._nodes = {
|
|
189
|
+
node_id: SpatialNode(node_id, data[:, self._node_id_to_index_map[node_id]])
|
|
190
|
+
for node_id in node_ids
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
assert int(start) >= 0, "start sample time must be >= 0"
|
|
194
|
+
self._start = int(start)
|
|
195
|
+
assert int(interval) >= 1, "sample interval must be >= 1"
|
|
196
|
+
self._interval = int(interval)
|
|
197
|
+
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _is_iterable(obj) -> bool:
|
|
202
|
+
try:
|
|
203
|
+
_ = iter(obj)
|
|
204
|
+
return True
|
|
205
|
+
except TypeError:
|
|
206
|
+
return False
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _isinteger(item) -> bool:
|
|
210
|
+
return isinstance(item, (int, np.integer))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def check_dimensionality(data, dimensionality: int) -> bool:
|
|
5
|
+
"""
|
|
6
|
+
Returns True/False: is the provided data a matrix of the specified dimensionality?
|
|
7
|
+
Args:
|
|
8
|
+
data: An object to check the dimensionality of
|
|
9
|
+
dimensionality: The expected dimensionality
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
True if data dimensionality is as expected, False if not.
|
|
13
|
+
"""
|
|
14
|
+
ret = True
|
|
15
|
+
if dimensionality == 0:
|
|
16
|
+
if isinstance(data, Iterable):
|
|
17
|
+
ret = False # expected to NOT be iterable still, but it is
|
|
18
|
+
else:
|
|
19
|
+
if isinstance(data, Iterable):
|
|
20
|
+
for item in data:
|
|
21
|
+
if check_dimensionality(data=item, dimensionality=dimensionality - 1) is False:
|
|
22
|
+
ret = False
|
|
23
|
+
break
|
|
24
|
+
else:
|
|
25
|
+
ret = False # expected to be iterable still, but it isn't
|
|
26
|
+
return ret
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from emod_api import schema_to_class as s2c
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BaseDistribution(ABC):
|
|
6
|
+
"""
|
|
7
|
+
Abstract base class for distribution classes such as UniformDistribution and ExpoentialDistribution. This class
|
|
8
|
+
should not be instantiated directly.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def set_intervention_distribution(self, intervention_object: s2c.ReadOnlyDict, prefix: str):
|
|
13
|
+
"""
|
|
14
|
+
Set the distribution parameters to the intervention object.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
intervention_object (s2c.ReadOnlyDict):
|
|
18
|
+
- The object to set.
|
|
19
|
+
prefix (str):
|
|
20
|
+
- The prefix of the parameters.
|
|
21
|
+
"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def get_demographic_distribution_parameters(self) -> dict:
|
|
26
|
+
"""
|
|
27
|
+
Yield the flag and relevant values necessary for setting a demographics distribution of the class type
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
a dict of the form: {'flag': X, 'value1': Y, 'value2': Z}
|
|
31
|
+
"""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
def _set_parameters(self, emod_object, key, value):
|
|
35
|
+
if hasattr(emod_object, key):
|
|
36
|
+
setattr(emod_object, key, value)
|
|
37
|
+
else:
|
|
38
|
+
raise AttributeError(f"Attribute {key} does not exist in {emod_object.__class__.__name__}")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from emod_api import schema_to_class as s2c
|
|
2
|
+
from emod_api.utils.distributions.base_distribution import BaseDistribution
|
|
3
|
+
from emod_api.utils.distributions.demographic_distribution_flag import DemographicDistributionFlag
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BimodalDistribution(BaseDistribution):
|
|
7
|
+
"""
|
|
8
|
+
This class represents a bimodal distribution, a type of statistical distribution with two different modes (peaks).
|
|
9
|
+
A bimodal distribution is defined by two parameters: the proportion of the second bin, user defined bin, and the
|
|
10
|
+
constant value of the second bin. The 1-proportion will be the first bin and constant value in the first bin is 1.
|
|
11
|
+
|
|
12
|
+
This distribution is not supported in EMOD interventions.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
proportion (float):
|
|
16
|
+
- The proportion of the second bin.
|
|
17
|
+
- This value should be between 0 and 1.
|
|
18
|
+
|
|
19
|
+
constant (float):
|
|
20
|
+
- The constant value of the second bin.
|
|
21
|
+
- The value should not be negative.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
>>> # Create a BimodalDistribution object.
|
|
25
|
+
>>> # In the follow example, there will be 20% of the second bin(5) and 80% of the first bin(1).
|
|
26
|
+
>>> bd = BimodalDistribution(0.2, 5)
|
|
27
|
+
>>> # The proportion and constant attributes can be accessed and updated.
|
|
28
|
+
>>> bd.proportion
|
|
29
|
+
0.2
|
|
30
|
+
>>> bd.constant
|
|
31
|
+
5
|
|
32
|
+
>>> bd.proportion = 0.6
|
|
33
|
+
>>> bd.proportion
|
|
34
|
+
0.6
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
DEMOGRAPHIC_DISTRIBUTION_FLAG = DemographicDistributionFlag.BIMODAL.value
|
|
38
|
+
|
|
39
|
+
def __init__(self, proportion: float, constant: float):
|
|
40
|
+
super().__init__()
|
|
41
|
+
if proportion < 0 or proportion > 1:
|
|
42
|
+
raise ValueError("The 'proportion' argument should be between 0 and 1.")
|
|
43
|
+
if constant < 0:
|
|
44
|
+
raise ValueError("The 'constant' argument should not be negative.")
|
|
45
|
+
self.proportion = proportion
|
|
46
|
+
self.constant = constant
|
|
47
|
+
|
|
48
|
+
def set_intervention_distribution(self, intervention_object: s2c.ReadOnlyDict, prefix: str):
|
|
49
|
+
"""
|
|
50
|
+
This function is not supported in the intervention object. Raise NotImplementedError if called.
|
|
51
|
+
"""
|
|
52
|
+
raise NotImplementedError("BimodalDistribution does not support intervention distribution. Please use "
|
|
53
|
+
"other distributions.")
|
|
54
|
+
|
|
55
|
+
def get_demographic_distribution_parameters(self) -> dict:
|
|
56
|
+
"""
|
|
57
|
+
Yield the flag and relevant values necessary for setting a demographics bimodal distribution
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
a dict of the form: {'flag': X, 'value1': Y, 'value2': Z}
|
|
61
|
+
"""
|
|
62
|
+
return {"flag": self.DEMOGRAPHIC_DISTRIBUTION_FLAG,
|
|
63
|
+
"value1": self.proportion,
|
|
64
|
+
"value2": self.constant}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from emod_api import schema_to_class as s2c
|
|
2
|
+
from emod_api.utils.distributions.base_distribution import BaseDistribution
|
|
3
|
+
from emod_api.utils.distributions.demographic_distribution_flag import DemographicDistributionFlag
|
|
4
|
+
from emod_api.utils.distributions.distribution_type import DistributionType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ConstantDistribution(BaseDistribution):
|
|
8
|
+
"""
|
|
9
|
+
This class represents a constant distribution, a type of statistical distribution where all outcomes are equally
|
|
10
|
+
likely. A constant distribution is defined by a single value that is returned for all inputs.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
value (float):
|
|
14
|
+
- The constant value that this distribution returns.
|
|
15
|
+
- The value should not be negative.
|
|
16
|
+
|
|
17
|
+
Raises:
|
|
18
|
+
ValueError: If the 'value' argument is negative.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> # Create a ConstantDistribution object.
|
|
22
|
+
>>> cd = ConstantDistribution(5)
|
|
23
|
+
>>> # The value attribute can be accessed and updated.
|
|
24
|
+
>>> cd.value
|
|
25
|
+
5
|
|
26
|
+
>>> cd.value = 10
|
|
27
|
+
>>> cd.value
|
|
28
|
+
10
|
|
29
|
+
"""
|
|
30
|
+
DEMOGRAPHIC_DISTRIBUTION_FLAG = DemographicDistributionFlag.CONSTANT.value
|
|
31
|
+
|
|
32
|
+
def __init__(self, value: float):
|
|
33
|
+
super().__init__()
|
|
34
|
+
if value < 0:
|
|
35
|
+
raise ValueError("The 'value' argument should not be negative.")
|
|
36
|
+
self.value = value
|
|
37
|
+
|
|
38
|
+
def set_intervention_distribution(self, intervention_object: s2c.ReadOnlyDict, prefix: str):
|
|
39
|
+
"""
|
|
40
|
+
Set the distribution parameters to the object.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
intervention_object (s2c.ReadOnlyDict):
|
|
44
|
+
- The object to set.
|
|
45
|
+
prefix (str):
|
|
46
|
+
- The prefix of the parameters.
|
|
47
|
+
"""
|
|
48
|
+
self._set_parameters(intervention_object, f"{prefix}_Distribution", DistributionType.CONSTANT_DISTRIBUTION.value)
|
|
49
|
+
self._set_parameters(intervention_object, f"{prefix}_Constant", self.value)
|
|
50
|
+
|
|
51
|
+
def get_demographic_distribution_parameters(self) -> dict:
|
|
52
|
+
"""
|
|
53
|
+
Yield the flag and relevant values necessary for setting a demographics constant distribution
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
a dict of the form: {'flag': X, 'value1': Y, 'value2': Z}
|
|
57
|
+
"""
|
|
58
|
+
return {"flag": self.DEMOGRAPHIC_DISTRIBUTION_FLAG, "value1": self.value, "value2": None} # value 2 not used
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DemographicDistributionFlag(Enum):
|
|
5
|
+
CONSTANT = 0
|
|
6
|
+
UNIFORM = 1
|
|
7
|
+
GAUSSIAN = 2
|
|
8
|
+
EXPONENTIAL = 3
|
|
9
|
+
POISSON = 4
|
|
10
|
+
LOG_NORMAL = 5
|
|
11
|
+
BIMODAL = 6
|
|
12
|
+
WEIBULL = 7
|
|
13
|
+
|
|
14
|
+
# Not supported for demographics
|
|
15
|
+
# DUAL_CONSTANT = -1
|
|
16
|
+
# DUAL_EXPONENTIAL = -2
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from emod_api.utils.str_enum import StrEnum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DistributionType(StrEnum):
|
|
5
|
+
NOT_INITIALIZED = 'NOT_INITIALIZED'
|
|
6
|
+
CONSTANT_DISTRIBUTION = 'CONSTANT_DISTRIBUTION'
|
|
7
|
+
UNIFORM_DISTRIBUTION = 'UNIFORM_DISTRIBUTION'
|
|
8
|
+
GAUSSIAN_DISTRIBUTION = 'GAUSSIAN_DISTRIBUTION'
|
|
9
|
+
EXPONENTIAL_DISTRIBUTION = 'EXPONENTIAL_DISTRIBUTION'
|
|
10
|
+
POISSON_DISTRIBUTION = 'POISSON_DISTRIBUTION'
|
|
11
|
+
LOG_NORMAL_DISTRIBUTION = 'LOG_NORMAL_DISTRIBUTION'
|
|
12
|
+
DUAL_CONSTANT_DISTRIBUTION = 'DUAL_CONSTANT_DISTRIBUTION'
|
|
13
|
+
WEIBULL_DISTRIBUTION = 'WEIBULL_DISTRIBUTION'
|
|
14
|
+
DUAL_EXPONENTIAL_DISTRIBUTION = 'DUAL_EXPONENTIAL_DISTRIBUTION'
|
|
15
|
+
BIMODAL_DISTRIBUTION = 'BIMODAL_DISTRIBUTION'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from emod_api import schema_to_class as s2c
|
|
2
|
+
from emod_api.utils.distributions.base_distribution import BaseDistribution
|
|
3
|
+
from emod_api.utils.distributions.distribution_type import DistributionType
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DualConstantDistribution(BaseDistribution):
|
|
7
|
+
"""
|
|
8
|
+
This class represents a dual constant distribution, a type of statistical distribution where the outcomes are
|
|
9
|
+
distributed between a constant value and zero based on a proportion. A dual constant
|
|
10
|
+
distribution is defined by two parameters: the proportion and the constant value.
|
|
11
|
+
|
|
12
|
+
This distribution is not supported in EMOD demographics.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
proportion (float):
|
|
16
|
+
- The proportion of value of zero.
|
|
17
|
+
- This value should be between 0 and 1.
|
|
18
|
+
|
|
19
|
+
constant (float):
|
|
20
|
+
- The second constant value that this distribution returns other than zero.
|
|
21
|
+
- The value should not be negative.
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
ValueError: If 'proportion' argument is not between 0 and 1 or 'constant' argument is negative.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
>>> # Create a DualConstantDistribution object.
|
|
28
|
+
>>> # In the follow example, there will be 20% of zeros and 80% of 5s.
|
|
29
|
+
>>> dcd = DualConstantDistribution(0.2, 5)
|
|
30
|
+
>>> # The proportion and constant attributes can be accessed and updated.
|
|
31
|
+
>>> dcd.proportion
|
|
32
|
+
0.2
|
|
33
|
+
>>> dcd.constant
|
|
34
|
+
5
|
|
35
|
+
>>> dcd.proportion = 0.6
|
|
36
|
+
>>> dcd.proportion
|
|
37
|
+
0.6
|
|
38
|
+
"""
|
|
39
|
+
def __init__(self, proportion: float, constant: float):
|
|
40
|
+
if proportion < 0 or proportion > 1:
|
|
41
|
+
raise ValueError("The 'proportion' argument should be between 0 and 1.")
|
|
42
|
+
if constant < 0:
|
|
43
|
+
raise ValueError("The 'constant' argument should not be negative.")
|
|
44
|
+
super().__init__()
|
|
45
|
+
self.proportion = proportion
|
|
46
|
+
self.constant = constant
|
|
47
|
+
|
|
48
|
+
def set_intervention_distribution(self, intervention_object: s2c.ReadOnlyDict, prefix: str):
|
|
49
|
+
"""
|
|
50
|
+
Set the distribution parameters to the object.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
intervention_object (s2c.ReadOnlyDict):
|
|
54
|
+
- The object to set.
|
|
55
|
+
prefix (str):
|
|
56
|
+
- The prefix of the parameters.
|
|
57
|
+
"""
|
|
58
|
+
self._set_parameters(intervention_object, f"{prefix}_Distribution",
|
|
59
|
+
DistributionType.DUAL_CONSTANT_DISTRIBUTION.value)
|
|
60
|
+
self._set_parameters(intervention_object, f"{prefix}_Proportion_0", self.proportion)
|
|
61
|
+
self._set_parameters(intervention_object, f"{prefix}_Peak_2_Value", self.constant)
|
|
62
|
+
|
|
63
|
+
def get_demographic_distribution_parameters(self) -> dict:
|
|
64
|
+
"""
|
|
65
|
+
This function is not supported in the demographic object. Raise NotImplementedError if called.
|
|
66
|
+
"""
|
|
67
|
+
raise NotImplementedError("DualConstantDistribution does not support demographic distribution. Please use "
|
|
68
|
+
"other distributions.")
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from emod_api import schema_to_class as s2c
|
|
2
|
+
from emod_api.utils.distributions.base_distribution import BaseDistribution
|
|
3
|
+
from emod_api.utils.distributions.distribution_type import DistributionType
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DualExponentialDistribution(BaseDistribution):
|
|
7
|
+
"""
|
|
8
|
+
This class represents a dual exponential distribution, a type of statistical distribution where the outcomes are
|
|
9
|
+
distributed between two exponential distributions based on a proportion. A dual exponential distribution is defined
|
|
10
|
+
by three parameters: the proportion, the first mean, and the second mean.
|
|
11
|
+
|
|
12
|
+
This distribution is not supported in EMOD demographics.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
proportion (float):
|
|
16
|
+
- The proportion of the first exponential distribution.
|
|
17
|
+
- This value should be between 0 and 1.
|
|
18
|
+
|
|
19
|
+
mean_1 (float):
|
|
20
|
+
- The mean of the first exponential distribution.
|
|
21
|
+
- This value should be positive.
|
|
22
|
+
|
|
23
|
+
mean_2 (float):
|
|
24
|
+
- The mean of the second exponential distribution.
|
|
25
|
+
- This value should be positive.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
ValueError: If 'proportion' argument is not between 0 and 1 or 'mean_1' or 'mean_2' arguments are negative.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> # Create a DualExponentialDistribution object.
|
|
32
|
+
>>> # In the follow example, there will be 20% of the first exponential distribution and 80% of the second.
|
|
33
|
+
>>> ded = DualExponentialDistribution(0.2, 1, 2)
|
|
34
|
+
>>> # The proportion, mean_1, and mean_2 attributes can be accessed and updated.
|
|
35
|
+
>>> ded.proportion
|
|
36
|
+
0.2
|
|
37
|
+
>>> ded.mean_1
|
|
38
|
+
1
|
|
39
|
+
>>> ded.mean_2
|
|
40
|
+
2
|
|
41
|
+
>>> ded.proportion = 0.6
|
|
42
|
+
>>> ded.proportion
|
|
43
|
+
0.6
|
|
44
|
+
"""
|
|
45
|
+
def __init__(self, proportion: float, mean_1: float, mean_2: float):
|
|
46
|
+
if proportion < 0 or proportion > 1:
|
|
47
|
+
raise ValueError("The 'proportion' argument should be between 0 and 1.")
|
|
48
|
+
if mean_1 <= 0 or mean_2 <= 0:
|
|
49
|
+
raise ValueError("The 'mean_1' and 'mean_2' arguments should be positive.")
|
|
50
|
+
super().__init__()
|
|
51
|
+
self.proportion = proportion
|
|
52
|
+
self.mean_1 = mean_1
|
|
53
|
+
self.mean_2 = mean_2
|
|
54
|
+
|
|
55
|
+
def set_intervention_distribution(self, intervention_object: s2c.ReadOnlyDict, prefix: str):
|
|
56
|
+
"""
|
|
57
|
+
Set the distribution parameters to the object.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
intervention_object (s2c.ReadOnlyDict):
|
|
61
|
+
- The object to set.
|
|
62
|
+
prefix (str):
|
|
63
|
+
- The prefix of the parameters.
|
|
64
|
+
"""
|
|
65
|
+
self._set_parameters(intervention_object, f"{prefix}_Distribution", DistributionType.DUAL_EXPONENTIAL_DISTRIBUTION.value)
|
|
66
|
+
self._set_parameters(intervention_object, f"{prefix}_Proportion_1", self.proportion)
|
|
67
|
+
self._set_parameters(intervention_object, f"{prefix}_Mean_1", self.mean_1)
|
|
68
|
+
self._set_parameters(intervention_object, f"{prefix}_Mean_2", self.mean_2)
|
|
69
|
+
|
|
70
|
+
def get_demographic_distribution_parameters(self) -> None:
|
|
71
|
+
"""
|
|
72
|
+
This function is not supported in the demographic object. Raise NotImplementedError if called.
|
|
73
|
+
"""
|
|
74
|
+
raise NotImplementedError("DualExponentialDistribution does not support demographic distribution. Please use "
|
|
75
|
+
"other distributions.")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from emod_api import schema_to_class as s2c
|
|
2
|
+
from emod_api.utils.distributions.base_distribution import BaseDistribution
|
|
3
|
+
from emod_api.utils.distributions.demographic_distribution_flag import DemographicDistributionFlag
|
|
4
|
+
from emod_api.utils.distributions.distribution_type import DistributionType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExponentialDistribution(BaseDistribution):
|
|
8
|
+
"""
|
|
9
|
+
This class represents an exponential distribution, a type of statistical distribution
|
|
10
|
+
where the probability of an event decreases exponentially with time.
|
|
11
|
+
An exponential distribution is defined by a single parameter: the mean, which represents the average time
|
|
12
|
+
between events.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
mean (float):
|
|
16
|
+
- The mean, also the scale parameter of the exponential distribution.
|
|
17
|
+
- It's the 1/rate parameter.
|
|
18
|
+
- This value is set during the initialization of the class instance. It can be updated using the 'update_attribute()' method.
|
|
19
|
+
- The value should not be negative.
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
ValueError: If 'mean' argument is negative.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
>>> # Create an ExponentialDistribution object.
|
|
26
|
+
>>> ed = ExponentialDistribution(1)
|
|
27
|
+
>>> # The mean attribute can be accessed and updated.
|
|
28
|
+
>>> ed.mean
|
|
29
|
+
1
|
|
30
|
+
>>> ed.mean = 2
|
|
31
|
+
>>> ed.mean
|
|
32
|
+
2
|
|
33
|
+
"""
|
|
34
|
+
DEMOGRAPHIC_DISTRIBUTION_FLAG = DemographicDistributionFlag.EXPONENTIAL.value
|
|
35
|
+
|
|
36
|
+
def __init__(self, mean: float):
|
|
37
|
+
super().__init__()
|
|
38
|
+
if mean < 0:
|
|
39
|
+
raise ValueError("The 'mean' argument should not be negative.")
|
|
40
|
+
self.mean = mean
|
|
41
|
+
|
|
42
|
+
def set_intervention_distribution(self, intervention_object: s2c.ReadOnlyDict, prefix: str):
|
|
43
|
+
"""
|
|
44
|
+
Set the distribution parameters to the object.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
intervention_object (s2c.ReadOnlyDict):
|
|
48
|
+
- The object to set.
|
|
49
|
+
prefix (str):
|
|
50
|
+
- The prefix of the parameters.
|
|
51
|
+
"""
|
|
52
|
+
self._set_parameters(intervention_object, f"{prefix}_Distribution",
|
|
53
|
+
DistributionType.EXPONENTIAL_DISTRIBUTION.value)
|
|
54
|
+
self._set_parameters(intervention_object, f"{prefix}_Exponential", self.mean)
|
|
55
|
+
|
|
56
|
+
def get_demographic_distribution_parameters(self) -> dict:
|
|
57
|
+
"""
|
|
58
|
+
Yield the flag and relevant values necessary for setting a demographics exponential distribution
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
a dict of the form: {'flag': X, 'value1': Y, 'value2': Z}
|
|
62
|
+
"""
|
|
63
|
+
return {"flag": self.DEMOGRAPHIC_DISTRIBUTION_FLAG, "value1": self.mean, "value2": None} # value2 not used
|