dat <- read_excel("Practice Dataset Volume 07082023 (1).xlsx") %>%
mutate(region_wb = countrycode(country, origin = 'country.name', destination = c('region'))) %>% # regional data from WB
inner_join(wbstats::wb_cachelist$countries %>%
select(country, iso3c, income_level),by = "country")
interventions at a glace (focusing on frequently used words in intervention description)
library(tidytext)
library(SnowballC)
dat %>%
tidytext::unnest_tokens(word, inter_desc) %>%
anti_join(stop_words) %>%
mutate(word = wordStem(word)) %>%
filter(str_detect(word, "[::digits::]")) %>%
count(word, sort = T) %>%
filter(n > 10) %>%
wordcloud2::wordcloud2()
## Joining with `by = join_by(word)`
library(plotly)
a <- dat %>%
mutate(authors_rsvd = str_remove_all(authors, "\\s+\\d+")) %>%
mutate(authors_rsvd = str_replace_all(authors_rsvd, "and", "&")) %>%
mutate(authors_year = str_extract(authors, "\\d+")) %>%
mutate(authors_rsvd = glue::glue("{authors_rsvd} ({authors_year})")) %>%
# select(yi, nfci_lower, nfci_upper, n_T_revised, authors_ctry_rob_inter) %>%
mutate(authors_rsvd = fct_reorder(authors_rsvd , yi)) %>%
ggplot(aes(yi, authors_rsvd,
text = paste0(
"Intervention subcode =",inter_subcode,"\n",
"ROB rating =", overall_rob,"\n",
"Region=", region,"\n",
"Country =", country,"\n",
"Outcome Description =", outcome_desc,"\n",
"Sample size =", n_T_revised))) +
geom_errorbarh(aes(xmin = nfci_lower,
xmax = nfci_upper),
height = 0.3,
size = 2) +
geom_point(size = 3, show.legend = F) +
geom_vline(xintercept = 0, lty = 2, size = 2, , show.legend = F, color = "red", alpha = .7) +
labs(y = "") +
labs(x = "Weighted average effect and confidence interval") +
theme(axis.text.y = element_text(size = 20, hjust = 0))
b <- ggplotly(a, tooltip = c("text"))
layout(b, yaxis = list(categoryorder = "array"))
dat %>%
count(inter_subcode) %>%
mutate(pct = n / 16) %>%
mutate(inter_subcode = fct_reorder(inter_subcode, pct)) %>%
ggplot(aes(pct, glue::glue("{inter_subcode} \n(N = {n})"), fill = inter_subcode)) +
geom_col(show.legend = F) +
geom_label(aes(label = scales::percent(pct)), show.legend = F) +
labs(x = "Percentage",
y = "") +
theme(axis.text.y = element_text(hjust = 0.5))
dat %>%
filter(inter_subcode == "Arranged or curated offers from buyers") %>%
pull(inter_desc)
## [1] "p.1: The SDCP, implemented by the Government of Kenya (GoK) from 2005 to 2015, was\r\ndesigned to reach dairy groups engaged in milk production in nine milk-producing\r\ncounties. The overall goal of SDCP was to increase the income of poor rural households\r\nthat depend substantially on production and trade of dairy products for their livelihoods.\r\nThe SDCP was implemented through various interrelated components.\r\nFirst, SDCP provided training on organisational, managerial, and enterprise skills (e.g.\r\nbookkeeping, accounting and financial planning) to farmers. Second, the programme\r\ntargeted household production by aiming to enhance dairy farming productivity and\r\nreduce production costs through grants, trainings, field days and demonstrations. Last,\r\nSDCP aimed to strengthen market linkages for small-scale milk producers by improving\r\nroad infrastructure and conducting additional trainings on milk handling practices and\r\nvalue-addition opportunities."
dat %>%
select(multi_inter, inter_subcode, id)
## # A tibble: 16 × 3
## multi_inter inter_subcode id
## <dbl> <chr> <chr>
## 1 0 Domestic transport infrastructure 7362…
## 2 0 Domestic transport infrastructure 7362…
## 3 0 Domestic transport infrastructure 7362…
## 4 1 Arranged or curated offers from buyers 7362…
## 5 0 Domestic transport infrastructure 7362…
## 6 0 Domestic transport infrastructure 7362…
## 7 1 Domestic transport infrastructure 7379…
## 8 0 Domestic transport infrastructure 7380…
## 9 0 Domestic transport infrastructure 7380…
## 10 1 Domestic transport infrastructure 7466…
## 11 1 Domestic transport infrastructure ; Alternative physical … 7471…
## 12 0 Domestic transport infrastructure 7471…
## 13 1 Domestic transport infrastructure 7471…
## 14 0 Domestic transport infrastructure 7471…
## 15 1 Domestic transport infrastructure 7541…
## 16 0 Domestic transport infrastructure 7727…
dat %>%
count(income_level) %>%
mutate(pct = n / 16) %>%
mutate(income_level = fct_reorder(income_level, pct)) %>%
ggplot(aes(pct, income_level, fill = income_level)) +
geom_col(show.legend = F) +
geom_label(aes(label = scales::percent(pct)), show.legend = F) +
labs(x = "Percentage",
y = "") +
theme(axis.text.y = element_text(hjust = 0.5))
“The twin island state of Trinidad and Tobago, is a small but high-income country. With a population of 1.3 million people and rich in natural resources, it has one of the highest GNI per capita in Latin America and the Caribbean (US$17,640 in 2015, Atlas method)” (WB website) https://www.worldbank.org/en/country/trinidadandtobago/overview#:~:text=The%20twin%20island%20state%20of,in%202015%2C%20Atlas%20method).
dat %>%
count(country, region, region_wb)
## # A tibble: 15 × 4
## country region region_wb n
## <chr> <chr> <chr> <int>
## 1 Argentina Latin America Latin Americ… 1
## 2 Armenia East Europe and Central and West Asia Europe & Cen… 1
## 3 Bangladesh South-East Asia South Asia 2
## 4 China East Asia East Asia & … 1
## 5 El Salvador Latin America Latin Americ… 1
## 6 India South Asia South Asia 1
## 7 Kenya Sub-Saharan Africa Sub-Saharan … 1
## 8 Liberia Sub-Saharan Africa Sub-Saharan … 1
## 9 Madagascar Sub-Saharan Africa Sub-Saharan … 1
## 10 Nicaragua Latin America Latin Americ… 1
## 11 Nigeria Sub-Saharan Africa Sub-Saharan … 1
## 12 Rwanda Sub-Saharan Africa Sub-Saharan … 1
## 13 Tanzania Sub-Saharan Africa Sub-Saharan … 1
## 14 Trinidad and Tobago Latin America Latin Americ… 1
## 15 Zambia Sub-Saharan Africa Sub-Saharan … 1
library(corrr)
## Warning: 패키지 'corrr'는 R 버전 4.2.3에서 작성되었습니다
dat %>%
select_if(is.numeric) %>%
select(-multi_inter) %>%
correlate() %>%
focus(yi) %>%
mutate(direction = ifelse(yi > 0, "Positive", "Negative")) %>%
mutate(term = fct_reorder(factor(term), yi)) %>%
ggplot(aes(yi, term, fill =direction)) +
geom_col() +
labs(x = "Correlation with effect size (yi)",
y= "") +
theme(axis.text.y = element_text(hjust = .5))
## Warning in stats::cor(x = x, y = y, use = use, method = method): 표준편차가
## 0입니다
## Correlation computed with
## • Method: 'pearson'
## • Missing treated using: 'pairwise.complete.obs'
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `term = fct_reorder(factor(term), yi)`.
## Caused by warning:
## ! `fct_reorder()` removing 1 missing value.
## ℹ Use `.na_rm = TRUE` to silence this message.
## ℹ Use `.na_rm = FALSE` to preserve NAs.
## Warning: Removed 1 rows containing missing values (`position_stack()`).
#forest plot ordered by effect size
resoutcomesubcode_meta <- metagen(TE = yi,
seTE = sqrt(vi),
data=dat,
sm = "SMD",
fixed = F,
random = T,
method.tau = "DL",
studlab = authors_ctry_rob_inter)
forest.meta(resoutcomesubcode_meta,
sortvar = TE,
leftcols = c("studlab"),
leftlabs = c("Author (Year), Country, Intervention abbreviation, Crop "),
rightcols = c("TE", "ci"),
weight.study = "common",
digits.TE = 2,
print.Q = F,
print.I2 = F,
print.pval.Q = F,
print.tau2 = F,
random =F,
text.addline2 = format("\n\n* indicates some concerns or low risk of bias"))
inf <- influence(resoutcomesubcode)
plot(inf)
funnel(resoutcomesubcode)
funnel(trimfill(resoutcomesubcode))
regtest(resoutcomesubcode, model = "lm")
##
## Regression Test for Funnel Plot Asymmetry
##
## Model: weighted regression with multiplicative dispersion
## Predictor: standard error
##
## Test for Funnel Plot Asymmetry: t = 0.6772, df = 14, p = 0.5093
## Limit Estimate (as sei -> 0): b = -0.0226 (CI: -0.1589, 0.1137)
https://bookdown.org/MathiasHarrer/Doing_Meta_Analysis_in_R/pub-bias.html?#eggers-test
(previously, the command did not specify the method. But it would be great to use the same estimator as the main regression, add the prompt “method =”DL”)
rma(yi, vi,
mods = ~ region_wb,
method = "DL", # the same method as the main meta regression
data = dat %>%
mutate(region_wb = fct_relevel(region_wb, "Sub-Saharan Africa")) # assign the reference group in the regression
) %>%
tidy()
## # A tibble: 5 × 6
## term type estimate std.error statis…¹ p.value
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 intercept summary 0.0394 0.0473 0.834 0.404
## 2 region_wbEast Asia & Pacific summary -0.199 0.123 -1.61 0.107
## 3 region_wbEurope & Central Asia summary -0.0325 0.118 -0.276 0.782
## 4 region_wbLatin America & Caribbean summary 0.0519 0.0820 0.633 0.527
## 5 region_wbSouth Asia summary -0.0546 0.0891 -0.613 0.540
## # … with abbreviated variable name ¹statistic
dat %>%
mutate(region_wb = fct_relevel(region_wb, "Sub-Saharan Africa")) %>%
count(region_wb)
## # A tibble: 5 × 2
## region_wb n
## <fct> <int>
## 1 Sub-Saharan Africa 7
## 2 East Asia & Pacific 1
## 3 Europe & Central Asia 1
## 4 Latin America & Caribbean 4
## 5 South Asia 3
rma(yi, vi,
mods = ~ income_level,
method = "DL", # the same method as the main meta regression
data = dat %>%
mutate(income_level = fct_relevel(income_level, c("Low income", "Lower middle income", "Upper middle income", "High income"))))
##
## Mixed-Effects Model (k = 16; tau^2 estimator: DL)
##
## tau^2 (estimated amount of residual heterogeneity): 0.0101 (SE = 0.0060)
## tau (square root of estimated tau^2 value): 0.1003
## I^2 (residual heterogeneity / unaccounted variability): 75.65%
## H^2 (unaccounted variability / sampling variability): 4.11
## R^2 (amount of heterogeneity accounted for): 0.00%
##
## Test for Residual Heterogeneity:
## QE(df = 12) = 49.2851, p-val < .0001
##
## Test of Moderators (coefficients 2:4):
## QM(df = 3) = 2.7445, p-val = 0.4327
##
## Model Results:
##
## estimate se zval pval ci.lb
## intrcpt 0.0440 0.0660 0.6668 0.5049 -0.0854
## income_levelLower middle income -0.0318 0.0779 -0.4085 0.6829 -0.1845
## income_levelUpper middle income -0.0722 0.0984 -0.7337 0.4631 -0.2650
## income_levelHigh income 0.1635 0.1439 1.1364 0.2558 -0.1185
## ci.ub
## intrcpt 0.1734
## income_levelLower middle income 0.1208
## income_levelUpper middle income 0.1207
## income_levelHigh income 0.4455
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
dat %>%
count(income_level)
## # A tibble: 4 × 2
## income_level n
## <chr> <int>
## 1 High income 1
## 2 Low income 3
## 3 Lower middle income 9
## 4 Upper middle income 3
rma(yi, vi,
mods = ~ multi_inter2,
method = "DL", # the same method as the main meta regression
data = dat %>%
mutate(multi_inter2 = ifelse(str_detect(inter_subcode,";"), "No", "Yes"))
)
##
## Mixed-Effects Model (k = 16; tau^2 estimator: DL)
##
## tau^2 (estimated amount of residual heterogeneity): 0.0065 (SE = 0.0041)
## tau (square root of estimated tau^2 value): 0.0805
## I^2 (residual heterogeneity / unaccounted variability): 68.71%
## H^2 (unaccounted variability / sampling variability): 3.20
## R^2 (amount of heterogeneity accounted for): 24.81%
##
## Test for Residual Heterogeneity:
## QE(df = 14) = 44.7488, p-val < .0001
##
## Test of Moderators (coefficient 2):
## QM(df = 1) = 4.1061, p-val = 0.0427
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## intrcpt -0.1597 0.0933 -1.7125 0.0868 -0.3425 0.0231 .
## multi_inter2Yes 0.1969 0.0971 2.0264 0.0427 0.0064 0.3873 *
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
dat %>%
mutate(multi_inter2 = ifelse(str_detect(inter_subcode,";"), "No", "Yes")) %>%
count(multi_inter2)
## # A tibble: 2 × 2
## multi_inter2 n
## <chr> <int>
## 1 No 1
## 2 Yes 15
rma(yi, vi,
mods = ~ road,
method = "DL", # the same method as the main meta regression
data = dat %>%
mutate(road = str_detect(road_bridge, "Road"))
)
##
## Mixed-Effects Model (k = 16; tau^2 estimator: DL)
##
## tau^2 (estimated amount of residual heterogeneity): 0.0100 (SE = 0.0057)
## tau (square root of estimated tau^2 value): 0.0998
## I^2 (residual heterogeneity / unaccounted variability): 76.61%
## H^2 (unaccounted variability / sampling variability): 4.28
## R^2 (amount of heterogeneity accounted for): 0.00%
##
## Test for Residual Heterogeneity:
## QE(df = 14) = 59.8664, p-val < .0001
##
## Test of Moderators (coefficient 2):
## QM(df = 1) = 0.0018, p-val = 0.9666
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## intrcpt 0.0261 0.0773 0.3378 0.7355 -0.1254 0.1776
## roadTRUE -0.0035 0.0841 -0.0419 0.9666 -0.1684 0.1614
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
dat %>%
mutate(road = str_detect(road_bridge, "Road")) %>% count(road)
## # A tibble: 2 × 2
## road n
## <lgl> <int>
## 1 FALSE 2
## 2 TRUE 14
rma(yi, vi,
mods = ~ road_bridge,
method = "DL", # the same method as the main meta regression
data = dat %>%
mutate(road_bridge = str_detect(road_bridge, ";"))
)
##
## Mixed-Effects Model (k = 16; tau^2 estimator: DL)
##
## tau^2 (estimated amount of residual heterogeneity): 0.0087 (SE = 0.0051)
## tau (square root of estimated tau^2 value): 0.0935
## I^2 (residual heterogeneity / unaccounted variability): 75.15%
## H^2 (unaccounted variability / sampling variability): 4.02
## R^2 (amount of heterogeneity accounted for): 0.00%
##
## Test for Residual Heterogeneity:
## QE(df = 14) = 56.3458, p-val < .0001
##
## Test of Moderators (coefficient 2):
## QM(df = 1) = 0.9255, p-val = 0.3360
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## intrcpt 0.0056 0.0341 0.1630 0.8705 -0.0613 0.0724
## road_bridgeTRUE 0.0629 0.0654 0.9621 0.3360 -0.0652 0.1910
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
rma(yi, vi,
mods = ~ year_end,
method = "DL", # the same method as the main meta regression
data = dat %>%
mutate(year_end = year_inter + length_followup/12 + expfollow_up) %>%
mutate(year_end = year_end >= 2019))
## Warning: 1 study with NAs omitted from model fitting.
##
## Mixed-Effects Model (k = 15; tau^2 estimator: DL)
##
## tau^2 (estimated amount of residual heterogeneity): 0.0095 (SE = 0.0055)
## tau (square root of estimated tau^2 value): 0.0972
## I^2 (residual heterogeneity / unaccounted variability): 76.77%
## H^2 (unaccounted variability / sampling variability): 4.31
## R^2 (amount of heterogeneity accounted for): 0.00%
##
## Test for Residual Heterogeneity:
## QE(df = 13) = 55.9668, p-val < .0001
##
## Test of Moderators (coefficient 2):
## QM(df = 1) = 0.2882, p-val = 0.5914
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## intrcpt 0.0131 0.0329 0.3997 0.6894 -0.0513 0.0776
## year_endTRUE 0.0434 0.0809 0.5369 0.5914 -0.1151 0.2019
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
dat %>%
mutate(year_end = year_inter + length_followup/12 + expfollow_up) %>%
mutate(year_end = year_end >= 2019) %>%
count(year_end)
## # A tibble: 3 × 2
## year_end n
## <lgl> <int>
## 1 FALSE 13
## 2 TRUE 2
## 3 NA 1
datlowrob <- dat %>%
filter(!str_detect(overall_rob, "High"))
resoutcomesubcodenohi <- rma(yi, vi, method="DL", data=datlowrob)
resoutcomesubcodenohi
##
## Random-Effects Model (k = 5; tau^2 estimator: DL)
##
## tau^2 (estimated amount of total heterogeneity): 0.0226 (SE = 0.0188)
## tau (square root of estimated tau^2 value): 0.1502
## I^2 (total heterogeneity / total variability): 87.22%
## H^2 (total variability / sampling variability): 7.83
##
## Test for Heterogeneity:
## Q(df = 4) = 31.3010, p-val < .0001
##
## Model Results:
##
## estimate se zval pval ci.lb ci.ub
## 0.0485 0.0723 0.6702 0.5027 -0.0932 0.1902
##
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
resoutcomesubcodehi <- rma(yi, vi, method="DL", data= dat %>% filter(str_detect(overall_rob, "High")))
resoutcomesubcodenohi_meta <- metagen(TE = yi,
seTE = sqrt(vi),
data=datlowrob,
sm = "SMD",
fixed = F,
random = T,
method.tau = "DL",
studlab = authors_ctry_rob_inter)
forest.meta(resoutcomesubcodenohi_meta,
sortvar = TE,
leftcols = c("studlab"),
leftlabs = c("Author (Year), Country, Intervention abbreviation, Crop "),
# rightcols = c("TE", "ci", "w.random"),
weight.study = "common",
digits.TE = 2,
print.Q = T,
print.I2 = T,
print.pval.Q = T,
text.addline2 = format("\n\n* indicates some concerns or low risk of bias"))
Number_study <-
dat %>%
mutate(type = ifelse(overall_rob != "High risk of bias", "estimates \nexcluding high risk of bias", "others")) %>%
count(type) %>%
janitor::adorn_totals(name = "all estimates") %>%
as_tibble() %>%
filter(type != "others")
comparision <-
tidy(resoutcomesubcode, conf.int = T) %>%
mutate(type = "all estimates") %>%
# bind_rows(tidy(resoutcomesubcodehi, conf.int = T) %>%
# mutate(type = "estimates only for high risk of bias"))
bind_rows(tidy(resoutcomesubcodenohi, conf.int = T) %>%
mutate(type = "estimates \nexcluding high risk of bias")) %>%
inner_join(Number_study, by = "type")
comparision %>%
mutate(type = str_to_sentence(type)) %>%
mutate(type = glue::glue("{type} (N = {n})")) %>%
mutate(type = fct_reorder(type, estimate)) %>%
ggplot(aes(x = estimate , y = type, color = type)) +
geom_errorbarh(aes(xmin = conf.low,
xmax = conf.high),
height = 0.3,
size = 2, show.legend = F) +
geom_point(size = 5, show.legend = F) +
geom_vline(xintercept = 0, lty = 2, linewidth = 2, , show.legend = F) +
labs(y = "",
x = "Weighted average effect and confidence interval") +
theme_light() +
theme(axis.text.y = element_text(size = 20,
hjust = .5))
rma(yi, vi,
mods = ~ nonstaggered,
data=dat) %>% tidy()
## # A tibble: 3 × 6
## term type estimate std.error statistic p.value
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 intercept summary 0.503 0.408 1.23 0.218
## 2 nonstaggeredNo summary -0.484 0.410 -1.18 0.238
## 3 nonstaggeredYes summary -0.475 0.414 -1.15 0.251
dat %>% count(nonstaggered)
## # A tibble: 3 × 2
## nonstaggered n
## <chr> <int>
## 1 NA 1
## 2 No 12
## 3 Yes 3
rma(yi, vi,
mods = ~ nonstaggered,
data=dat %>% filter(nonstaggered != "NA")) %>% tidy()
## # A tibble: 2 × 6
## term type estimate std.error statistic p.value
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 intercept summary 0.0188 0.0371 0.506 0.613
## 2 nonstaggeredYes summary 0.00833 0.0775 0.108 0.914
If we filtered out “NA” observations, the results can be more intepretable. non-staggeredYes vs non-staggeredNo
So, it would be better if the reference groups need to be reported in the moderator analysis