11import numpy as np
22import pandas as pd
33import pytest
4+ from layered_config_tree import ConfigurationError
5+ from vivarium import InteractiveContext
6+ from vivarium .testing_utilities import TestPopulation
47
58from tests .risks .test_effect import _setup_risk_effect_simulation
69from tests .test_utilities import make_age_bins
@@ -57,11 +60,15 @@ def test_lbwsg_risk_effect_rr_pipeline(base_config, base_plugins, mock_rr_interp
5760 # Have to match age bins and rr data to make age intervals
5861 rr_data = make_categorical_data (agees )
5962 # Exposure data used for risk component
60- exposure = make_categorical_data (agees )
63+ birth_exposure = make_categorical_data (agees )
64+ exposure = birth_exposure .copy ()
65+ exposure .loc [exposure ["value" ] == 0.75 , "value" ] = 0.65
66+ exposure .loc [exposure ["value" ] == 0.25 , "value" ] = 0.35
6167
6268 # Add data dict to add to artifact
6369 data = {
64- f"{ risk .name } .birth_exposure" : exposure ,
70+ f"{ risk .name } .birth_exposure" : birth_exposure ,
71+ f"{ risk .name } .exposure" : exposure ,
6572 f"{ risk .name } .relative_risk" : rr_data ,
6673 f"{ risk .name } .population_attributable_fraction" : 0 ,
6774 f"{ risk .name } .categories" : categories ,
@@ -81,6 +88,16 @@ def test_lbwsg_risk_effect_rr_pipeline(base_config, base_plugins, mock_rr_interp
8188 )
8289 sim = _setup_risk_effect_simulation (base_config , base_plugins , risk , lbwsg_effect , data )
8390 pop = sim .get_population ()
91+ # Verify exposure is used instead of birth_exposure since age end is 1.0
92+ # Check values of pipeline match birth exposure data since age_end is 0.0
93+ exposure_pipeline_values = sim .get_value (
94+ "risk_factor.low_birth_weight_and_short_gestation.exposure_parameters"
95+ )(pop .index )
96+ assert isinstance (exposure_pipeline_values , pd .DataFrame )
97+ assert "cat81" in exposure_pipeline_values .columns
98+ assert "cat82" in exposure_pipeline_values .columns
99+ assert (exposure_pipeline_values ["cat81" ] == 0.65 ).all ()
100+ assert (exposure_pipeline_values ["cat82" ] == 0.35 ).all ()
84101
85102 expected_pipeline_name = (
86103 f"effect_of_{ lbwsg_effect .risk .name } _on_{ lbwsg_effect .target .name } .relative_risk"
@@ -120,7 +137,8 @@ def map_age_groups(value):
120137 assert (actual_rr == 1.0 ).all ()
121138
122139
123- def test_use_birth_exposure (base_config , base_plugins , mock_rr_interpolators ):
140+ @pytest .mark .parametrize ("age_end" , [0.0 , 1.0 ])
141+ def test_use_exposure (base_config , base_plugins , mock_rr_interpolators , age_end ):
124142 risk = LBWSGRisk ()
125143 lbwsg_effect = LBWSGRiskEffect ("cause.test_cause.cause_specific_mortality_rate" )
126144
@@ -135,7 +153,7 @@ def test_use_birth_exposure(base_config, base_plugins, mock_rr_interpolators):
135153 # Have to match age bins and rr data to make age intervals
136154 rr_data = make_categorical_data (ages )
137155 # Format birth exposure data
138- exposure = pd .DataFrame (
156+ birth_exposure = pd .DataFrame (
139157 {
140158 "sex" : ["Male" , "Female" , "Male" , "Female" ],
141159 "year_start" : [2021 , 2021 , 2021 , 2021 ],
@@ -144,35 +162,123 @@ def test_use_birth_exposure(base_config, base_plugins, mock_rr_interpolators):
144162 "value" : [0.75 , 0.75 , 0.25 , 0.25 ],
145163 }
146164 )
165+ exposure = birth_exposure .copy ()
166+ exposure ["value" ] = [0.65 , 0.65 , 0.35 , 0.35 ]
147167
148168 # Add data dict to add to artifact
149169 data = {
150- f"{ risk .name } .birth_exposure" : exposure ,
170+ f"{ risk .name } .birth_exposure" : birth_exposure ,
171+ f"{ risk .name } .exposure" : exposure ,
151172 f"{ risk .name } .relative_risk" : rr_data ,
152173 f"{ risk .name } .population_attributable_fraction" : 0 ,
153174 f"{ risk .name } .categories" : categories ,
154175 f"{ risk .name } .relative_risk_interpolator" : mock_rr_interpolators ,
155176 }
156177
157178 # Only have neontal age groups
158- age_start = 0.0
159- age_end = 1.0
179+ age_end = 0.0
160180 base_config .update (
161181 {
162182 "population" : {
163- "initialization_age_start" : age_start ,
183+ "initialization_age_start" : 0.0 ,
164184 "initialization_age_max" : age_end ,
165- }
185+ },
166186 }
167187 )
168188 sim = _setup_risk_effect_simulation (base_config , base_plugins , risk , lbwsg_effect , data )
169189 pop = sim .get_population ()
190+ # Check values of pipeline match birth exposure data since age_end is 0.0
191+ exposure_pipeline_values = sim .get_value (
192+ "risk_factor.low_birth_weight_and_short_gestation.exposure_parameters"
193+ )(pop .index )
194+ assert isinstance (exposure_pipeline_values , pd .DataFrame )
195+ assert "cat81" in exposure_pipeline_values .columns
196+ assert "cat82" in exposure_pipeline_values .columns
197+ exposure_values = {
198+ 0.0 : {"cat81" : 0.75 , "cat82" : 0.25 },
199+ 1.0 : {"cat81" : 0.65 , "cat82" : 0.35 },
200+ }
201+ assert (exposure_pipeline_values ["cat81" ] == exposure_values [age_end ]["cat81" ]).all ()
202+ assert (exposure_pipeline_values ["cat82" ] == exposure_values [age_end ]["cat82" ]).all ()
170203
171204 # Assert LBWSG birth exposure columns were created
172205 assert "birth_weight_exposure" in pop .columns
173206 assert "gestational_age_exposure" in pop .columns
174207
175208
209+ @pytest .mark .parametrize ("exposure_key" , ["birth_exposure" , "exposure" , "missing" ])
210+ def test_lbwsg_exposure_data_logging (exposure_key , base_config , mocker , caplog ) -> None :
211+ risk = LBWSGRisk ()
212+
213+ # Add mock data to artifact
214+ # Format birth exposure data
215+ exposure_data = pd .DataFrame (
216+ {
217+ "sex" : ["Male" , "Female" , "Male" , "Female" ],
218+ "year_start" : [2021 , 2021 , 2021 , 2021 ],
219+ "year_end" : [2022 , 2022 , 2022 , 2022 ],
220+ "parameter" : ["cat81" , "cat81" , "cat82" , "cat82" ],
221+ "value" : [0.75 , 0.75 , 0.25 , 0.25 ],
222+ }
223+ )
224+
225+ # Only have neontal age groups
226+ if exposure_key == "birth_exposure" :
227+ age_end = 0.0
228+ else :
229+ age_end = 1.0
230+
231+ if exposure_key != "missing" :
232+ no_data_dict = {
233+ "birth_exposure" : "exposure" ,
234+ "exposure" : "birth_exposure" ,
235+ }
236+ no_data_key = no_data_dict [exposure_key ]
237+ override_config = {
238+ "population" : {
239+ "initialization_age_start" : 0.0 ,
240+ "initialization_age_max" : age_end ,
241+ },
242+ risk .name : {
243+ "data_sources" : {
244+ exposure_key : exposure_data ,
245+ }
246+ },
247+ }
248+ else :
249+ override_config = {
250+ "population" : {
251+ "initialization_age_start" : 0.0 ,
252+ "initialization_age_max" : age_end ,
253+ },
254+ }
255+
256+ # Patch get_category intervals so we do not need the mock artifact
257+ mocker .patch (
258+ "vivarium_public_health.risks.implementations.low_birth_weight_and_short_gestation.LBWSGDistribution.get_category_intervals"
259+ )
260+ assert not caplog .records
261+ if exposure_key != "missing" :
262+ missing_key = "exposure" if exposure_key == "birth_exposure" else "birth_exposure"
263+ sim = InteractiveContext (
264+ base_config ,
265+ components = [TestPopulation (), risk ],
266+ configuration = override_config ,
267+ )
268+ assert f"The data for LBWSG { missing_key } is missing from the simulation"
269+ else :
270+ with pytest .raises (
271+ ConfigurationError ,
272+ match = "The LBWSG distribution requires either 'birth_exposure' or 'exposure' data to be "
273+ "available in the simulation." ,
274+ ):
275+ InteractiveContext (
276+ base_config ,
277+ components = [TestPopulation (), risk ],
278+ configuration = override_config ,
279+ )
280+
281+
176282def make_categorical_data (data : pd .DataFrame ) -> pd .DataFrame :
177283 # Takes age gropus and adds sex, years, categories, and values
178284 dfs = []
0 commit comments