vivarium-public-health 3.0.3__py3-none-any.whl → 3.0.4__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. vivarium_public_health/_version.py +1 -1
  2. vivarium_public_health/disease/state.py +24 -19
  3. vivarium_public_health/mslt/delay.py +13 -5
  4. vivarium_public_health/mslt/disease.py +35 -14
  5. vivarium_public_health/mslt/intervention.py +12 -9
  6. vivarium_public_health/mslt/observer.py +56 -17
  7. vivarium_public_health/mslt/population.py +7 -10
  8. vivarium_public_health/plugins/parser.py +29 -80
  9. vivarium_public_health/population/add_new_birth_cohorts.py +8 -9
  10. vivarium_public_health/population/base_population.py +0 -5
  11. vivarium_public_health/population/data_transformations.py +1 -8
  12. vivarium_public_health/population/mortality.py +3 -3
  13. vivarium_public_health/results/columns.py +1 -1
  14. vivarium_public_health/results/disability.py +85 -11
  15. vivarium_public_health/results/disease.py +125 -2
  16. vivarium_public_health/results/mortality.py +78 -2
  17. vivarium_public_health/results/observer.py +141 -6
  18. vivarium_public_health/results/risk.py +66 -5
  19. vivarium_public_health/results/simple_cause.py +8 -2
  20. vivarium_public_health/results/stratification.py +39 -14
  21. vivarium_public_health/risks/base_risk.py +14 -16
  22. vivarium_public_health/risks/data_transformations.py +3 -1
  23. vivarium_public_health/risks/distributions.py +0 -1
  24. vivarium_public_health/risks/effect.py +31 -29
  25. vivarium_public_health/risks/implementations/low_birth_weight_and_short_gestation.py +51 -30
  26. vivarium_public_health/treatment/scale_up.py +6 -10
  27. vivarium_public_health/treatment/therapeutic_inertia.py +3 -1
  28. {vivarium_public_health-3.0.3.dist-info → vivarium_public_health-3.0.4.dist-info}/METADATA +1 -1
  29. vivarium_public_health-3.0.4.dist-info/RECORD +49 -0
  30. {vivarium_public_health-3.0.3.dist-info → vivarium_public_health-3.0.4.dist-info}/WHEEL +1 -1
  31. vivarium_public_health-3.0.3.dist-info/RECORD +0 -49
  32. {vivarium_public_health-3.0.3.dist-info → vivarium_public_health-3.0.4.dist-info}/LICENSE.txt +0 -0
  33. {vivarium_public_health-3.0.3.dist-info → vivarium_public_health-3.0.4.dist-info}/top_level.txt +0 -0
@@ -41,6 +41,24 @@ class DiseaseObserver(PublicHealthObserver):
41
41
  - "sex"
42
42
  include:
43
43
  - "sample_stratification"
44
+
45
+ Attributes
46
+ ----------
47
+ disease
48
+ The name of the disease being observed.
49
+ previous_state_column_name
50
+ The name of the column that stores the previous state of the disease.
51
+ step_size
52
+ The time step size of the simulation.
53
+ disease_model
54
+ The disease model for the disease being observed.
55
+ entity_type
56
+ The type of entity being observed.
57
+ entity
58
+ The entity being observed.
59
+ transition_stratification_name
60
+ The stratification name for transitions between disease states.
61
+
44
62
  """
45
63
 
46
64
  ##############
@@ -49,6 +67,9 @@ class DiseaseObserver(PublicHealthObserver):
49
67
 
50
68
  @property
51
69
  def configuration_defaults(self) -> Dict[str, Any]:
70
+ """A dictionary containing the defaults for any configurations managed by
71
+ this component.
72
+ """
52
73
  return {
53
74
  "stratification": {
54
75
  self.disease: super().configuration_defaults["stratification"][
@@ -59,14 +80,17 @@ class DiseaseObserver(PublicHealthObserver):
59
80
 
60
81
  @property
61
82
  def columns_created(self) -> List[str]:
83
+ """Columns created by this observer."""
62
84
  return [self.previous_state_column_name]
63
85
 
64
86
  @property
65
87
  def columns_required(self) -> List[str]:
88
+ """Columns required by this observer."""
66
89
  return [self.disease]
67
90
 
68
91
  @property
69
92
  def initialization_requirements(self) -> Dict[str, List[str]]:
93
+ """Requirements for observer initialization."""
70
94
  return {
71
95
  "requires_columns": [self.disease],
72
96
  }
@@ -76,6 +100,13 @@ class DiseaseObserver(PublicHealthObserver):
76
100
  #####################
77
101
 
78
102
  def __init__(self, disease: str) -> None:
103
+ """Constructor for this observer.
104
+
105
+ Parameters
106
+ ----------
107
+ disease
108
+ The name of the disease being observed.
109
+ """
79
110
  super().__init__()
80
111
  self.disease = disease
81
112
  self.previous_state_column_name = f"previous_{self.disease}"
@@ -85,6 +116,7 @@ class DiseaseObserver(PublicHealthObserver):
85
116
  #################
86
117
 
87
118
  def setup(self, builder: Builder) -> None:
119
+ """Set up the observer."""
88
120
  self.step_size = builder.time.step_size()
89
121
  self.disease_model = builder.components.get_component(f"disease_model.{self.disease}")
90
122
  self.entity_type = self.disease_model.cause_type
@@ -92,10 +124,35 @@ class DiseaseObserver(PublicHealthObserver):
92
124
  self.transition_stratification_name = f"transition_{self.disease}"
93
125
 
94
126
  def get_configuration(self, builder: Builder) -> LayeredConfigTree:
127
+ """Get the stratification configuration for this observer.
128
+
129
+ Parameters
130
+ ----------
131
+ builder
132
+ The builder object for the simulation.
133
+
134
+ Returns
135
+ -------
136
+ The stratification configuration for this observer.
137
+ """
95
138
  return builder.configuration.stratification[self.disease]
96
139
 
97
140
  def register_observations(self, builder: Builder) -> None:
98
-
141
+ """Register stratifications and observations.
142
+
143
+ Notes
144
+ -----
145
+ Ideally, each observer registers a single observation. This one, however,
146
+ registeres two.
147
+
148
+ While it's typical for all stratification registrations to be encapsulated
149
+ in a single class (i.e. the
150
+ :class:ResultsStratifier <vivarium_public_health.results.stratification.ResultsStratifier),
151
+ this observer registers two additional stratifications. While they could
152
+ be registered in the ``ResultsStratifier`` as well, they are specific to
153
+ this observer and so they are registered here while we have easy access
154
+ to the required names and categories.
155
+ """
99
156
  self.register_disease_state_stratification(builder)
100
157
  self.register_transition_stratification(builder)
101
158
 
@@ -104,6 +161,7 @@ class DiseaseObserver(PublicHealthObserver):
104
161
  self.register_transition_count_observation(builder, pop_filter)
105
162
 
106
163
  def register_disease_state_stratification(self, builder: Builder) -> None:
164
+ """Register the disease state stratification."""
107
165
  builder.results.register_stratification(
108
166
  self.disease,
109
167
  [state.state_id for state in self.disease_model.states],
@@ -111,6 +169,20 @@ class DiseaseObserver(PublicHealthObserver):
111
169
  )
112
170
 
113
171
  def register_transition_stratification(self, builder: Builder) -> None:
172
+ """Register the transition stratification.
173
+
174
+ This stratification is used to track transitions between disease states.
175
+ It appends 'no_transition' to the list of transition categories and also
176
+ includes it as an exluded category.
177
+
178
+ Notes
179
+ -----
180
+ It is important to include 'no_transition' in bith the list of transition
181
+ categories as well as the list of excluded categories. This is because
182
+ it must exist as a category for the transition mapping to work correctly,
183
+ but then we don't want to include it later during the actual stratification
184
+ process.
185
+ """
114
186
  transitions = [
115
187
  str(transition) for transition in self.disease_model.transition_names
116
188
  ] + ["no_transition"]
@@ -130,6 +202,7 @@ class DiseaseObserver(PublicHealthObserver):
130
202
  )
131
203
 
132
204
  def register_person_time_observation(self, builder: Builder, pop_filter: str) -> None:
205
+ """Register a person time observation."""
133
206
  self.register_adding_observation(
134
207
  builder=builder,
135
208
  name=f"person_time_{self.disease}",
@@ -144,6 +217,7 @@ class DiseaseObserver(PublicHealthObserver):
144
217
  def register_transition_count_observation(
145
218
  self, builder: Builder, pop_filter: str
146
219
  ) -> None:
220
+ """Register a transition count observation."""
147
221
  self.register_adding_observation(
148
222
  builder=builder,
149
223
  name=f"transition_count_{self.disease}",
@@ -158,6 +232,17 @@ class DiseaseObserver(PublicHealthObserver):
158
232
  )
159
233
 
160
234
  def map_transitions(self, df: pd.DataFrame) -> pd.Series:
235
+ """Map previous and current disease states to transition string.
236
+
237
+ Parameters
238
+ ----------
239
+ df
240
+ The DataFrame containing the disease states.
241
+
242
+ Returns
243
+ -------
244
+ The transitions between disease states.
245
+ """
161
246
  transitions = pd.Series(index=df.index, dtype=str)
162
247
  transition_mask = df[self.previous_state_column_name] != df[self.disease]
163
248
  transitions[~transition_mask] = "no_transition"
@@ -179,7 +264,10 @@ class DiseaseObserver(PublicHealthObserver):
179
264
  self.population_view.update(pop)
180
265
 
181
266
  def on_time_step_prepare(self, event: Event) -> None:
182
- # This enables tracking of transitions between states
267
+ """Update the previous state column to the current state.
268
+
269
+ This enables tracking of transitions between states.
270
+ """
183
271
  prior_state_pop = self.population_view.get(event.index)
184
272
  prior_state_pop[self.previous_state_column_name] = prior_state_pop[self.disease]
185
273
  self.population_view.update(prior_state_pop)
@@ -189,6 +277,17 @@ class DiseaseObserver(PublicHealthObserver):
189
277
  ###############
190
278
 
191
279
  def aggregate_state_person_time(self, x: pd.DataFrame) -> float:
280
+ """Aggregate person time for the time step.
281
+
282
+ Parameters
283
+ ----------
284
+ x
285
+ The DataFrame containing the population.
286
+
287
+ Returns
288
+ -------
289
+ The aggregated person time.
290
+ """
192
291
  return len(x) * to_years(self.step_size())
193
292
 
194
293
  ##############################
@@ -196,6 +295,26 @@ class DiseaseObserver(PublicHealthObserver):
196
295
  ##############################
197
296
 
198
297
  def format(self, measure: str, results: pd.DataFrame) -> pd.DataFrame:
298
+ """Rename the appropriate column to 'sub_entity'.
299
+
300
+ The primary thing this method does is rename the appropriate column
301
+ (either the transition stratification name of the disease name, depending
302
+ on the measure) to 'sub_entity'. We do this here instead of the
303
+ 'get_sub_entity_column' method simply because we do not want the original
304
+ column at all. If we keep it here and then return it as the sub-entity
305
+ column later, the final results would have both.
306
+
307
+ Parameters
308
+ ----------
309
+ measure
310
+ The measure.
311
+ results
312
+ The results to format.
313
+
314
+ Returns
315
+ -------
316
+ The formatted results.
317
+ """
199
318
  results = results.reset_index()
200
319
  if "transition_count_" in measure:
201
320
  sub_entity = self.transition_stratification_name
@@ -205,6 +324,7 @@ class DiseaseObserver(PublicHealthObserver):
205
324
  return results
206
325
 
207
326
  def get_measure_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
327
+ """Get the 'measure' column values."""
208
328
  if "transition_count_" in measure:
209
329
  measure_name = "transition_count"
210
330
  if "person_time_" in measure:
@@ -212,11 +332,14 @@ class DiseaseObserver(PublicHealthObserver):
212
332
  return pd.Series(measure_name, index=results.index)
213
333
 
214
334
  def get_entity_type_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
335
+ """Get the 'entity_type' column values."""
215
336
  return pd.Series(self.entity_type, index=results.index)
216
337
 
217
338
  def get_entity_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
339
+ """Get the 'entity' column values."""
218
340
  return pd.Series(self.entity, index=results.index)
219
341
 
220
342
  def get_sub_entity_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
343
+ """Get the 'sub_entity' column values."""
221
344
  # The sub-entity col was created in the 'format' method
222
345
  return results[COLUMNS.SUB_ENTITY]
@@ -47,6 +47,18 @@ class MortalityObserver(PublicHealthObserver):
47
47
  This observer needs to access the has_excess_mortality attribute of the causes
48
48
  we're observing, but this attribute gets defined in the setup of the cause models.
49
49
  As a result, the model specification should list this observer after causes.
50
+
51
+ Attributes
52
+ ----------
53
+ required_death_columns
54
+ Columns required by the deaths observation.
55
+ required_yll_columns
56
+ Columns required by the ylls observation.
57
+ clock
58
+ The simulation clock.
59
+ causes_of_death
60
+ Causes of death to be observed.
61
+
50
62
  """
51
63
 
52
64
  def __init__(self) -> None:
@@ -65,12 +77,12 @@ class MortalityObserver(PublicHealthObserver):
65
77
 
66
78
  @property
67
79
  def mortality_classes(self) -> list[type]:
80
+ """The classes to be considered as causes of death."""
68
81
  return [DiseaseState, RiskAttributableDisease]
69
82
 
70
83
  @property
71
84
  def configuration_defaults(self) -> Dict[str, Any]:
72
- """
73
- A dictionary containing the defaults for any configurations managed by
85
+ """A dictionary containing the defaults for any configurations managed by
74
86
  this component.
75
87
  """
76
88
  config_defaults = super().configuration_defaults
@@ -79,6 +91,7 @@ class MortalityObserver(PublicHealthObserver):
79
91
 
80
92
  @property
81
93
  def columns_required(self) -> List[str]:
94
+ """Columns required by this observer."""
82
95
  return [
83
96
  "alive",
84
97
  "years_of_life_lost",
@@ -91,10 +104,22 @@ class MortalityObserver(PublicHealthObserver):
91
104
  #################
92
105
 
93
106
  def setup(self, builder: Builder) -> None:
107
+ """Set up the observer."""
94
108
  self.clock = builder.time.clock()
95
109
  self.set_causes_of_death(builder)
96
110
 
97
111
  def set_causes_of_death(self, builder: Builder) -> None:
112
+ """Set the causes of death to be observed.
113
+
114
+ The causes to be observed are any registered components of class types
115
+ found in the ``mortality_classes`` property.
116
+
117
+ Notes
118
+ -----
119
+ We do not actually exclude any categories in this method.
120
+
121
+ Also note that we add 'not_dead' and 'other_causes' categories here.
122
+ """
98
123
  causes_of_death = [
99
124
  cause
100
125
  for cause in builder.components.get_components_by_type(
@@ -112,9 +137,35 @@ class MortalityObserver(PublicHealthObserver):
112
137
  ]
113
138
 
114
139
  def get_configuration(self, builder: Builder) -> LayeredConfigTree:
140
+ """Get the stratification configuration for this observer.
141
+
142
+ Parameters
143
+ ----------
144
+ builder
145
+ The builder object for the simulation.
146
+
147
+ Returns
148
+ -------
149
+ The stratification configuration for this observer.
150
+ """
115
151
  return builder.configuration.stratification[self.get_configuration_name()]
116
152
 
117
153
  def register_observations(self, builder: Builder) -> None:
154
+ """Register stratifications and observations.
155
+
156
+ Notes
157
+ -----
158
+ Ideally, each observer registers a single observation. This one, however,
159
+ registeres two.
160
+
161
+ While it's typical for all stratification registrations to be encapsulated
162
+ in a single class (i.e. the
163
+ :class:ResultsStratifier <vivarium_public_health.results.stratification.ResultsStratifier),
164
+ this observer potentially registers an additional one. While it could
165
+ be registered in the ``ResultsStratifier`` as well, it is specific to
166
+ this observer and so it is registered here while we have easy access
167
+ to the required categories.
168
+ """
118
169
  pop_filter = 'alive == "dead" and tracked == True'
119
170
  additional_stratifications = self.configuration.include
120
171
  if not self.configuration.aggregate:
@@ -155,10 +206,12 @@ class MortalityObserver(PublicHealthObserver):
155
206
  ###############
156
207
 
157
208
  def count_deaths(self, x: pd.DataFrame) -> float:
209
+ """Count the number of deaths that occurred during this time step."""
158
210
  died_of_cause = x["exit_time"] > self.clock()
159
211
  return sum(died_of_cause)
160
212
 
161
213
  def calculate_ylls(self, x: pd.DataFrame) -> float:
214
+ """Calculate the years of life lost during this time step."""
162
215
  died_of_cause = x["exit_time"] > self.clock()
163
216
  return x.loc[died_of_cause, "years_of_life_lost"].sum()
164
217
 
@@ -167,6 +220,26 @@ class MortalityObserver(PublicHealthObserver):
167
220
  ##############################
168
221
 
169
222
  def format(self, measure: str, results: pd.DataFrame) -> pd.DataFrame:
223
+ """Rename the appropriate column to 'entity'.
224
+
225
+ The primary thing this method does is rename the 'cause_of_death' column
226
+ to 'entity' (or, it we are aggregating, and there is no 'cause_of_death'
227
+ column, we simply create a new 'entity' column). We do this here instead
228
+ of the 'get_entity_column' method simply because we do not want the
229
+ 'cause_of_death' at all. If we keep it here and then return it as the
230
+ entity column later, the final results would have both.
231
+
232
+ Parameters
233
+ ----------
234
+ measure
235
+ The measure.
236
+ results
237
+ The results to format.
238
+
239
+ Returns
240
+ -------
241
+ The formatted results.
242
+ """
170
243
  results = results.reset_index()
171
244
  if self.configuration.aggregate:
172
245
  results[COLUMNS.ENTITY] = "all_causes"
@@ -175,12 +248,15 @@ class MortalityObserver(PublicHealthObserver):
175
248
  return results
176
249
 
177
250
  def get_entity_type_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
251
+ """Get the 'entity_type' column values."""
178
252
  entity_type_map = {cause.state_id: cause.cause_type for cause in self.causes_of_death}
179
253
  return results[COLUMNS.ENTITY].map(entity_type_map).astype(CategoricalDtype())
180
254
 
181
255
  def get_entity_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
256
+ """Get the 'entity' column values."""
182
257
  # The entity col was created in the 'format' method
183
258
  return results[COLUMNS.ENTITY]
184
259
 
185
260
  def get_sub_entity_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
261
+ """Get the 'sub_entity' column values."""
186
262
  return results[COLUMNS.ENTITY]
@@ -8,16 +8,19 @@ from vivarium_public_health.results.columns import COLUMNS
8
8
 
9
9
 
10
10
  class PublicHealthObserver(Observer):
11
- """A convenience class for typical public health observers. It provides
12
- an entry point for registering the most common observation type
13
- as well as standardized results formatting methods to overwrite as necessary.
11
+ """A convenience class for typical public health observers.
12
+
13
+ It exposes a method for registering the most common observation type
14
+ (adding observation) as well methods for formatting public health results
15
+ in a standardized way (to be overwritten as necessary).
16
+
14
17
  """
15
18
 
16
19
  def register_adding_observation(
17
20
  self,
18
21
  builder: Builder,
19
- name,
20
- pop_filter,
22
+ name: str,
23
+ pop_filter: str,
21
24
  when: str = "collect_metrics",
22
25
  requires_columns: List[str] = [],
23
26
  requires_values: List[str] = [],
@@ -25,7 +28,42 @@ class PublicHealthObserver(Observer):
25
28
  excluded_stratifications: List[str] = [],
26
29
  aggregator_sources: Optional[List[str]] = None,
27
30
  aggregator: Callable[[pd.DataFrame], Union[float, pd.Series]] = len,
28
- ):
31
+ ) -> None:
32
+ """Registers an adding observation to the results system.
33
+
34
+ An "adding" observation is one that adds/sums new results to existing
35
+ result values. It is the most common type of observation used in public
36
+ health models.
37
+
38
+ Parameters
39
+ ----------
40
+ builder
41
+ The builder object.
42
+ name
43
+ Name of the observation. It will also be the name of the output results
44
+ file for this particular observation.
45
+ pop_filter
46
+ A Pandas query filter string to filter the population down to the
47
+ simulants who should be considered for the observation.
48
+ when
49
+ Name of the lifecycle phase the observation should happen. Valid values are:
50
+ "time_step__prepare", "time_step", "time_step__cleanup", or "collect_metrics".
51
+ requires_columns
52
+ List of the state table columns that are required by either the `pop_filter`
53
+ or the `aggregator`.
54
+ requires_values
55
+ List of the value pipelines that are required by either the `pop_filter`
56
+ or the `aggregator`.
57
+ additional_stratifications
58
+ List of additional stratification names by which to stratify this
59
+ observation by.
60
+ excluded_stratifications
61
+ List of default stratification names to remove from this observation.
62
+ aggregator_sources
63
+ List of population view columns to be used in the `aggregator`.
64
+ aggregator
65
+ Function that computes the quantity for this observation.
66
+ """
29
67
  builder.results.register_adding_observation(
30
68
  name=name,
31
69
  pop_filter=pop_filter,
@@ -42,6 +80,27 @@ class PublicHealthObserver(Observer):
42
80
  def format_results(self, measure: str, results: pd.DataFrame) -> pd.DataFrame:
43
81
  """Top-level results formatter that calls standard sub-methods to be
44
82
  overwritten as necessary.
83
+
84
+ Public health observations typically require four columns in addition to
85
+ any stratifications and results columns: 'measure', 'entity_type', 'entity',
86
+ and 'sub_entity'. This method provides a standardized way to format
87
+ results by providing five sub-methods to be overwritten as necessary:
88
+ - format()
89
+ - get_measure_column()
90
+ - get_entity_type_column()
91
+ - get_entity_column()
92
+ - get_sub_entity_column()
93
+
94
+ Parameters
95
+ ----------
96
+ measure
97
+ The measure name.
98
+ results
99
+ The raw results.
100
+
101
+ Returns
102
+ -------
103
+ The formatted results.
45
104
  """
46
105
 
47
106
  results = self.format(measure, results)
@@ -63,16 +122,92 @@ class PublicHealthObserver(Observer):
63
122
  return results[ordered_columns]
64
123
 
65
124
  def format(self, measure: str, results: pd.DataFrame) -> pd.DataFrame:
125
+ """Format results.
126
+
127
+ This method should be overwritten in subclasses to provide custom formatting
128
+ for the results.
129
+
130
+ Parameters
131
+ ----------
132
+ measure
133
+ The measure name.
134
+ results
135
+ The raw results.
136
+
137
+ Returns
138
+ -------
139
+ The formatted results.
140
+ """
66
141
  return results
67
142
 
68
143
  def get_measure_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
144
+ """Get the 'measure' column.
145
+
146
+ This method should be overwritten in subclasses to provide the 'measure' column.
147
+
148
+ Parameters
149
+ ----------
150
+ measure
151
+ The measure name.
152
+ results
153
+ The raw results.
154
+
155
+ Returns
156
+ -------
157
+ The 'measure' column values.
158
+ """
69
159
  return pd.Series(measure, index=results.index)
70
160
 
71
161
  def get_entity_type_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
162
+ """Get the 'entity_type' column.
163
+
164
+ This method should be overwritten in subclasses to provide the 'entity_type' column.
165
+
166
+ Parameters
167
+ ----------
168
+ measure
169
+ The measure name.
170
+ results
171
+ The raw results.
172
+
173
+ Returns
174
+ -------
175
+ The 'entity_type' column values.
176
+ """
72
177
  return pd.Series(None, index=results.index)
73
178
 
74
179
  def get_entity_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
180
+ """Get the 'entity' column.
181
+
182
+ This method should be overwritten in subclasses to provide the 'entity' column.
183
+
184
+ Parameters
185
+ ----------
186
+ measure
187
+ The measure name.
188
+ results
189
+ The raw results.
190
+
191
+ Returns
192
+ -------
193
+ The 'entity' column values.
194
+ """
75
195
  return pd.Series(None, index=results.index)
76
196
 
77
197
  def get_sub_entity_column(self, measure: str, results: pd.DataFrame) -> pd.Series:
198
+ """Get the 'sub_entity' column.
199
+
200
+ This method should be overwritten in subclasses to provide the 'sub_entity' column.
201
+
202
+ Parameters
203
+ ----------
204
+ measure
205
+ The measure name.
206
+ results
207
+ The raw results.
208
+
209
+ Returns
210
+ -------
211
+ The 'sub_entity' column values.
212
+ """
78
213
  return pd.Series(None, index=results.index)