Screening, in duplicate, of a first batch yielded 14 IPDMAs that included at least 1 CRT in their meta-analysis. Total of 43 CRTs across the 14 IPDMAs.
# Importdf <-read_excel(here("IPDMA_CRT_extraction_10112025.xlsx"))df$`State Of Play Covidence #`<-as.factor(df$`State Of Play Covidence #`)# Recode the comparator group description levelsdf <- df %>%mutate(`Comparator category`=case_when(str_detect(`Describe Comparator`, regex("placebo", ignore_case =TRUE)) ~"Placebo or usual care",`Describe Comparator`%in%c("iron and folic acid supplements only", "Saline") ~"Active Comparator",TRUE~"Usual care" ) )# Recode the intervention group description levelsdf <- df %>%mutate(`Intervention category`=case_when(`Intervention category`=="Behavioural"~"Behavioural (lifestyle, psychological, exercise)",`Intervention category`=="Rehabilitation"~"Behavioural (lifestyle, psychological, exercise)",TRUE~`Intervention category` ) )# Rename the medical field leveldf <- df %>%mutate(`Medical field`=case_when(`Medical field`=="Cardiovascular/Cardiac"~"Cardiovascular",TRUE~`Medical field` ) )# Rename variablesdf <- df %>%rename(`ICC reported`=`ICC or any other measure of between-cluster variability for at least one of the pooled outcomes (incuding CRTs) reported`,`CRTs labeled as CRTs`=`CRTs flagged as CRT in ‘Characteristics of included studies’ table`,`Analysis framework`=`Analysis framework for the main IPDMA analysis`,`Analysis approach`=`Main IPDMA analysis approach`,`Treatment effect model`=`Main IPDMA analysis model regarding treatment effect`,`Accounted for two-level clustering`=`Do the authors account for the two-level clustering, i.e., CRT-level (clusters within CRT(s)) and IPDMA-level (pooling of trials)?`,`CRT-level estimator`=`If two-stage, what estimator do the authors use to account for clustering at the CRT-level`,`Small sample correction`=`Do the authors report any correction in case of low cluster number in any of the involved CRTs (\"small sample correction\")?`,`CRT vs IRT analysis`=`Any subgroup analysis/meta-regression performed based on study design (CRTs vs individual RCTs)?`,`Covariate adjustment`=`Any covariate adjustment applied for the main analysis?`,`Risk of bias`=`Any risk of bias assessment done?`,`CRT-specific risk of bias`=`If RoB done, any CRT-specific risk of biases assessed?`, )# Recode the CRT-specific risk of bias variable by explicitly showing Unknowndf <- df %>%mutate(`CRT-specific risk of bias`=case_when(is.na(`CRT-specific risk of bias`) ~"Not applicable",TRUE~`CRT-specific risk of bias`))# Create long dataset to work with CRT/IPDMA formatdf_long <- df %>%pivot_longer(cols =matches("^CRT[0-9]+:"),names_to =c("CRT", ".value"),names_pattern ="(CRT[0-9]+): (.*)" )df_long <- df_long %>%filter(!is.na(`name/ID`))# Rename the consent variabledf_long <- df_long %>%mutate(`Consent procedure`=case_when(`Consent procedure`=="Passive consent (Participation occurs unless they actively refuse “opt-out”)"~"Passive consent",`Consent procedure`=="Waiver of consent (No consent sought/communicated to participants)"~"Waiver of consent",`Consent procedure`=="Active consent (Participant must explicitly agree “opt-in”)"~"Active consent",TRUE~`Consent procedure` ) )
# summarized dataalluvial_df <- df %>%select(`Intervention category`, `Comparator category`, `Medical field`) %>%filter(complete.cases(.)) %>%count(`Medical field`, `Intervention category`, `Comparator category`)# First order by total frequencymedical_freq <- alluvial_df %>%group_by(`Medical field`) %>%summarise(total =sum(n), .groups ="drop") %>%arrange(desc(total))# Define custom priority for those with total = 1custom_order_1 <-c("Primary Care / Public Health","Rheumatology","Endocrinology","Intensive Care","Gynecology / Obstetrics","Dermatology")# Create final ordering vector:# Psychiatry first among the >=2 group, then custom order for the 1-count groupfinal_medical_order <-c( medical_freq$`Medical field`[medical_freq$total >1], custom_order_1)# reorder Intervention category with Drug/Product/Device on topintervention_levels <- alluvial_df %>%distinct(`Intervention category`) %>%pull()final_intervention_order <-c("Drug/Product/Device", setdiff(intervention_levels, "Drug/Product/Device"))# Apply factor relevelingalluvial_df <- alluvial_df %>%mutate(`Medical field`=factor(`Medical field`, levels = final_medical_order),`Intervention category`=factor(`Intervention category`, levels = final_intervention_order))# determine axis tick rangemax_n <-14ggplot(alluvial_df,aes(axis1 =`Medical field`,axis2 =`Intervention category`,axis3 =`Comparator category`,y = n)) +geom_alluvium(aes(fill =`Intervention category`), width =0.25) +geom_stratum(width =0.25) +geom_label(stat ="stratum", aes(label =after_stat(stratum))) +scale_x_discrete(limits =c("Medical Field", "Intervention", "Comparator")) +scale_y_continuous(breaks =seq(0, max_n, by =1)) +theme_minimal(base_size =14) +theme(legend.position ="none",panel.grid.minor =element_blank(), # remove horizontal helper linespanel.grid.major.x =element_blank(), # remove vertical linespanel.grid.major.y =element_line(color ="grey80"),plot.background =element_blank(),panel.background =element_blank(),# axis.title.x = element_blank(),# axis.text.x = element_text(size = 12),# axis.text.y = element_text(size = 12),# force tight plot wrapping (to fix Quarto spacing)plot.margin =margin(t =10, r =5, b =5, l =5) ) +labs(y ="Number of IPDMAs" )
(4) Number of CRTs & CRT participants per IPDMA
Code
# Select CRT participant columnscrt_part_cols <-grep("CRT[0-9]+: number of participants randomized", names(df), value =TRUE)# Calculate total participants in CRTs per IPDMAdf <- df %>%rowwise() %>%mutate(total_CRT_participants =sum(c_across(all_of(crt_part_cols)), na.rm =TRUE) ) %>%ungroup()# Calculate CRT trial and CRT participant percentages per IPDMAdf <- df %>%mutate(pct_trials_CRT = (`Of RCTs with IPD obtained, number of CRTs`/`Number of eligible trials for which IPD were obtained and included in the MA`) *100,pct_participants_CRT = total_CRT_participants /`Number of eligible participants for which IPD were obtained and included in the MA`*100 ) %>%mutate(IPDMA_index =1:n())# Calculate overall percentagesoverall_pct_trials <-sum(df$`Of RCTs with IPD obtained, number of CRTs`) /sum(df$`Number of eligible trials for which IPD were obtained and included in the MA`) *100overall_pct_participants <-sum(df$total_CRT_participants, na.rm =TRUE) /sum(df$`Number of eligible participants for which IPD were obtained and included in the MA`) *100# Create data for stacked barscrt_stacked <- df %>%mutate(n_nonCRT =`Number of eligible trials for which IPD were obtained and included in the MA`-`Of RCTs with IPD obtained, number of CRTs`,pct_nonCRT =100- pct_trials_CRT,n_nonCRT_participants =`Number of eligible participants for which IPD were obtained and included in the MA`- total_CRT_participants,pct_nonCRT_participants =100- pct_participants_CRT )# Stacked plot for trialstrial_stack_df <- crt_stacked %>%select(IPDMA_index, `Of RCTs with IPD obtained, number of CRTs`, n_nonCRT, pct_trials_CRT, pct_nonCRT) %>%pivot_longer(cols =c(`Of RCTs with IPD obtained, number of CRTs`, n_nonCRT), names_to ="Type", values_to ="Count") %>%mutate(pct =ifelse(Type =="Of RCTs with IPD obtained, number of CRTs", pct_trials_CRT, pct_nonCRT),Type =factor(Type, levels =c("n_nonCRT", "Of RCTs with IPD obtained, number of CRTs"), labels =c("Non-CRT", "CRT")) )ggplot(trial_stack_df, aes(x = IPDMA_index, y = pct, fill = Type)) +geom_col(color ="white", linewidth =0.2) +geom_hline(yintercept = overall_pct_trials, linetype ="dashed", color ="darkgrey", linewidth =1) +geom_text(aes(label = Count), position =position_stack(vjust =0.5), size =3, color ="white") +scale_fill_manual(values =c("Non-CRT"="#b0c4de", "CRT"="#2E8B57")) +scale_x_continuous(breaks =1:14) +scale_y_continuous(limits =c(0, 100)) +labs(x ="IPDMA",y ="Percentage of trials",fill =NULL,title ="CRT vs Non-CRT trials per IPDMA",caption ="Counts in stacked bars, percentage on y-axis. Grey line: Average across all." ) +theme_minimal() +theme(axis.text.x =element_text(angle =0, hjust =0.5),plot.caption =element_text(hjust =0) )
Code
# Stacked plot for participantsparticipant_stack_df <- crt_stacked %>%select(IPDMA_index, total_CRT_participants, n_nonCRT_participants, pct_participants_CRT, pct_nonCRT_participants) %>%pivot_longer(cols =c(total_CRT_participants, n_nonCRT_participants), names_to ="Type", values_to ="Count") %>%mutate(pct =ifelse(Type =="total_CRT_participants", pct_participants_CRT, pct_nonCRT_participants),Type =factor(Type, levels =c("n_nonCRT_participants", "total_CRT_participants"), labels =c("Non-CRT participants", "CRT participants")) )ggplot(participant_stack_df, aes(x = IPDMA_index, y = pct, fill = Type)) +geom_col(color ="white", linewidth =0.2) +geom_hline(yintercept = overall_pct_participants, linetype ="dashed", color ="darkgrey", linewidth =1) +geom_text(aes(label = Count), position =position_stack(vjust =0.5), size =3, color ="white") +scale_fill_manual(values =c("Non-CRT participants"="#b0c4de", "CRT participants"="#2E8B57")) +scale_x_continuous(breaks =1:14) +scale_y_continuous(limits =c(0, 100)) +labs(x ="IPDMA",y ="Percentage of participants",fill =NULL,title ="CRT vs Non-CRT participants per IPDMA",caption ="Counts in stacked bars, percentage on y-axis. Grey line: Average across all." ) +theme_minimal() +theme(axis.text.x =element_text(angle =0, hjust =0.5),plot.caption =element_text(hjust =0) )