# Loading packageslibrary(tidyverse)library(gtsummary)library(labelled)library(scales)library(ggrepel)library(RColorBrewer)library(utils)library(patchwork)library(stats)# Data importdata <- readr::read_csv("data.csv", show_col_types =FALSE)
Table 1. Self-reported test result and the reported number of lines among participants of phase 1, and positivity rates according to different hypotheses
Figure 3. Positivity rates based on self-interpreted HIVST results or the reported number of visible lines, by distribution channel, gender and country, among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021).
Code
# Creating "key population profiles" variabledata <- data |>mutate(key_population_profile =interaction(sex, delivery_channel_grouped) |>fct_recode() |>fct_relevel("man.MSM-based channels", "woman.MSM-based channels", "man.FSW-based channels","woman.FSW-based channels", "man.Other delivery channels", "woman.Other delivery channels" ) ) |>set_variable_labels(key_population_profile ="Key population profile")# Recoding variable "key population profiles"data <- data |>mutate(key_population_profile = key_population_profile |>fct_recode("man\nMSM-\nbased\nchannels"="man.MSM-based channels","woman\nMSM-\nbased\nchannels"="woman.MSM-based channels","man\nFSW-\n based\nchannels"="man.FSW-based channels","woman\nFSW-\nbased\nchannels"="woman.FSW-based channels","man\nother\nchannels"="man.Other delivery channels","woman\nother\nchannels"="woman.Other delivery channels" ) ) |>set_variable_labels(key_population_profile ="key population profiles")dh1 <- data |>group_by(key_population_profile, country) |>summarise(complete ="Based on self-interpreted test results",n =sum(HIVST_reported_result %in%c("reactive")),N =sum(!HIVST_reported_result %in%c("DK", "R")),Lowest ="Based on self-interpreted test results",n1 =sum(HIVST_reported_result %in%c("reactive")),N1 =n(),Highest ="Based on self-interpreted test results",n2 =sum(HIVST_reported_result %in%c("reactive", "DK", "R")),N2 =n() )dh2 <- data |>group_by(key_population_profile, country) |>summarise(complete ="Based on the reported number of lines",n =sum(HIVST_reported_lines %in%c("2 lines")),N =sum(!HIVST_reported_result %in%c("DK", "R")),Lowest ="Based on the reported number of lines",n1 =sum(HIVST_reported_lines %in%c("2 lines")),N1 =n(),Highest ="Based on the reported number of lines",n2 =sum(HIVST_reported_lines %in%c("2 lines", "DK", "R")),N2 =n() )dh0 <-bind_rows(dh1, dh2) |>mutate(complete =factor(complete, levels =c("Based on self-interpreted test results", "Based on the reported number of lines")),positivity_rate = n / N,label = scales::percent(positivity_rate, suffix ="", accuracy = .1),lowest =factor(Lowest, levels =c("reported result", "reported lines")),lowest_possible_rate = n1 / N1,label1 = scales::percent(lowest_possible_rate, suffix ="", accuracy = .1),Highest =factor(Highest, levels =c("reported result", "reported lines")),highest_possible_rate = n2 / N2,label2 = scales::percent(highest_possible_rate, suffix ="", accuracy = .1) ) |>mutate(ylabel = highest_possible_rate + .01 )# Dropping cells with denominator below 25to_drop <- dh0$N <25to_drop <- dh0$N1 <25to_drop <- dh0$N2 <25dh0$positivity_rate[to_drop] <-NAdh0$lowest_possible_rate[to_drop] <-NAdh0$highest_possible_rate[to_drop] <-NAdh0$label[to_drop] <-""dh0$label[to_drop] <-"*"dh0$ylabel[to_drop] <-0fig3 <- dh0 |>ggplot() +aes(x = key_population_profile,y = positivity_rate,ymin = lowest_possible_rate,ymax = highest_possible_rate,color = complete,shape = complete, ) +geom_errorbar(position =position_dodge(width =0.9),color ="#555555",width = .2 ) +geom_point(stat ="identity",position =position_dodge(width =0.9),size =2.5 ) +geom_text(mapping =aes(label = label, y =NULL),y =-0.01,position =position_dodge(width =0.9),color ="black",size =3,vjust =1,face ="bold" ) +facet_grid(cols =vars(country)) +labs(title ="", x ="", y ="") +ggtitle("") +scale_y_continuous(labels = scales::percent, limits =c(-0.01, .25)) +scale_colour_manual(values =c("#DD3D2D", "#1B7837")) +theme_classic() +theme(panel.grid.major.y =element_line(colour ="#DDDDDD", linetype ="dotted"),legend.title =element_blank(),legend.position ="bottom" )fig3
Figure 4. Elements for the flow chart of the participant selection process for Phase 2 of the survey
126 participants who reported a reactive test and/or two lines in phase 1
120 participants who agreed to be recalled several months later
6 participants refusal to be recalled several months later
24 unreachable at the time of phase 2
96 successfully recontacted for phase 2
7 refused to participate
89 accepted to participate in phase 2 survey
1 disconnected and unreachable
10 dropped out before the end
78 completed questionnaire phase 2
Table 2. Linkage to confirmatory testing, proportion being confirmed HIV positive and treatment initiation, by reported number of lines and self-interpreted HIVST result among phase 2 eligible participants who completed their questionnaire.
Code
data <- data |>set_variable_labels(country ="Country",age_group ="Age group",marital_status ="Marital status",educational_level ="Educational level",first_time_tester ="First time tester" )# Selection of individuals eligible for phase 2 who completed their phase 2 questionnairephase2 <- data |>filter( HIVST_reported_result =="reactive"| HIVST_reported_lines =="2 lines", recontact_phase2 =="yes", status_phase2 =="questionnaires completed" ) |>mutate(all ="ALL")phase2$test_result_number_lines_reported <-droplevels(phase2$test_result_number_lines_reported)var_label(phase2$test_result_number_lines_reported) <-"Reported number of lines /self-interpreted result"var_label(phase2$all) <-"Overall"# Computing the numbers of individuals who completed phase 2 questionnairetbl_completed <- phase2 |>tbl_summary(include =c(all, test_result_number_lines_reported),statistic =~"{n}" ) |>modify_header(stat_0 ="**n**") |>modify_footnote(update =everything() ~NA)# Computing the proportion who linked to confirmatory testingtbl_linked <- phase2 |>tbl_summary(by = confirmatory_test,include =c(all, test_result_number_lines_reported),percent ="row",digits =~0 ) |>add_ci(style_fun =~label_percent(accuracy =1, suffix ="")) |>modify_column_hide(c(stat_1, ci_stat_1)) |>modify_header(stat_2 ~"**n (%)**") |>modify_footnote(update =everything() ~NA)# Computing the proportion who were confirmed HIV-positivetbl_confirmed <- phase2 |>filter(confirmatory_test =="yes") |>mutate(confirmed = confirmatory_test_result =="positive") |>tbl_summary(by = confirmed,include =c(all, test_result_number_lines_reported),percent ="row",digits =~0 ) |>add_ci(style_fun =~label_percent(accuracy =1, suffix ="")) |>modify_column_hide(c(stat_1, ci_stat_1)) |>modify_header(stat_2 ~"**n (%)**") |>modify_footnote(update =everything() ~NA)# Computing the proportion who initiated ARTtbl_initiated <- phase2 |>filter(confirmatory_test_result =="positive") |>mutate(initiated = consulted_health_prof =="yes") |>mutate(test_result_number_lines_reported =fct_drop(test_result_number_lines_reported)) |>tbl_summary(by = initiated,include =c(all, test_result_number_lines_reported),percent ="row",digits =~0 ) |>add_ci(style_fun =~label_percent(accuracy =1, suffix ="")) |>modify_column_hide(c(stat_1, ci_stat_1)) |>modify_header(stat_2 ~"**n (%)**") |>modify_footnote(update =everything() ~NA)# Merging tablestbl_merge(list(tbl_completed, tbl_linked, tbl_confirmed, tbl_initiated),tab_spanner =c("**Completed phase 2**","**Linked to confirmatory testing**","**Confirmed HIV positive**","**Initiated ART**" )) %>%bold_labels()
Characteristic
Completed phase 2
Linked to confirmatory testing
Confirmed HIV positive
Initiated ART
n
n (%)
95% CI1
n (%)
95% CI1
n (%)
95% CI1
Overall
ALL
78
34 (44%)
33%, 55%
19 (56%)
38%, 72%
18 (95%)
72%, 100%
Reported number of lines /self-interpreted result
2 lines / reactive
27
15 (56%)
36%, 74%
12 (80%)
51%, 95%
12 (100%)
70%, 100%
1 line / reactive
7
1 (14%)
1%, 58%
0 (0%)
0%, 95%
2 lines / non-reactive
25
9 (36%)
19%, 57%
3 (33%)
9%, 69%
3 (100%)
31%, 100%
2 lines / DK-R
18
8 (44%)
22%, 69%
4 (50%)
22%, 78%
3 (75%)
22%, 99%
DK-R / reactive
1
1 (100%)
5%, 100%
0 (0%)
0%, 95%
1 CI = Confidence Interval
Table S1a. Factors associated (logistic regression) with positivity rate based on the reported number of visible lines among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021)
Table S1b. Factors associated (logistic regression) with positivity rate based on self-reported HIVST, among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021).
Table S2a. Positivity rates based on self-interpreted HIVST result, by key population profiles and country, among phase 1 participants
Code
# Creation of a table of the lowest possible positivity ratestblS2a_low <- data |>to_factor() |>tbl_custom_summary(include =c(country),by = key_population_profile,stat_fns =~proportion_summary(variable ="HIVST_reported_result", value =c("reactive")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Creation of a table of positivity rates based on complete responsestblS2a_central <- data |>filter(HIVST_reported_result !="DK", HIVST_reported_result !="R") |>to_factor() |>tbl_custom_summary(include =c(country),by = key_population_profile,stat_fns =~proportion_summary(variable ="HIVST_reported_result", value =c("reactive")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Creation of a table of the highest possible positivity ratestblS2a_high <- data |>to_factor() |>tbl_custom_summary(include =c(country),by = key_population_profile,stat_fns =~proportion_summary(variable ="HIVST_reported_result", value =c("reactive", "DK", "R")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Merging the different tablestblS2a <-tbl_stack(list(tblS2a_low, tblS2a_central, tblS2a_high),group_header =c("Lowest possible rate: positivity rate based on self reported test result and DK-R are considered not reactive","Complete responses: positivity rate based on self reported test result and DK-R are excluded","Highest possible rate: positivity rate based on self reported test result and DK-R are considered reactive" ))tblS2a |>modify_header(label ~"**Variable**") |>modify_spanning_header(c("stat_1", "stat_2") ~"**MSM-based channels**",c("stat_3", "stat_4") ~"**FSW-based channels**",c("stat_5", "stat_6") ~"**Others delivery channels**", stat_0 ~"**Total**" ) |>modify_header( stat_1 ~"**Man**", stat_2 ~"**Woman**", stat_3 ~"**Man**", stat_4 ~"**Woman**", stat_5 ~"**Man**", stat_6 ~"**Woman**", stat_0 ~" " ) |>modify_footnote(update =everything() ~NA)
Variable
Total
MSM-based channels
FSW-based channels
Others delivery channels
Man
Woman
Man
Woman
Man
Woman
Lowest possible rate: positivity rate based on self reported test result and DK-R are considered not reactive
Overall
2.4% (62/2,615)
3.2% (32/997)
1.0% (1/103)
1.6% (10/620)
2.5% (17/685)
0.7% (1/137)
1.4% (1/73)
Country
Côte d'Ivoire
1.8% (25/1,390)
2.5% (16/650)
1.4% (1/73)
1.5% (5/339)
1.2% (3/245)
0% (0/60)
0% (0/23)
Mali
3.5% (34/984)
4.6% (14/306)
0% (0/29)
1.9% (5/269)
3.9% (14/360)
9.1% (1/11)
0% (0/9)
Senegal
1.2% (3/241)
4.9% (2/41)
0% (0/1)
0% (0/12)
0% (0/80)
0% (0/66)
2.4% (1/41)
Complete responses: positivity rate based on self reported test result and DK-R are excluded
Overall
2.5% (62/2,440)
3.4% (32/931)
1.0% (1/101)
1.7% (10/579)
2.7% (17/631)
0.8% (1/130)
1.5% (1/68)
Country
Côte d'Ivoire
2.0% (25/1,279)
2.7% (16/597)
1.4% (1/71)
1.6% (5/311)
1.4% (3/221)
0% (0/58)
0% (0/21)
Mali
3.6% (34/952)
4.7% (14/301)
0% (0/29)
1.9% (5/257)
4.1% (14/345)
9.1% (1/11)
0% (0/9)
Senegal
1.4% (3/209)
6.1% (2/33)
0% (0/1)
0% (0/11)
0% (0/65)
0% (0/61)
2.6% (1/38)
Highest possible rate: positivity rate based on self reported test result and DK-R are considered reactive
Overall
9.1% (237/2,615)
9.8% (98/997)
2.9% (3/103)
8.2% (51/620)
10% (71/685)
5.8% (8/137)
8.2% (6/73)
Country
Côte d'Ivoire
9.8% (136/1,390)
11% (69/650)
4.1% (3/73)
9.7% (33/339)
11% (27/245)
3.3% (2/60)
8.7% (2/23)
Mali
6.7% (66/984)
6.2% (19/306)
0% (0/29)
6.3% (17/269)
8.1% (29/360)
9.1% (1/11)
0% (0/9)
Senegal
15% (35/241)
24% (10/41)
0% (0/1)
8.3% (1/12)
19% (15/80)
7.6% (5/66)
9.8% (4/41)
Table S2b. Positivity rates based on the reported number of lines, by key population profiles and country, among phase 1 participants
Code
# Creation of a table of the lowest possible positivity ratestblS2b_low <- data |>to_factor() |>tbl_custom_summary(include =c(country),by = key_population_profile,stat_fns =~proportion_summary(variable ="HIVST_reported_lines", value =c("2 lines")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Creation of a table of positivity rates based on complete responsestblS2b_central <- data |>filter(HIVST_reported_lines !="DK", HIVST_reported_lines !="R") |>to_factor() |>tbl_custom_summary(include =c(country),by = key_population_profile,stat_fns =~proportion_summary(variable ="HIVST_reported_lines", value =c("2 lines")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Creation of a table of the highest possible positivity ratestblS2b_high <- data |>to_factor() |>tbl_custom_summary(include =c(country),by = key_population_profile,stat_fns =~proportion_summary(variable ="HIVST_reported_lines", value =c("2 lines", "DK", "R")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Merging of the different tablestblS2b <-tbl_stack(list(tblS2b_low, tblS2b_central, tblS2b_high),group_header =c("Lowest possible rate: positivity rate based on self reported number of lines and DK-R are considered not 2 lines","Complete responses: positivity rate based on self reported number of lines and DK-R are excluded","Highest possible rate: positivity rate based on self reported number of lines and DK-R are considered 2 lines" ))tblS2b |>modify_header(label ~"**Variable**") |>modify_spanning_header(c("stat_1", "stat_2") ~"**MSM-based channels**",c("stat_3", "stat_4") ~"**FSW-based channels**",c("stat_5", "stat_6") ~"**Others delivery channels**", stat_0 ~"**Total**" ) |>modify_header( stat_1 ~"**Man**", stat_2 ~"**Woman**", stat_3 ~"**Man**", stat_4 ~"**Woman**", stat_5 ~"**Man**", stat_6 ~"**Woman**", stat_0 ~" " ) |>modify_footnote(update =everything() ~NA)
Variable
Total
MSM-based channels
FSW-based channels
Others delivery channels
Man
Woman
Man
Woman
Man
Woman
Lowest possible rate: positivity rate based on self reported number of lines and DK-R are considered not 2 lines
Overall
4.4% (114/2,615)
4.7% (47/997)
4.9% (5/103)
4.5% (28/620)
4.1% (28/685)
2.9% (4/137)
2.7% (2/73)
Country
Côte d'Ivoire
3.8% (53/1,390)
4.2% (27/650)
5.5% (4/73)
4.7% (16/339)
2.0% (5/245)
0% (0/60)
4.3% (1/23)
Mali
4.9% (48/984)
4.9% (15/306)
3.4% (1/29)
4.5% (12/269)
5.3% (19/360)
9.1% (1/11)
0% (0/9)
Senegal
5.4% (13/241)
12% (5/41)
0% (0/1)
0% (0/12)
5.0% (4/80)
4.5% (3/66)
2.4% (1/41)
Complete responses: positivity rate based on self reported number of lines and DK-R are excluded
Overall
4.5% (114/2,541)
4.8% (47/977)
4.9% (5/103)
4.6% (28/605)
4.2% (28/660)
3.1% (4/128)
2.9% (2/68)
Country
Côte d'Ivoire
3.9% (53/1,368)
4.2% (27/641)
5.5% (4/73)
4.8% (16/331)
2.1% (5/241)
0% (0/60)
4.5% (1/22)
Mali
5.0% (48/955)
5.0% (15/298)
3.4% (1/29)
4.5% (12/264)
5.5% (19/344)
9.1% (1/11)
0% (0/9)
Senegal
6.0% (13/218)
13% (5/38)
0% (0/1)
0% (0/10)
5.3% (4/75)
5.3% (3/57)
2.7% (1/37)
Highest possible rate: positivity rate based on self reported number of lines and DK-R are considered 2 lines
Overall
7.2% (188/2,615)
6.7% (67/997)
4.9% (5/103)
6.9% (43/620)
7.7% (53/685)
9.5% (13/137)
9.6% (7/73)
Country
Côte d'Ivoire
5.4% (75/1,390)
5.5% (36/650)
5.5% (4/73)
7.1% (24/339)
3.7% (9/245)
0% (0/60)
8.7% (2/23)
Mali
7.8% (77/984)
7.5% (23/306)
3.4% (1/29)
6.3% (17/269)
9.7% (35/360)
9.1% (1/11)
0% (0/9)
Senegal
15% (36/241)
20% (8/41)
0% (0/1)
17% (2/12)
11% (9/80)
18% (12/66)
12% (5/41)
Table S3a. Positivity rates based on self-interpreted HIVST result, by age group and country, among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021)
Code
# Creation of a table of the lowest possible positivity ratestblS3a_low <- data |>to_factor() |>tbl_custom_summary(include =c(country),by = age_group,stat_fns =~proportion_summary(variable ="HIVST_reported_result", value =c("reactive")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Creation of a table of positivity rates based on complete responsestblS3a_central <- data |>filter(HIVST_reported_result !="DK", HIVST_reported_result !="R") |>to_factor() |>tbl_custom_summary(include =c(country),by = age_group,stat_fns =~proportion_summary(variable ="HIVST_reported_result", value =c("reactive")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Creation of a table of the highest possible positivity ratestblS3a_high <- data |>to_factor() |>tbl_custom_summary(include =c(country),by = age_group,stat_fns =~proportion_summary(variable ="HIVST_reported_result", value =c("reactive", "DK", "R")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Merging of the different tablestblS3a <-tbl_stack(list(tblS3a_low, tblS3a_central, tblS3a_high),group_header =c("Lowest possible rate: positivity rate based on self reported test result and DK-R are considered not reactive","Complete responses: positivity rate based on self reported test result and DK-R are excluded","Highest possible rate: positivity rate based on self reported test result and DK-R are considered reactive" ))tblS3a |>modify_header(label ~"")
Overall, N = 2,6151
24 years or less, N = 1,1641
25-34 years, N = 1,0631
35 years or more, N = 3881
Lowest possible rate: positivity rate based on self reported test result and DK-R are considered not reactive
Overall
2.4% (62/2,615)
2.2% (26/1,164)
2.7% (29/1,063)
1.8% (7/388)
Country
Côte d'Ivoire
1.8% (25/1,390)
1.7% (11/645)
2.0% (11/553)
1.6% (3/192)
Mali
3.5% (34/984)
3.3% (15/455)
3.9% (16/415)
2.6% (3/114)
Senegal
1.2% (3/241)
0% (0/64)
2.1% (2/95)
1.2% (1/82)
Complete responses: positivity rate based on self reported test result and DK-R are excluded
Overall
2.5% (62/2,440)
2.4% (26/1,099)
2.9% (29/991)
2.0% (7/350)
Country
Côte d'Ivoire
2.0% (25/1,279)
1.8% (11/604)
2.2% (11/506)
1.8% (3/169)
Mali
3.6% (34/952)
3.4% (15/439)
4.0% (16/403)
2.7% (3/110)
Senegal
1.4% (3/209)
0% (0/56)
2.4% (2/82)
1.4% (1/71)
Highest possible rate: positivity rate based on self reported test result and DK-R are considered reactive
Overall
9.1% (237/2,615)
7.8% (91/1,164)
9.5% (101/1,063)
12% (45/388)
Country
Côte d'Ivoire
9.8% (136/1,390)
8.1% (52/645)
10% (58/553)
14% (26/192)
Mali
6.7% (66/984)
6.8% (31/455)
6.7% (28/415)
6.1% (7/114)
Senegal
15% (35/241)
13% (8/64)
16% (15/95)
15% (12/82)
1 prop% (n/N)
Table S3b. Positivity rates based on the reported number of visible lines, by age group and country, among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021)
Code
# Creation of a table of the lowest possible positivity ratestblS3b_low <- data |>to_factor() |>tbl_custom_summary(include =c(country),by = age_group,stat_fns =~proportion_summary(variable ="HIVST_reported_lines", value =c("2 lines")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Creation of a table of positivity rates based on complete responsestblS3b_central <- data |>filter(HIVST_reported_lines !="DK", HIVST_reported_lines !="R") |>to_factor() |>tbl_custom_summary(include =c(country),by = age_group,stat_fns =~proportion_summary(variable ="HIVST_reported_lines", value =c("2 lines")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Creation of a table of the highest possible positivity ratestblS3b_high <- data |>to_factor() |>tbl_custom_summary(include =c(country),by = age_group,stat_fns =~proportion_summary(variable ="HIVST_reported_lines", value =c("2 lines", "DK", "R")),statistic =~"{prop}% ({n}/{N})",digits =~list(function(x) {style_percent(x, digits =0) }, 0, 0 ),overall_row =TRUE ) |>add_overall(last =FALSE)# Merging of the different tablestblS3b <-tbl_stack(list(tblS3b_low, tblS3b_central, tblS3b_high),group_header =c("Lowest possible rate: positivity rate based on self reported number of lines and DK-R are considered not 2 lines","Complete responses: positivity rate based on self reported number of lines and DK-R are excluded","Highest possible rate: positivity rate based on self reported number of lines and DK-R are considered 2 lines" ))tblS3b |>modify_header(label ~"")
Overall, N = 2,6151
24 years or less, N = 1,1641
25-34 years, N = 1,0631
35 years or more, N = 3881
Lowest possible rate: positivity rate based on self reported number of lines and DK-R are considered not 2 lines
Overall
4.4% (114/2,615)
3.7% (43/1,164)
4.9% (52/1,063)
4.9% (19/388)
Country
Côte d'Ivoire
3.8% (53/1,390)
3.1% (20/645)
4.5% (25/553)
4.2% (8/192)
Mali
4.9% (48/984)
4.8% (22/455)
4.8% (20/415)
5.3% (6/114)
Senegal
5.4% (13/241)
1.6% (1/64)
7.4% (7/95)
6.1% (5/82)
Complete responses: positivity rate based on self reported number of lines and DK-R are excluded
Overall
4.5% (114/2,541)
3.8% (43/1,138)
5.0% (52/1,032)
5.1% (19/371)
Country
Côte d'Ivoire
3.9% (53/1,368)
3.1% (20/637)
4.6% (25/546)
4.3% (8/185)
Mali
5.0% (48/955)
4.9% (22/447)
5.0% (20/401)
5.6% (6/107)
Senegal
6.0% (13/218)
1.9% (1/54)
8.2% (7/85)
6.3% (5/79)
Highest possible rate: positivity rate based on self reported number of lines and DK-R are considered 2 lines
Overall
7.2% (188/2,615)
5.9% (69/1,164)
7.8% (83/1,063)
9.3% (36/388)
Country
Côte d'Ivoire
5.4% (75/1,390)
4.3% (28/645)
5.8% (32/553)
7.8% (15/192)
Mali
7.8% (77/984)
6.6% (30/455)
8.2% (34/415)
11% (13/114)
Senegal
15% (36/241)
17% (11/64)
18% (17/95)
9.8% (8/82)
1 prop% (n/N)
Table S4a. Eligibility and participation in phase 2 survey by sociodemographic characteristics, distribution channel, and HIV testing history (bivariable comparison ).
eligible for phase 2 but did not complete the questionnaire, N = 481
phase 1 participants not eligible for phase 2, N = 2,4891
Overall, N = 2,6151
p-value2
Country
>0.9
Côte d'Ivoire
39 (50%)
23 (48%)
1,328 (53%)
1,390 (53%)
Mali
31 (40%)
20 (42%)
933 (37%)
984 (38%)
Senegal
8 (10%)
5 (10%)
228 (9.2%)
241 (9.2%)
Sex and distribution channel
0.3
man MSM- based channels
35 (45%)
18 (38%)
944 (38%)
997 (38%)
woman MSM- based channels
5 (6.4%)
0 (0%)
98 (3.9%)
103 (3.9%)
man FSW- based channels
22 (28%)
10 (21%)
588 (24%)
620 (24%)
woman FSW- based channels
14 (18%)
16 (33%)
655 (26%)
685 (26%)
man other channels
1 (1.3%)
3 (6.3%)
133 (5.3%)
137 (5.2%)
woman other channels
1 (1.3%)
1 (2.1%)
71 (2.9%)
73 (2.8%)
Age group
0.5
24 years or less
27 (35%)
21 (44%)
1,116 (45%)
1,164 (45%)
25-34 years
38 (49%)
20 (42%)
1,005 (40%)
1,063 (41%)
35 years or more
13 (17%)
7 (15%)
368 (15%)
388 (15%)
Marital status
0.3
single
54 (69%)
32 (67%)
1,675 (67%)
1,761 (67%)
divorced / separated / widowed
6 (7.7%)
2 (4.2%)
89 (3.6%)
97 (3.7%)
living with partner / married
18 (23%)
14 (29%)
725 (29%)
757 (29%)
Educational level
0.079
none / primary
13 (17%)
13 (27%)
477 (19%)
503 (19%)
secondary
50 (64%)
29 (60%)
1,353 (54%)
1,432 (55%)
higher
15 (19%)
6 (13%)
659 (26%)
680 (26%)
Firt-time tester
0.2
no
40 (51%)
25 (52%)
1,472 (59%)
1,537 (59%)
yes
38 (49%)
23 (48%)
1,017 (41%)
1,078 (41%)
1 n (%)
2 Pearson’s Chi-squared test
Table S4b. Eligibility and participation in phase 2 survey by sociodemographic characteristics, distribution channel, and HIV testing history (multivariable multinomial regression model).
# weights: 48 (30 variable)
initial value 2872.871135
iter 10 value 667.083122
iter 20 value 581.210504
iter 30 value 569.730948
iter 40 value 569.541017
final value 569.540841
converged
Code
# Model results displaytbl_reg <- reg |>tbl_regression(exponentiate =TRUE)# Displaying the coefficients table in long formatmultinom_pivot_wider <-function(x) {# check inputs match expectatationsif (!inherits(x, "tbl_regression") ||!inherits(x$model_obj, "multinom")) {stop("`x=` must be class 'tbl_regression' summary of a `nnet::multinom()` model.") }# create tibble of results df <- tibble::tibble(outcome_level =unique(x$table_body$groupname_col)) df$tbl_reg <- purrr::map( df$outcome_level,function(lvl) { gtsummary::modify_table_body( x,~ dplyr::filter(.x, .data$groupname_col %in% lvl) %>% dplyr::ungroup() %>% dplyr::select(-.data$groupname_col) ) } )tbl_merge(df$tbl_reg, tab_spanner =paste0("**", df$outcome_level, "**"))}# Computing Table S4.btbl_reg |>multinom_pivot_wider() |>bold_labels()
Characteristic
eligible for phase 2 but did not complete the questionnaire
Table S5. Time between HIVST and confirmatory testing among phase 2 participants who did link to confirmatory testing, by reported number of lines and self-interpreted HIVST result
Code
data <- data |>mutate(time_to_confirmation = time_to_confirmation |>fct_relevel("last than a week after the HIVST", "between 1 and 2 weeks","between 3 and 4 weeks", "between 1 and 2 months after", "3 months later or more" ) )data <- data |>set_variable_labels(time_to_confirmation ="Time between HIVST and confirmatory testing `\n` among phase 2 participants who did link to confirmatory testing" )data |>filter( recontact_phase2 =="yes", HIVST_reported_result =="reactive"| HIVST_reported_lines =="2 lines", confirmatory_test_result =="positive", confirmatory_test =="yes", status_phase2 =="questionnaires completed", consulted_health_prof =="yes", ) |>mutate(test_result_number_lines_reported =droplevels(test_result_number_lines_reported) ) |>tbl_summary(include =c(time_to_confirmation),by = test_result_number_lines_reported ) |>add_overall() |>bold_labels()
Characteristic
Overall, N = 181
2 lines / reactive, N = 121
2 lines / non-reactive, N = 31
2 lines / DK-R, N = 31
Time between HIVST and confirmatory testing ` ` among phase 2 participants who did link to confirmatory testing
last than a week after the HIVST
13 (72%)
10 (83%)
0 (0%)
3 (100%)
between 1 and 2 weeks
2 (11%)
0 (0%)
2 (67%)
0 (0%)
between 3 and 4 weeks
1 (5.6%)
1 (8.3%)
0 (0%)
0 (0%)
between 1 and 2 months after
1 (5.6%)
1 (8.3%)
0 (0%)
0 (0%)
3 months later or more
1 (5.6%)
0 (0%)
1 (33%)
0 (0%)
1 n (%)
Table S6. Main reason for not linking to confirmatory testing among phase 2 participants who did not link to confirmatory testing, by reported number of lines and self-interpreted HIVST result
Code
data <- data |>set_variable_labels(reason_non_confirmatory_test ="Main reason for not `\n` linking to confirmatory testing among phase 2 `\n` participants who did not link to confirmatory testing" )data |>filter( status_phase2 =="questionnaires completed", HIVST_reported_result =="reactive"| HIVST_reported_lines =="2 lines", recontact_phase2 =="yes", confirmatory_test =="no" ) |>mutate(test_result_number_lines_reported =droplevels(test_result_number_lines_reported) ) |>tbl_summary(include =c(reason_non_confirmatory_test),by = test_result_number_lines_reported,sort = reason_non_confirmatory_test ~"frequency" ) |>bold_labels()
Characteristic
2 lines / reactive, N = 121
1 line / reactive, N = 61
2 lines / non-reactive, N = 161
2 lines / DK-R, N = 101
Main reason for not ` ` linking to confirmatory testing among phase 2 ` ` participants who did not link to confirmatory testing
my test was non-reactive
7 (58%)
3 (50%)
5 (31%)
5 (50%)
Didn't know he should get a confirmatory test
2 (17%)
2 (33%)
5 (31%)
1 (10%)
Didn't have time
3 (25%)
0 (0%)
3 (19%)
2 (20%)
Fear that the result will be known by others
0 (0%)
0 (0%)
1 (6.3%)
1 (10%)
No reason
0 (0%)
1 (17%)
1 (6.3%)
0 (0%)
Did not know where to take the test
0 (0%)
0 (0%)
1 (6.3%)
0 (0%)
The testing site was too for away
0 (0%)
0 (0%)
0 (0%)
1 (10%)
1 n (%)
Table S7. Place of confirmatory testing among phase 2 participants who did link to confirmatory testing, by reported number of lines and self-interpreted HIVST result
Code
data <- data |>set_variable_labels(place_confirmatory_test ="Place of confirmatory testing among phase 2 participants who did link to confirmatory testing" )data |>filter( recontact_phase2 =="yes", HIVST_reported_result =="reactive"| HIVST_reported_lines =="2 lines", status_phase2 =="questionnaires completed", confirmatory_test =="yes" ) |>mutate(test_result_number_lines_reported =droplevels(test_result_number_lines_reported) ) |>tbl_summary(include =c(place_confirmatory_test),by = test_result_number_lines_reported ) |>add_overall() |>bold_labels()
Characteristic
Overall, N = 341
2 lines / reactive, N = 151
1 line / reactive, N = 11
2 lines / non-reactive, N = 91
2 lines / DK-R, N = 81
DK-R / reactive, N = 11
Place of confirmatory testing among phase 2 participants who did link to confirmatory testing
Community Clinic/Dedicated Health Center
12 (35%)
3 (20%)
0 (0%)
6 (67%)
3 (38%)
0 (0%)
Health Center/ Hospital /Clinic/ Maternity
22 (65%)
12 (80%)
1 (100%)
3 (33%)
5 (63%)
1 (100%)
1 n (%)
Table S8. Time between phase 1 and phase 2 interviews among phase 2 participants who did link to confirmatory testing, by reported number of lines and self-interpreted HIVST result
Code
# Computing time between phase 1 and phase 2data$date_phase1 <-ymd_hms(data$date_phase1)data$date_phase2 <-ymd_hms(data$date_phase2)data <- data |>mutate(time_between_phase1_2 =time_length(date_phase2 - date_phase1, "month") )data$time_between_phase1_2 <-cut(data$time_between_phase1_2,include.lowest =TRUE,right =TRUE,dig.lab =4,breaks =c(3, 4, 6, 9),labels =c("less than 4 months", "Between 4 and 6 months", "More than 6 months"))# labeling the "place_confirmatory_test" variabledata <- data |>set_variable_labels(time_between_phase1_2 ="Time between phase 1 and phase 2 interviews among phase 2 participants who did link to confirmatory testing" )# Generating the S7 tabledata |>filter( recontact_phase2 =="yes", HIVST_reported_result =="reactive"| HIVST_reported_lines =="2 lines", status_phase2 =="questionnaires completed" ) |>mutate(test_result_number_lines_reported =droplevels(test_result_number_lines_reported) ) |>tbl_summary(include =c(time_between_phase1_2),by = test_result_number_lines_reported ) |>add_overall() |>bold_labels()
Characteristic
Overall, N = 781
2 lines / reactive, N = 271
1 line / reactive, N = 71
2 lines / non-reactive, N = 251
2 lines / DK-R, N = 181
DK-R / reactive, N = 11
Time between phase 1 and phase 2 interviews among phase 2 participants who did link to confirmatory testing
less than 4 months
8 (10%)
3 (11%)
0 (0%)
4 (16%)
1 (5.6%)
0 (0%)
Between 4 and 6 months
67 (86%)
24 (89%)
5 (71%)
21 (84%)
17 (94%)
0 (0%)
More than 6 months
3 (3.8%)
0 (0%)
2 (29%)
0 (0%)
0 (0%)
1 (100%)
1 n (%)
Source Code
---title: "HIV self-testing positivity rate and linkage to confirmatory testing and care: a telephone survey in Côte d’Ivoire, Mali and Senegal"author: "Arsène Kouassi Kra et al"date: todaytoc: trueformat: html: code-tools: true code-fold: true self-contained: true toc: false dpi: 300 fig-responsive: trueexecute: warning: false---```{r setup, include=FALSE}knitr::opts_chunk$set(echo = TRUE)``````{r message=FALSE}# Loading packageslibrary(tidyverse)library(gtsummary)library(labelled)library(scales)library(ggrepel)library(RColorBrewer)library(utils)library(patchwork)library(stats)# Data importdata <- readr::read_csv("data.csv", show_col_types = FALSE)```## Table 1. Self-reported test result and the reported number of lines among participants of phase 1, and positivity rates according to different hypotheses```{r fig.height=6, fig.width=12, message=FALSE, warning=FALSE}# Creating "test_result_number_lines_reported" variabledata <- data |> mutate( test_result_number_lines_reported = case_when( (HIVST_reported_lines == "2 lines" & HIVST_reported_result == "reactive") ~ "2 lines / reactive", (HIVST_reported_lines == "2 lines" & HIVST_reported_result == "non reactive") ~ "2 lines / non-reactive", (HIVST_reported_lines == "2 lines" & (HIVST_reported_result == "DK" | HIVST_reported_result == "R")) ~ "2 lines / DK-R", (HIVST_reported_lines == "1 line" & HIVST_reported_result == "reactive") ~ "1 line / reactive", (HIVST_reported_lines == "1 line" & HIVST_reported_result == "non reactive") ~ "1 line / non-reactive", (HIVST_reported_lines == "1 line" & (HIVST_reported_result == "DK" | HIVST_reported_result == "R")) ~ "1 line / DK-R", (HIVST_reported_lines == "0 line" & HIVST_reported_result == "non reactive") ~ "0 line / non-reactive", (HIVST_reported_lines == "0 line" & HIVST_reported_result == "reactive") ~ "0 line / reactive", (HIVST_reported_lines == "0 line" & (HIVST_reported_result == "DK" | HIVST_reported_result == "R")) ~ "0 line / DK-R", ((HIVST_reported_lines == "0 line" | HIVST_reported_lines == "1 line") & HIVST_reported_result == "invalid") ~ "0-1 line / invalid", (HIVST_reported_lines == "2 lines" & HIVST_reported_result == "invalid") ~ "2 lines / invalid", ((HIVST_reported_lines == "DK" | HIVST_reported_lines == "R") & HIVST_reported_result == "invalid") ~ "DK-R / invalid", ((HIVST_reported_lines == "DK" | HIVST_reported_lines == "R") & HIVST_reported_result == "reactive") ~ "DK-R / reactive", ((HIVST_reported_lines == "DK" | HIVST_reported_lines == "R") & HIVST_reported_result == "non reactive") ~ "DK-R / non-reactive", ((HIVST_reported_lines == "DK" | HIVST_reported_lines == "R") & (HIVST_reported_result == "DK" | HIVST_reported_result == "R")) ~ "DK-R / DK-R" ) ) |> set_variable_labels( test_result_number_lines_reported = "Reported number of lines / self-interpreted HIVST result" )data$test_result_number_lines_reported <- data$test_result_number_lines_reported |> fct_relevel( "2 lines / reactive", "1 line / non-reactive", "0-1 line / invalid", "1 line / reactive", "2 lines / non-reactive", "0 line / non-reactive", "0 line / DK-R", "1 line / DK-R", "2 lines / DK-R", "DK-R / reactive", "DK-R / DK-R", "DK-R / non-reactive" )# Generating Table 1tbl1 <- data |> tbl_summary( include = test_result_number_lines_reported, label = test_result_number_lines_reported ~ "" ) |> modify_header(label = "**Phase 1 participants**") |> as_gt() |> gt::tab_row_group( label = gt::md("*Partial reponse (P)*"), rows = 8:13 ) |> gt::tab_row_group( label = gt::md("*Inconsistant reponse (I)*"), rows = 5:7 ) |> gt::tab_row_group( label = gt::md("*Consistant reponse (C)*"), rows = 2:4 )tbl1```## Figure 3. Positivity rates based on self-interpreted HIVST results or the reported number of visible lines, by distribution channel, gender and country, among participants of the first survey phase in Côte d'Ivoire, Mali, and Senegal (2021).```{r, message=FALSE, warning=FALSE}#| fig-width: 12#| fig-height: 6#| column: page# Creating "key population profiles" variabledata <- data |> mutate( key_population_profile = interaction(sex, delivery_channel_grouped) |> fct_recode() |> fct_relevel( "man.MSM-based channels", "woman.MSM-based channels", "man.FSW-based channels", "woman.FSW-based channels", "man.Other delivery channels", "woman.Other delivery channels" ) ) |> set_variable_labels(key_population_profile = "Key population profile")# Recoding variable "key population profiles"data <- data |> mutate( key_population_profile = key_population_profile |> fct_recode( "man\nMSM-\nbased\nchannels" = "man.MSM-based channels", "woman\nMSM-\nbased\nchannels" = "woman.MSM-based channels", "man\nFSW-\n based\nchannels" = "man.FSW-based channels", "woman\nFSW-\nbased\nchannels" = "woman.FSW-based channels", "man\nother\nchannels" = "man.Other delivery channels", "woman\nother\nchannels" = "woman.Other delivery channels" ) ) |> set_variable_labels(key_population_profile = "key population profiles")dh1 <- data |> group_by(key_population_profile, country) |> summarise( complete = "Based on self-interpreted test results", n = sum(HIVST_reported_result %in% c("reactive")), N = sum(!HIVST_reported_result %in% c("DK", "R")), Lowest = "Based on self-interpreted test results", n1 = sum(HIVST_reported_result %in% c("reactive")), N1 = n(), Highest = "Based on self-interpreted test results", n2 = sum(HIVST_reported_result %in% c("reactive", "DK", "R")), N2 = n() )dh2 <- data |> group_by(key_population_profile, country) |> summarise( complete = "Based on the reported number of lines", n = sum(HIVST_reported_lines %in% c("2 lines")), N = sum(!HIVST_reported_result %in% c("DK", "R")), Lowest = "Based on the reported number of lines", n1 = sum(HIVST_reported_lines %in% c("2 lines")), N1 = n(), Highest = "Based on the reported number of lines", n2 = sum(HIVST_reported_lines %in% c("2 lines", "DK", "R")), N2 = n() )dh0 <- bind_rows(dh1, dh2) |> mutate( complete = factor(complete, levels = c("Based on self-interpreted test results", "Based on the reported number of lines")), positivity_rate = n / N, label = scales::percent(positivity_rate, suffix = "", accuracy = .1), lowest = factor(Lowest, levels = c("reported result", "reported lines")), lowest_possible_rate = n1 / N1, label1 = scales::percent(lowest_possible_rate, suffix = "", accuracy = .1), Highest = factor(Highest, levels = c("reported result", "reported lines")), highest_possible_rate = n2 / N2, label2 = scales::percent(highest_possible_rate, suffix = "", accuracy = .1) ) |> mutate( ylabel = highest_possible_rate + .01 )# Dropping cells with denominator below 25to_drop <- dh0$N < 25to_drop <- dh0$N1 < 25to_drop <- dh0$N2 < 25dh0$positivity_rate[to_drop] <- NAdh0$lowest_possible_rate[to_drop] <- NAdh0$highest_possible_rate[to_drop] <- NAdh0$label[to_drop] <- ""dh0$label[to_drop] <- "*"dh0$ylabel[to_drop] <- 0fig3 <- dh0 |> ggplot() + aes( x = key_population_profile, y = positivity_rate, ymin = lowest_possible_rate, ymax = highest_possible_rate, color = complete, shape = complete, ) + geom_errorbar( position = position_dodge(width = 0.9), color = "#555555", width = .2 ) + geom_point( stat = "identity", position = position_dodge(width = 0.9), size = 2.5 ) + geom_text( mapping = aes(label = label, y = NULL), y = -0.01, position = position_dodge(width = 0.9), color = "black", size = 3, vjust = 1, face = "bold" ) + facet_grid(cols = vars(country)) + labs(title = "", x = "", y = "") + ggtitle("") + scale_y_continuous(labels = scales::percent, limits = c(-0.01, .25)) + scale_colour_manual(values = c("#DD3D2D", "#1B7837")) + theme_classic() + theme( panel.grid.major.y = element_line(colour = "#DDDDDD", linetype = "dotted"), legend.title = element_blank(), legend.position = "bottom" )fig3```## Figure 4. Elements for the flow chart of the participant selection process for Phase 2 of the survey```{r fig.height=6, fig.width=12, message=FALSE, warning=FALSE}n_eligible_for_phase_2 <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines")) |> nrow()n_eligible_agreed_recontacted <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "yes")) |> nrow()n_eligible_refusal_recontacted <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "no")) |> nrow()n_unreachable_time_phase2 <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "yes") & (status_phase2 == "unavaible" | is.na(status_phase2))) |> nrow()n_successfully_recontacted <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "yes") & (status_phase2 != "unavaible" & !is.na(status_phase2))) |> nrow()n_refused_partcipate <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "yes") & (status_phase2 != "unavaible" & !is.na(status_phase2)) & status_phase2 == "refusal") |> nrow()n_accepted_partcipate <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "yes") & (status_phase2 != "unavaible" & !is.na(status_phase2)) & status_phase2 != "refusal") |> nrow()n_disconneted <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "yes") & (status_phase2 != "unavaible" & !is.na(status_phase2)) & (status_phase2 != "refusal" & status_phase2 == "disconnected")) |> nrow()n_dropped <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "yes") & (status_phase2 != "unavaible" & !is.na(status_phase2)) & status_phase2 != "refusal" & status_phase2 == "dropped out before the end") |> nrow()n_completed_quest_phase2 <- data |> filter((HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines") & (recontact_phase2 == "yes") & status_phase2 == "questionnaires completed") |> nrow()```- `r n_eligible_for_phase_2` participants who reported a reactive test and/or two lines in phase 1- `r n_eligible_agreed_recontacted` participants who agreed to be recalled several months later- `r n_eligible_refusal_recontacted` participants refusal to be recalled several months later- `r n_unreachable_time_phase2` unreachable at the time of phase 2- `r n_successfully_recontacted` successfully recontacted for phase 2- `r n_refused_partcipate` refused to participate- `r n_accepted_partcipate` accepted to participate in phase 2 survey- `r n_disconneted` disconnected and unreachable- `r n_dropped` dropped out before the end- `r n_completed_quest_phase2` completed questionnaire phase 2## Table 2. Linkage to confirmatory testing, proportion being confirmed HIV positive and treatment initiation, by reported number of lines and self-interpreted HIVST result among phase 2 eligible participants who completed their questionnaire.```{r, message=FALSE, warning=FALSE}data <- data |> set_variable_labels( country = "Country", age_group = "Age group", marital_status = "Marital status", educational_level = "Educational level", first_time_tester = "First time tester" )# Selection of individuals eligible for phase 2 who completed their phase 2 questionnairephase2 <- data |> filter( HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines", recontact_phase2 == "yes", status_phase2 == "questionnaires completed" ) |> mutate(all = "ALL")phase2$test_result_number_lines_reported <- droplevels(phase2$test_result_number_lines_reported)var_label(phase2$test_result_number_lines_reported) <- "Reported number of lines /self-interpreted result"var_label(phase2$all) <- "Overall"# Computing the numbers of individuals who completed phase 2 questionnairetbl_completed <- phase2 |> tbl_summary( include = c(all, test_result_number_lines_reported), statistic = ~"{n}" ) |> modify_header(stat_0 = "**n**") |> modify_footnote(update = everything() ~ NA)# Computing the proportion who linked to confirmatory testingtbl_linked <- phase2 |> tbl_summary( by = confirmatory_test, include = c(all, test_result_number_lines_reported), percent = "row", digits = ~0 ) |> add_ci(style_fun = ~ label_percent(accuracy = 1, suffix = "")) |> modify_column_hide(c(stat_1, ci_stat_1)) |> modify_header(stat_2 ~ "**n (%)**") |> modify_footnote(update = everything() ~ NA)# Computing the proportion who were confirmed HIV-positivetbl_confirmed <- phase2 |> filter(confirmatory_test == "yes") |> mutate(confirmed = confirmatory_test_result == "positive") |> tbl_summary( by = confirmed, include = c(all, test_result_number_lines_reported), percent = "row", digits = ~0 ) |> add_ci(style_fun = ~ label_percent(accuracy = 1, suffix = "")) |> modify_column_hide(c(stat_1, ci_stat_1)) |> modify_header(stat_2 ~ "**n (%)**") |> modify_footnote(update = everything() ~ NA)# Computing the proportion who initiated ARTtbl_initiated <- phase2 |> filter(confirmatory_test_result == "positive") |> mutate(initiated = consulted_health_prof == "yes") |> mutate(test_result_number_lines_reported = fct_drop(test_result_number_lines_reported)) |> tbl_summary( by = initiated, include = c(all, test_result_number_lines_reported), percent = "row", digits = ~0 ) |> add_ci(style_fun = ~ label_percent(accuracy = 1, suffix = "")) |> modify_column_hide(c(stat_1, ci_stat_1)) |> modify_header(stat_2 ~ "**n (%)**") |> modify_footnote(update = everything() ~ NA)# Merging tablestbl_merge( list(tbl_completed, tbl_linked, tbl_confirmed, tbl_initiated), tab_spanner = c( "**Completed phase 2**", "**Linked to confirmatory testing**", "**Confirmed HIV positive**", "**Initiated ART**" )) %>% bold_labels()```## Table S1a. Factors associated (logistic regression) with positivity rate based on the reported number of visible lines among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021)```{r, message=FALSE, warning=FALSE}data$HIVST_reported_lines_status <- data$HIVST_reported_lines %>% fct_recode( NULL = "0 line", "non reactive" = "1 line", "reactive" = "2 lines", NULL = "DK", NULL = "R" )modele_reported_lines <- glm( HIVST_reported_lines_status ~ key_population_profile + country + age_group + marital_status + educational_level + first_time_tester, data = data, family = binomial)modele_reported_lines %>% tbl_regression(intercept = TRUE, exponentiate = TRUE) %>% add_global_p() %>% add_q(method = "bonferroni") %>% bold_labels()```## Table S1b. Factors associated (logistic regression) with positivity rate based on self-reported HIVST, among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021).```{r, message=FALSE, warning=FALSE}data$HIVST_reported_result_status <- data$HIVST_reported_result %>% fct_recode( NULL = "DK", NULL = "invalid", NULL = "R" )modele_reported_result <- glm( HIVST_reported_result_status ~ key_population_profile + country + age_group + marital_status + educational_level + first_time_tester, data = data, family = binomial)modele_reported_result %>% tbl_regression(intercept = TRUE, exponentiate = TRUE) %>% add_global_p() %>% add_q(method = "bonferroni") %>% bold_labels()```## Table S2a. Positivity rates based on self-interpreted HIVST result, by key population profiles and country, among phase 1 participants```{r, message=FALSE, warning=FALSE}# Creation of a table of the lowest possible positivity ratestblS2a_low <- data |> to_factor() |> tbl_custom_summary( include = c(country), by = key_population_profile, stat_fns = ~ proportion_summary(variable = "HIVST_reported_result", value = c("reactive")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Creation of a table of positivity rates based on complete responsestblS2a_central <- data |> filter(HIVST_reported_result != "DK", HIVST_reported_result != "R") |> to_factor() |> tbl_custom_summary( include = c(country), by = key_population_profile, stat_fns = ~ proportion_summary(variable = "HIVST_reported_result", value = c("reactive")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Creation of a table of the highest possible positivity ratestblS2a_high <- data |> to_factor() |> tbl_custom_summary( include = c(country), by = key_population_profile, stat_fns = ~ proportion_summary(variable = "HIVST_reported_result", value = c("reactive", "DK", "R")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Merging the different tablestblS2a <- tbl_stack( list(tblS2a_low, tblS2a_central, tblS2a_high), group_header = c( "Lowest possible rate: positivity rate based on self reported test result and DK-R are considered not reactive", "Complete responses: positivity rate based on self reported test result and DK-R are excluded", "Highest possible rate: positivity rate based on self reported test result and DK-R are considered reactive" ))tblS2a |> modify_header(label ~ "**Variable**") |> modify_spanning_header( c("stat_1", "stat_2") ~ "**MSM-based channels**", c("stat_3", "stat_4") ~ "**FSW-based channels**", c("stat_5", "stat_6") ~ "**Others delivery channels**", stat_0 ~ "**Total**" ) |> modify_header( stat_1 ~ "**Man**", stat_2 ~ "**Woman**", stat_3 ~ "**Man**", stat_4 ~ "**Woman**", stat_5 ~ "**Man**", stat_6 ~ "**Woman**", stat_0 ~ " " ) |> modify_footnote(update = everything() ~ NA)```## Table S2b. Positivity rates based on the reported number of lines, by key population profiles and country, among phase 1 participants```{r, message=FALSE, warning=FALSE}# Creation of a table of the lowest possible positivity ratestblS2b_low <- data |> to_factor() |> tbl_custom_summary( include = c(country), by = key_population_profile, stat_fns = ~ proportion_summary(variable = "HIVST_reported_lines", value = c("2 lines")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Creation of a table of positivity rates based on complete responsestblS2b_central <- data |> filter(HIVST_reported_lines != "DK", HIVST_reported_lines != "R") |> to_factor() |> tbl_custom_summary( include = c(country), by = key_population_profile, stat_fns = ~ proportion_summary(variable = "HIVST_reported_lines", value = c("2 lines")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Creation of a table of the highest possible positivity ratestblS2b_high <- data |> to_factor() |> tbl_custom_summary( include = c(country), by = key_population_profile, stat_fns = ~ proportion_summary(variable = "HIVST_reported_lines", value = c("2 lines", "DK", "R")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Merging of the different tablestblS2b <- tbl_stack( list(tblS2b_low, tblS2b_central, tblS2b_high), group_header = c( "Lowest possible rate: positivity rate based on self reported number of lines and DK-R are considered not 2 lines", "Complete responses: positivity rate based on self reported number of lines and DK-R are excluded", "Highest possible rate: positivity rate based on self reported number of lines and DK-R are considered 2 lines" ))tblS2b |> modify_header(label ~ "**Variable**") |> modify_spanning_header( c("stat_1", "stat_2") ~ "**MSM-based channels**", c("stat_3", "stat_4") ~ "**FSW-based channels**", c("stat_5", "stat_6") ~ "**Others delivery channels**", stat_0 ~ "**Total**" ) |> modify_header( stat_1 ~ "**Man**", stat_2 ~ "**Woman**", stat_3 ~ "**Man**", stat_4 ~ "**Woman**", stat_5 ~ "**Man**", stat_6 ~ "**Woman**", stat_0 ~ " " ) |> modify_footnote(update = everything() ~ NA)```## Table S3a. Positivity rates based on self-interpreted HIVST result, by age group and country, among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021)```{r, message=FALSE, warning=FALSE}# Creation of a table of the lowest possible positivity ratestblS3a_low <- data |> to_factor() |> tbl_custom_summary( include = c(country), by = age_group, stat_fns = ~ proportion_summary(variable = "HIVST_reported_result", value = c("reactive")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Creation of a table of positivity rates based on complete responsestblS3a_central <- data |> filter(HIVST_reported_result != "DK", HIVST_reported_result != "R") |> to_factor() |> tbl_custom_summary( include = c(country), by = age_group, stat_fns = ~ proportion_summary(variable = "HIVST_reported_result", value = c("reactive")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Creation of a table of the highest possible positivity ratestblS3a_high <- data |> to_factor() |> tbl_custom_summary( include = c(country), by = age_group, stat_fns = ~ proportion_summary(variable = "HIVST_reported_result", value = c("reactive", "DK", "R")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Merging of the different tablestblS3a <- tbl_stack( list(tblS3a_low, tblS3a_central, tblS3a_high), group_header = c( "Lowest possible rate: positivity rate based on self reported test result and DK-R are considered not reactive", "Complete responses: positivity rate based on self reported test result and DK-R are excluded", "Highest possible rate: positivity rate based on self reported test result and DK-R are considered reactive" ))tblS3a |> modify_header(label ~ "")```## Table S3b. Positivity rates based on the reported number of visible lines, by age group and country, among participants of the first survey phase in Côte d’Ivoire, Mali, and Senegal (2021)```{r, message=FALSE, warning=FALSE}# Creation of a table of the lowest possible positivity ratestblS3b_low <- data |> to_factor() |> tbl_custom_summary( include = c(country), by = age_group, stat_fns = ~ proportion_summary(variable = "HIVST_reported_lines", value = c("2 lines")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Creation of a table of positivity rates based on complete responsestblS3b_central <- data |> filter(HIVST_reported_lines != "DK", HIVST_reported_lines != "R") |> to_factor() |> tbl_custom_summary( include = c(country), by = age_group, stat_fns = ~ proportion_summary(variable = "HIVST_reported_lines", value = c("2 lines")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Creation of a table of the highest possible positivity ratestblS3b_high <- data |> to_factor() |> tbl_custom_summary( include = c(country), by = age_group, stat_fns = ~ proportion_summary(variable = "HIVST_reported_lines", value = c("2 lines", "DK", "R")), statistic = ~"{prop}% ({n}/{N})", digits = ~ list( function(x) { style_percent(x, digits = 0) }, 0, 0 ), overall_row = TRUE ) |> add_overall(last = FALSE)# Merging of the different tablestblS3b <- tbl_stack( list(tblS3b_low, tblS3b_central, tblS3b_high), group_header = c( "Lowest possible rate: positivity rate based on self reported number of lines and DK-R are considered not 2 lines", "Complete responses: positivity rate based on self reported number of lines and DK-R are excluded", "Highest possible rate: positivity rate based on self reported number of lines and DK-R are considered 2 lines" ))tblS3b |> modify_header(label ~ "")```## Table S4a. Eligibility and participation in phase 2 survey by sociodemographic characteristics, distribution channel, and HIV testing history (bivariable comparison ).```{r, message=FALSE, warning=FALSE}# Reordoring value labelsdata <- data |> mutate( marital_status = marital_status |> fct_relevel( "single", "divorced / separated / widowed", "living with partner / married" ), educational_level = educational_level |> fct_relevel( "none / primary", "secondary", "higher" ) )# Improving variable labelsdata <- data |> set_variable_labels( country = "Country", key_population_profile = "Sex and distribution channel", age_group = "Age group", marital_status = "Marital status", educational_level = "Educational level", first_time_tester = "Firt-time tester" )# Computing Table S4.adata |> tbl_summary( include = c( country, key_population_profile, age_group, marital_status, educational_level, first_time_tester ), by = final_status_phas, type = list(c(first_time_tester) ~ "categorical") ) |> add_overall(TRUE) |> add_p( test = list( key_population_profile ~ "chisq.test", country ~ "chisq.test", marital_status ~ "chisq.test" ) ) |> bold_labels()```## Table S4b. Eligibility and participation in phase 2 survey by sociodemographic characteristics, distribution channel, and HIV testing history (multivariable multinomial regression model).### Details of the multinomial model```{r, message=FALSE, warning=FALSE}# Calculating the multinomial modelreg <- nnet::multinom( final_status_phas ~ country + key_population_profile + age_group + marital_status + educational_level + first_time_tester, data = data)# Model results displaytbl_reg <- reg |> tbl_regression(exponentiate = TRUE)# Displaying the coefficients table in long formatmultinom_pivot_wider <- function(x) { # check inputs match expectatations if (!inherits(x, "tbl_regression") || !inherits(x$model_obj, "multinom")) { stop("`x=` must be class 'tbl_regression' summary of a `nnet::multinom()` model.") } # create tibble of results df <- tibble::tibble(outcome_level = unique(x$table_body$groupname_col)) df$tbl_reg <- purrr::map( df$outcome_level, function(lvl) { gtsummary::modify_table_body( x, ~ dplyr::filter(.x, .data$groupname_col %in% lvl) %>% dplyr::ungroup() %>% dplyr::select(-.data$groupname_col) ) } ) tbl_merge(df$tbl_reg, tab_spanner = paste0("**", df$outcome_level, "**"))}# Computing Table S4.btbl_reg |> multinom_pivot_wider() |> bold_labels()```### Global p-values used in Table S4```{r}reg |> car::Anova()```## Table S5. Time between HIVST and confirmatory testing among phase 2 participants who did link to confirmatory testing, by reported number of lines and self-interpreted HIVST result```{r, message=FALSE, warning=FALSE}data <- data |> mutate( time_to_confirmation = time_to_confirmation |> fct_relevel( "last than a week after the HIVST", "between 1 and 2 weeks", "between 3 and 4 weeks", "between 1 and 2 months after", "3 months later or more" ) )data <- data |> set_variable_labels( time_to_confirmation = "Time between HIVST and confirmatory testing `\n` among phase 2 participants who did link to confirmatory testing" )data |> filter( recontact_phase2 == "yes", HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines", confirmatory_test_result == "positive", confirmatory_test == "yes", status_phase2 == "questionnaires completed", consulted_health_prof == "yes", ) |> mutate( test_result_number_lines_reported = droplevels(test_result_number_lines_reported) ) |> tbl_summary( include = c(time_to_confirmation), by = test_result_number_lines_reported ) |> add_overall() |> bold_labels()```## Table S6. Main reason for not linking to confirmatory testing among phase 2 participants who did not link to confirmatory testing, by reported number of lines and self-interpreted HIVST result```{r, message=FALSE, warning=FALSE}data <- data |> set_variable_labels( reason_non_confirmatory_test = "Main reason for not `\n` linking to confirmatory testing among phase 2 `\n` participants who did not link to confirmatory testing" )data |> filter( status_phase2 == "questionnaires completed", HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines", recontact_phase2 == "yes", confirmatory_test == "no" ) |> mutate( test_result_number_lines_reported = droplevels(test_result_number_lines_reported) ) |> tbl_summary( include = c(reason_non_confirmatory_test), by = test_result_number_lines_reported, sort = reason_non_confirmatory_test ~ "frequency" ) |> bold_labels()```## Table S7. Place of confirmatory testing among phase 2 participants who did link to confirmatory testing, by reported number of lines and self-interpreted HIVST result```{r, message=FALSE, warning=FALSE}data <- data |> set_variable_labels( place_confirmatory_test = "Place of confirmatory testing among phase 2 participants who did link to confirmatory testing" )data |> filter( recontact_phase2 == "yes", HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines", status_phase2 == "questionnaires completed", confirmatory_test == "yes" ) |> mutate( test_result_number_lines_reported = droplevels(test_result_number_lines_reported) ) |> tbl_summary( include = c(place_confirmatory_test), by = test_result_number_lines_reported ) |> add_overall() |> bold_labels()```## Table S8. Time between phase 1 and phase 2 interviews among phase 2 participants who did link to confirmatory testing, by reported number of lines and self-interpreted HIVST result```{r, message=FALSE, warning=FALSE}# Computing time between phase 1 and phase 2data$date_phase1 <- ymd_hms(data$date_phase1)data$date_phase2 <- ymd_hms(data$date_phase2)data <- data |> mutate( time_between_phase1_2 = time_length(date_phase2 - date_phase1, "month") )data$time_between_phase1_2 <- cut(data$time_between_phase1_2, include.lowest = TRUE, right = TRUE, dig.lab = 4, breaks = c(3, 4, 6, 9), labels = c("less than 4 months", "Between 4 and 6 months", "More than 6 months"))# labeling the "place_confirmatory_test" variabledata <- data |> set_variable_labels( time_between_phase1_2 = "Time between phase 1 and phase 2 interviews among phase 2 participants who did link to confirmatory testing" )# Generating the S7 tabledata |> filter( recontact_phase2 == "yes", HIVST_reported_result == "reactive" | HIVST_reported_lines == "2 lines", status_phase2 == "questionnaires completed" ) |> mutate( test_result_number_lines_reported = droplevels(test_result_number_lines_reported) ) |> tbl_summary( include = c(time_between_phase1_2), by = test_result_number_lines_reported ) |> add_overall() |> bold_labels()```