Skip to content

Commit e923fec

Browse files
committed
Merge branch 'master' into mnjowe/update-copd-write-up
2 parents 9fedeeb + 8473a31 commit e923fec

25 files changed

+1727
-518
lines changed

.github/workflows/checks.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
with:
2828
lfs: false
2929
- name: Cache tox
30-
uses: actions/cache@v3
30+
uses: actions/cache@v4
3131
with:
3232
path: .tox
3333
key: tox-${{hashFiles('requirements/*.txt', 'tox.ini')}}

.github/workflows/docs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
with:
3636
lfs: true
3737
- name: Cache tox
38-
uses: actions/cache@v3
38+
uses: actions/cache@v4
3939
with:
4040
path: .tox
4141
key: tox-${{hashFiles('requirements/*.txt', 'tox.ini')}}

.github/workflows/run-on-comment.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ jobs:
126126
- name: Get comment-bot token
127127
if: always() && steps.has_permissions.outputs.result == 'true'
128128
id: get_comment_bot_token
129-
uses: peter-murray/workflow-application-token-action@b1ad34e28f4ef30582df1bd5f45df2044eb7a6b9
129+
uses: peter-murray/workflow-application-token-action@dc0413987a085fa17d19df9e47d4677cf81ffef3
130130
with:
131131
application_id: ${{ secrets.application-id }}
132132
application_private_key: ${{ secrets.application-private-key }}

.github/workflows/run-profiling.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ jobs:
9191
uses: actions/checkout@v4
9292
with:
9393
ref: ${{ github.sha }}
94+
lfs: true
9495

9596
## The profile environment produces outputs in the /results directory
9697
- name: Run profiling in dev environment
@@ -150,7 +151,7 @@ jobs:
150151
user_name: rc-softdev-admin
151152

152153
- name: Trigger website rebuild
153-
uses: peter-evans/repository-dispatch@v2
154+
uses: peter-evans/repository-dispatch@v3
154155
with:
155156
token: ${{ secrets.PROFILING_REPO_ACCESS }}
156157
repository: UCL/TLOmodel-profiling

src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py

+80-16
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import pandas as pd
1212
from matplotlib import pyplot as plt
1313

14+
from tlo import Date
1415
from tlo.analysis.utils import (
16+
CAUSE_OF_DEATH_OR_DALY_LABEL_TO_COLOR_MAP,
1517
extract_results,
1618
format_gbd,
1719
get_color_cause_of_death_or_daly_label,
@@ -166,7 +168,7 @@ def _sort_columns(df):
166168

167169
fig, ax = plt.subplots()
168170
plot_clustered_stacked(ax=ax,
169-
dfall=_dat,
171+
dfall=({k: v/1e3 for k, v in _dat.items()} if what == 'DALYs' else _dat),
170172
color_for_column_map=get_color_cause_of_death_or_daly_label,
171173
scaled=scaled,
172174
legends=False,
@@ -178,7 +180,6 @@ def _sort_columns(df):
178180
ax.set_xlabel('Age Group')
179181
ax.set_xticklabels(ax.get_xticklabels(), rotation=90)
180182

181-
# ax.set_xlim([0, 17.5])
182183
if scaled:
183184
ax.set_ylim([0, 1.05])
184185
else:
@@ -188,23 +189,22 @@ def _sort_columns(df):
188189
ax.set_ylim([0, 25_000])
189190
ax.set_yticks(np.arange(0, 30_000, 5_000))
190191
else:
191-
ax.set_ylabel(f"{what} per year\n")
192+
ax.set_ylabel(f"{what} per year (/1000)\n")
193+
ax.set_ylim([0, 2000.0])
192194

193195
# Create figure legend and remove duplicated entries, but keep the first entries
194196
handles, labels = ax.get_legend_handles_labels()
195197
lgd = dict()
196198
for k, v in zip(labels, handles):
197199
lgd.setdefault(k, v)
198-
ax.legend(reversed(lgd.values()), reversed(lgd.keys()), loc="upper right", ncol=2, fontsize=8)
200+
# ax.legend(reversed(lgd.values()), reversed(lgd.keys()), loc="upper right", ncol=2, fontsize=8)
201+
# ax.text(
202+
# 5.2, 11_000, 'GBD || Model', horizontalalignment='left', verticalalignment='bottom', fontsize=8)
203+
ax.legend().set_visible(False) # Hide legend
199204

200205
fig.tight_layout()
201206
fig.savefig(make_graph_file_name(
202207
f"{what}_{period}_{sex}_StackedBars_ModelvsGBD_{'scaled' if scaled else ''}"))
203-
204-
# ax.text(
205-
# 5.2, 11_000, 'GBD || Model', horizontalalignment='left', verticalalignment='bottom', fontsize=8)
206-
ax.legend().set_visible(False)
207-
208208
plt.close(fig)
209209

210210
# Simple pie-charts of just TLO estimates
@@ -296,9 +296,9 @@ def shift_row_to_top(df, index_to_shift):
296296
dat: outcome_by_age_pt[dat].sum(axis=0) for dat in outcome_by_age_pt.keys()
297297
}, axis=1
298298
)
299-
# todo N.B. For GBD, should really use all ages and all sex numbers from GBD to get correct uncertainty bounds
300-
# (the addition of the bounds for the sub-categories - as done here - is not strictly correct.)
301-
# ... OR use formula to make my own explicit assumption about correlation of uncertainty in different age-grps.
299+
# todo N.B. For GBD, would ideally use all ages and all sex numbers from GBD to get correct uncertainty bounds
300+
# (the addition of the bounds for the sub-categories - as done here - might over-state the uncertainty.) This
301+
# plot should be taken as indicative only.
302302

303303
select_labels = []
304304

@@ -311,6 +311,11 @@ def shift_row_to_top(df, index_to_shift):
311311

312312
for cause in all_causes:
313313

314+
if (cause == 'Other') and (what == 'DALYs'):
315+
# Skip 'Other' when plotting DALYS as it's misleading. We don't have "Other" (non-modelled) causes
316+
# of disability.
317+
continue
318+
314319
vals = tot_outcomes_by_cause.loc[(slice(None), cause), ] / 1e3
315320

316321
x = vals.at[('mean', cause), 'GBD']
@@ -367,19 +372,22 @@ def shift_row_to_top(df, index_to_shift):
367372
])
368373

369374
outcomes = outcome_by_age_pt['GBD'][("mean")]
370-
fraction_causes_modelled = (1.0 - outcomes['Other'] / outcomes.sum(axis=1))
375+
fraction_causes_modelled_overall = (1.0 - outcomes['Other'].sum() / outcomes.sum().sum())
376+
fraction_causes_modelled_by_sex_and_age = (1.0 - outcomes['Other'] / outcomes.sum(axis=1))
371377
fig, ax = plt.subplots()
372378
for sex in sexes:
373-
fraction_causes_modelled.loc[(sex, slice(None))].plot(
379+
fraction_causes_modelled_by_sex_and_age.loc[(sex, slice(None))].plot(
374380
ax=ax,
375381
color=get_color_cause_of_death_or_daly_label('Other'),
376382
linestyle=':' if sex == 'F' else '-',
377383
label=sexname(sex),
378384
lw=5,
379385
)
380-
ax.legend()
386+
ax.axhline(fraction_causes_modelled_overall, color='b',
387+
label=f'Overall: {round(100 * fraction_causes_modelled_overall)}%')
388+
ax.legend(loc='upper right')
381389
ax.set_ylim(0, 1.0)
382-
xticks = fraction_causes_modelled.index.levels[1]
390+
xticks = fraction_causes_modelled_by_sex_and_age.index.levels[1]
383391
ax.set_xticks(range(len(xticks)))
384392
ax.set_xticklabels(xticks, rotation=90)
385393
ax.grid(axis='y')
@@ -392,6 +400,62 @@ def shift_row_to_top(df, index_to_shift):
392400
plt.savefig(make_graph_file_name(f"C_{what}_{period}_coverage"))
393401
plt.close(fig)
394402

403+
# Describe the burden with respect to wealth quintile:
404+
TARGET_PERIOD = (Date(2015, 1, 1), Date(2019, 12, 31))
405+
406+
def get_total_num_dalys_by_wealth_and_label(_df):
407+
"""Return the total number of DALYS in the TARGET_PERIOD by wealth and cause label."""
408+
wealth_cats = {5: '0-19%', 4: '20-39%', 3: '40-59%', 2: '60-79%', 1: '80-100%'}
409+
410+
return _df \
411+
.loc[_df['year'].between(*[d.year for d in TARGET_PERIOD])] \
412+
.drop(columns=['date', 'year']) \
413+
.assign(
414+
li_wealth=lambda x: x['li_wealth'].map(wealth_cats).astype(
415+
pd.CategoricalDtype(wealth_cats.values(), ordered=True)
416+
)
417+
).melt(id_vars=['li_wealth'], var_name='label') \
418+
.groupby(by=['li_wealth', 'label'])['value'] \
419+
.sum()
420+
421+
total_num_dalys_by_wealth_and_label = summarize(
422+
extract_results(
423+
results_folder,
424+
module="tlo.methods.healthburden",
425+
key="dalys_by_wealth_stacked_by_age_and_time",
426+
custom_generate_series=get_total_num_dalys_by_wealth_and_label,
427+
do_scaling=True,
428+
),
429+
collapse_columns=True,
430+
only_mean=True,
431+
).unstack()
432+
433+
format_to_plot = total_num_dalys_by_wealth_and_label \
434+
.sort_index(axis=0) \
435+
.reindex(columns=CAUSE_OF_DEATH_OR_DALY_LABEL_TO_COLOR_MAP.keys(), fill_value=0.0) \
436+
.sort_index(axis=1, key=order_of_cause_of_death_or_daly_label)
437+
438+
fig, ax = plt.subplots()
439+
name_of_plot = 'DALYS by Wealth and Cause, 2015-2019'
440+
(
441+
format_to_plot / 1e6
442+
).plot.bar(stacked=True, ax=ax,
443+
color=[get_color_cause_of_death_or_daly_label(_label) for _label in format_to_plot.columns],
444+
)
445+
ax.axhline(0.0, color='black')
446+
ax.set_title(name_of_plot)
447+
ax.set_ylabel('Number of DALYs Averted (/1e6)')
448+
ax.set_ylim(0, 10)
449+
ax.set_xlabel('Wealth Percentile')
450+
ax.grid()
451+
ax.spines['top'].set_visible(False)
452+
ax.spines['right'].set_visible(False)
453+
ax.legend(ncol=3, fontsize=8, loc='upper right')
454+
ax.legend().set_visible(False)
455+
fig.tight_layout()
456+
fig.savefig(make_graph_file_name(name_of_plot.replace(' ', '_')))
457+
plt.close(fig)
458+
395459
# %% Make graphs for each of Deaths and DALYS for a specific period
396460
# make_std_graphs(what='Deaths', period='2010-2014')
397461
# make_std_graphs(what='DALYs', period='2010-2014')

src/scripts/calibration_analyses/analysis_scripts/analysis_compare_appt_usage_real_and_simulation.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ def apply(results_folder: Path, output_folder: Path, resourcefilepath: Path = No
344344

345345
# Plot Simulation vs Real usage (Across all levels and At each level) (trimmed to 0.1 and 10)
346346
# format plot
347-
def format_and_save(_fig, _ax, _name_of_plot):
347+
def format_and_save(_fig, _ax, _name_of_plot, legend=True):
348348
_ax.set_title(_name_of_plot)
349349
_ax.set_yscale('log')
350350
_ax.set_ylim(1 / 20, 20)
@@ -356,6 +356,8 @@ def format_and_save(_fig, _ax, _name_of_plot):
356356
_ax.xaxis.grid(True, which='major', linestyle='--')
357357
_ax.yaxis.grid(True, which='both', linestyle='--')
358358
_ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
359+
if not legend:
360+
_ax.legend().set_visible(False)
359361
_fig.tight_layout()
360362
_fig.savefig(make_graph_file_name(_name_of_plot.replace(',', '').replace('\n', '_').replace(' ', '_')))
361363
plt.close(_fig)
@@ -383,7 +385,7 @@ def format_and_save(_fig, _ax, _name_of_plot):
383385
if not pd.isna(rel_diff_all_levels[idx]):
384386
ax.text(idx, rel_diff_all_levels[idx]*(1+0.2), round(rel_diff_all_levels[idx], 1),
385387
ha='left', fontsize=8)
386-
format_and_save(fig, ax, name_of_plot)
388+
format_and_save(fig, ax, name_of_plot, legend=False)
387389

388390
# plot for each level
389391
rel_diff_by_levels = (
@@ -467,7 +469,7 @@ def format_real_usage():
467469
round(rel_diff_real.loc[idx, 'mean'], 1),
468470
ha='left', fontsize=8)
469471
ax.axhline(1.0, color='r')
470-
format_and_save(fig, ax, name_of_plot)
472+
format_and_save(fig, ax, name_of_plot, legend=False)
471473

472474
# Plot Simulation vs Real usage by appt type and show fraction of usage at each level
473475
# Model, Adjusted real and Unadjusted real average annual usage all normalised to 1

src/scripts/calibration_analyses/analysis_scripts/analysis_demography_calibrations.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ def get_annual_mean_usage(_df):
404404
spacing = (np.arange(len(mean_usage_mean)) % 5) == 0
405405
mean_usage_mean.loc[spacing].plot.bar(stacked=True, ax=ax, legend=False)
406406
plt.title('Proportion Females 15-49 Using Contraceptive Methods')
407-
plt.xlabel('Date')
407+
plt.xlabel('Year')
408408
plt.ylabel('Proportion')
409409

410410
fig.legend(loc=7)

0 commit comments

Comments
 (0)