UNAIDS put forward the ambitious 90-90-90 target to end the AIDS epidemic by 2030. This target aims for 90% of people living with HIV (PLHIV) to be aware of their HIV-positive status, 90% of those diagnosed to receive antiretroviral therapy, and 90% of those on treatment to have a suppressed viral load by 2020 (each reaching 95% by 2030). HIV testing remains an important bottleneck in this cascade, however, and obtaining reliable epidemiological data on the proportion of PLHIV aware of their status is difficult. Such information is nevertheless crucial to effectively monitor HIV prevention efforts. Tracking progress towards achievement of this “first 90” target could be improved by combining population-based surveys and programmatic data on the number of HIV tests performed (and yield) in a coherent deterministic/statistical model. This type of integrative systems modelling is especially useful to fully consider HIV incidence, mortality, testing behaviours, as well as to coherently combine different sources of data.
The goal of the first90 package is to provide annual estimates of the proportion of PLHIV that are aware of their status, by combining estimates of PLHIV from EPP/Spectrum, annual programmatic data on the number of HIV tests performed (and yield), and nationally-representative survey of HIV testing behaviors.
Install via Github using devtools
:
devtools::install_github("mrc-ide/first90release")
This example demonstrates basic model steps.
# Read PJNZ file(s)
pjnz <- "~/Downloads/Malawi_2018_version_8.PJNZ"
cnt <- first90::read_country(pjnz)
fp <- first90::prepare_inputs(pjnz)
# first90::prepare_inputs can also take a list of files, if using regional files
# e.g. fp <- first90::prepare_inputs(list.files("~/Documents/Data/", "CotedIvoire.*PJNZ$", full.names=TRUE, ignore.case=TRUE))
fp$popadjust <- FALSE
# We visualize the PJNZ data
first90::plot_pjnz(fp)
The following functions enable users to produce invidual plots.
pjnz_summary <- first90::get_pjnz_summary_data(fp)
first90::plot_pjnz_pop(pjnz_summary)
first90::plot_pjnz_plhiv(pjnz_summary)
first90::plot_pjnz_prv(pjnz_summary)
first90::plot_pjnz_inc(pjnz_summary)
age_group <- c('15-24','25-34','35-49')
# Import and prepare your survey data. See [guidance](SurveyDataGuidance.md)
survey_hts <- data.frame(country="Malawi",
surveyid="Survey1",
year=2000,
agegr="15-99",
sex="both",
outcome="evertest",
hivstatus="positive",
est=0.553,
se=0.0159,
ci_l=2.5652e-12,
ci_u=8958e-12,
counts=16168)
dat <- first90::select_hts(survey_hts, cnt, age_group)
# Import and prepare your programmatic data. See [guidance](ProgramDataGuidance.md)
prgm_dat <- data.frame(country = "Malawi",
year = 2010,
sex = 'both',
agegr = "15-99",
tot = 215269,
totpos = 50115,
vct = 107634,
vctpos = 25057,
anc = 107635,
ancpos = 25058)
prg_dat <- first90::select_prgmdata(prgm_dat, cnt, age_group)
# We visualize the program data
first90::plot_inputdata(prg_dat, fp)
The following functions enable users to produce invidual plots.
first90::plot_input_tot(prgm_dat, fp)
first90::plot_input_totpos(prgm_dat, fp)
first90::plot_input_anctot(prgm_dat, fp)
first90::plot_input_ancpos(prgm_dat, fp)
# ---- Enter parameters here ----
# We create the likelihood data
likdat <- first90::prepare_hts_likdat(dat, prg_dat, fp)
# Starting parameters
data("theta0", package="first90")
first90::ll_hts(theta0, fp, likdat)
#> [1] -537.5141
opt <- optim(theta0, ll_hts, fp = fp, likdat = likdat, method = "BFGS",
control = list(fnscale = -1, trace = 4, REPORT = 1, maxit = 250), hessian = TRUE)
#> initial value 537.514135
#> iter 2 value 225.403355
#> iter 3 value 197.950605
#> iter 4 value 165.579953
#> iter 5 value 145.454905
#> iter 6 value 128.346255
#> iter 7 value 111.494032
#> iter 8 value 95.202033
#> iter 9 value 79.210668
#> iter 10 value 68.875906
#> iter 11 value 62.954741
#> iter 12 value 54.275624
#> iter 13 value 47.287029
#> iter 14 value 42.366994
#> iter 15 value 39.518052
#> iter 16 value 35.799708
#> iter 17 value 34.644478
#> iter 18 value 33.809043
#> iter 19 value 33.219866
#> iter 20 value 31.156984
#> iter 21 value 30.520792
#> iter 22 value 28.957640
#> iter 23 value 28.621258
#> iter 24 value 28.240368
#> iter 25 value 27.913753
#> iter 26 value 27.533294
#> iter 27 value 27.059544
#> iter 28 value 26.579216
#> iter 29 value 26.479967
#> iter 30 value 25.877987
#> iter 31 value 25.406857
#> iter 32 value 25.165369
#> iter 33 value 25.065890
#> iter 34 value 25.031020
#> iter 35 value 24.701009
#> iter 36 value 24.298628
#> iter 37 value 24.009337
#> iter 38 value 23.886433
#> iter 39 value 23.464852
#> iter 40 value 23.167976
#> iter 41 value 23.094387
#> iter 42 value 22.716660
#> iter 43 value 22.665284
#> iter 44 value 22.440609
#> iter 45 value 21.562546
#> iter 46 value 20.846583
#> iter 47 value 19.998687
#> iter 48 value 19.969511
#> iter 49 value 19.269384
#> iter 50 value 18.944166
#> iter 51 value 17.996559
#> iter 52 value 17.089936
#> iter 53 value 16.180640
#> iter 54 value 15.990390
#> iter 55 value 15.863024
#> iter 56 value 15.806579
#> iter 57 value 15.660232
#> iter 58 value 14.976849
#> iter 59 value 14.801829
#> iter 60 value 14.631621
#> iter 61 value 14.524722
#> iter 62 value 14.502729
#> iter 63 value 14.465752
#> iter 64 value 14.363280
#> iter 65 value 14.301438
#> iter 66 value 14.179658
#> iter 67 value 14.169216
#> iter 68 value 14.164491
#> iter 69 value 14.146303
#> iter 70 value 14.143437
#> iter 71 value 14.138722
#> iter 72 value 14.138266
#> iter 73 value 14.137414
#> iter 74 value 14.137281
#> iter 75 value 14.137204
#> iter 76 value 14.136923
#> iter 77 value 14.136073
#> iter 78 value 14.135064
#> iter 79 value 14.134614
#> iter 80 value 14.133262
#> iter 81 value 14.131301
#> iter 82 value 14.129535
#> iter 83 value 14.125115
#> iter 84 value 14.122768
#> iter 85 value 14.119140
#> iter 86 value 14.116008
#> iter 87 value 14.111339
#> iter 88 value 14.106948
#> iter 89 value 14.105951
#> iter 90 value 14.100838
#> iter 91 value 14.094597
#> iter 92 value 14.094149
#> iter 93 value 14.092650
#> iter 94 value 14.092023
#> iter 95 value 14.091302
#> iter 96 value 14.090756
#> iter 97 value 14.089687
#> iter 98 value 14.088408
#> iter 99 value 14.087422
#> iter 100 value 14.087391
#> iter 101 value 14.081511
#> iter 102 value 14.080634
#> iter 103 value 14.080198
#> iter 104 value 14.080116
#> iter 105 value 14.080062
#> iter 106 value 14.079788
#> iter 107 value 14.077864
#> iter 108 value 14.077237
#> iter 109 value 14.074794
#> iter 110 value 14.074198
#> iter 111 value 14.073334
#> iter 112 value 14.072913
#> iter 113 value 14.072340
#> iter 114 value 14.071787
#> iter 115 value 14.071607
#> iter 116 value 14.071456
#> iter 117 value 14.071284
#> iter 118 value 14.070894
#> iter 119 value 14.070825
#> iter 120 value 14.070791
#> iter 121 value 14.070716
#> iter 122 value 14.070582
#> iter 123 value 14.070325
#> iter 124 value 14.070142
#> iter 125 value 14.069919
#> iter 126 value 14.069783
#> iter 127 value 14.069775
#> iter 128 value 14.069729
#> iter 129 value 14.069609
#> iter 130 value 14.069454
#> iter 131 value 14.069273
#> iter 132 value 14.069172
#> iter 133 value 14.069150
#> iter 134 value 14.069133
#> iter 135 value 14.069088
#> iter 136 value 14.068956
#> iter 137 value 14.068752
#> iter 138 value 14.068430
#> iter 139 value 14.068284
#> iter 140 value 14.067399
#> iter 141 value 14.067376
#> iter 142 value 14.067201
#> iter 143 value 14.066982
#> iter 144 value 14.066862
#> iter 145 value 14.066758
#> iter 146 value 14.066732
#> iter 147 value 14.066722
#> iter 147 value 14.066722
#> iter 148 value 14.066488
#> iter 149 value 14.066443
#> iter 150 value 14.066386
#> iter 151 value 14.066378
#> iter 152 value 14.066372
#> iter 152 value 14.066372
#> iter 153 value 14.066371
#> iter 154 value 14.066358
#> iter 154 value 14.066358
#> iter 155 value 14.066307
#> iter 155 value 14.066307
#> iter 155 value 14.066307
#> final value 14.066307
#> converged
simul <- first90::simul.test(opt, fp, sim = 400)
# ---- Plots for FITS ----
fp <- first90::create_hts_param(opt$par, fp)
mod <- first90::simmod(fp)
# ---- The Fitted Parameters ----
first90::optimized_par(opt)
#> Loading required package: Matrix
#> Parameter_Name Estimate LCI UCI
#> 1 RR testing: men in 2005 0.80 0.67 0.90
#> 2 RR testing: men in 2012 0.83 0.73 0.91
#> 3 RR re-testing 2010 2.11 1.32 3.85
#> 4 RR re-testing 2015 2.11 1.29 3.99
#> 5 RR testing: PLHIV unaware 1.45 1.21 1.64
#> 6 RR re-testing: PLHIV aware (not ART) 2010 1.35 0.10 6.20
#> 7 RR re-testing: PLHIV aware (not ART) 2021 1.38 0.13 5.78
#> 8 RR re-testing: PLHIV on ART (*RR not ART) 0.23 0.01 0.88
#> 9 RR among 25-34 men 1.56 1.33 1.81
#> 10 RR among 35+ men 2.44 0.43 5.28
#> 11 RR among 25-34 women 1.66 1.46 1.86
#> 12 RR among 35+ women 2.56 0.38 5.48
#> 13 RR OI Dx (ART Cov) 1.20 1.00 1.30
# ---- Functions for individuals model fits ----
out_evertest <- first90::get_out_evertest(mod, fp)
first90::plot_out(mod, fp, likdat, cnt, survey_hts, out_evertest, simul)
The model fits by age and sex.
first90::plot_out_strat(mod, fp, likdat, cnt, survey_hts, out_evertest, simul)
The following functions enable users to produce invidual plots.
first90::plot_out_nbtest(mod, fp, likdat, cnt, simul)
first90::plot_out_nbpostest(mod, fp, likdat, cnt, simul)
first90::plot_out_evertestneg(mod, fp, likdat, cnt, survey_hts, out_evertest, simul)
first90::plot_out_evertestpos(mod, fp, likdat, cnt, survey_hts, out_evertest, simul)
first90::plot_out_evertest(mod, fp, likdat, cnt, survey_hts, out_evertest, simul)
first90::plot_out_90s(mod, fp, likdat, cnt, out_evertest, survey_hts, simul)
first90::plot_out_evertest_fbyage(mod, fp, likdat, cnt, survey_hts, out_evertest, simul)
first90::plot_out_evertest_mbyage(mod, fp, likdat, cnt, survey_hts, out_evertest, simul)
We can compare HIV tests’ positivity through time, the estimated true yield of new HIV diagnoses, and compare those to population-level HIV prevalence.
par(mfrow = c(1,1))
first90::plot_prv_pos_yld(mod, fp, likdat, cnt, yr_pred = 2018)
We can also examine some ouptuts related to the distribution of HIV tests performed in those susceptibles to HIV and PLHIV by different awareness and treatment status. (First sets of graphs is on the absolute scale, second one on the relative scale.)
par(mfrow = c(1,2))
first90::plot_retest_test_neg(mod, fp, likdat, cnt)
first90::plot_retest_test_pos(mod, fp, likdat, cnt)
par(mfrow = c(1,2))
first90::plot_retest_test_neg(mod, fp, likdat, cnt, relative = TRUE)
first90::plot_retest_test_pos(mod, fp, likdat, cnt, relative = TRUE)
Finally, tabular outputs can be obtained by using the following functions.
# ---- Tabular outputs ----
first90::tab_out_evertest(mod, fp, simul = simul)
#> year outcome agegr sex hivstatus value lower upper
#> 1 2010 evertest 15-49 both all 13.5 12.6 15.1
#> 2 2011 evertest 15-49 both all 14.9 13.9 16.7
#> 3 2012 evertest 15-49 both all 16.3 14.9 18.9
#> 4 2013 evertest 15-49 both all 17.5 15.7 21.5
#> 5 2014 evertest 15-49 both all 18.6 16.4 24.4
#> 6 2015 evertest 15-49 both all 19.5 16.8 27.1
#> 7 2016 evertest 15-49 both all 20.5 17.1 29.6
#> 8 2017 evertest 15-49 both all 21.3 17.4 31.5
#> 9 2018 evertest 15-49 both all 22.0 17.5 33.7
#> 10 2019 evertest 15-49 both all 22.6 17.5 36.4
#> 11 2020 evertest 15-49 both all 23.0 17.3 38.5
#> 12 2021 evertest 15-49 both all 23.3 16.9 40.4
first90::tab_out_aware(mod, fp, simul = simul)
#> year outcome agegr sex hivstatus value lower upper
#> 1 2010 aware 15-49 both positive 28.3 28.2 31.7
#> 2 2011 aware 15-49 both positive 34.9 34.9 37.8
#> 3 2012 aware 15-49 both positive 41.8 41.8 44.5
#> 4 2013 aware 15-49 both positive 47.9 47.9 50.5
#> 5 2014 aware 15-49 both positive 53.1 53.1 55.9
#> 6 2015 aware 15-49 both positive 58.3 58.3 61.3
#> 7 2016 aware 15-49 both positive 64.0 64.0 66.7
#> 8 2017 aware 15-49 both positive 69.3 69.3 71.6
#> 9 2018 aware 15-49 both positive 73.7 73.7 76.1
#> 10 2019 aware 15-49 both positive 77.4 77.4 79.8
#> 11 2020 aware 15-49 both positive 80.0 79.9 82.4
#> 12 2021 aware 15-49 both positive 81.3 81.1 83.9
first90::tab_out_nbaware(mod, fp)
#> year outcome agegr sex hivstatus value
#> 1 2010 number aware 15-49 both positive 198924
#> 2 2011 number aware 15-49 both positive 252007
#> 3 2012 number aware 15-49 both positive 310329
#> 4 2013 number aware 15-49 both positive 363967
#> 5 2014 number aware 15-49 both positive 411324
#> 6 2015 number aware 15-49 both positive 458015
#> 7 2016 number aware 15-49 both positive 507482
#> 8 2017 number aware 15-49 both positive 552065
#> 9 2018 number aware 15-49 both positive 589397
#> 10 2019 number aware 15-49 both positive 619931
#> 11 2020 number aware 15-49 both positive 639431
#> 12 2021 number aware 15-49 both positive 646686
first90::tab_out_artcov(mod, fp)
#> year outcome agegr sex hivstatus value
#> 1 2010 artcov 15+ both positive 30.2
#> 2 2011 artcov 15+ both positive 37.6
#> 3 2012 artcov 15+ both positive 45.6
#> 4 2013 artcov 15+ both positive 51.4
#> 5 2014 artcov 15+ both positive 56.9
#> 6 2015 artcov 15+ both positive 61.1
#> 7 2016 artcov 15+ both positive 68.1
#> 8 2017 artcov 15+ both positive 72.6
#> 9 2018 artcov 15+ both positive 77.2
#> 10 2019 artcov 15+ both positive 80.8
#> 11 2020 artcov 15+ both positive 83.2
#> 12 2021 artcov 15+ both positive 84.3
Some tests require sample files. If you have access, Spectrum files are
available on SharePoint
here
To use them, create a directory with mkdir tests/testhat/sample_files
and copy the Malawi .PJNZ file into it.
Or if you access to the private repo, you can clone it:
git clone https://github.com/mrc-ide/shiny90_sample_files tests/testthat/sample_files
Then run r devtools::test()