vivarium-public-health 3.0.3__py3-none-any.whl → 3.0.4__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.
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)