0. Training
purpose
This practical follows the order of the Stata binary-outcome
workflow, but uses simpler R code for trainees.
Question: Does hyaluronidase reduce the risk of
binary outcomes such as perineal trauma or episiotomy?
Outcome type: binary outcome: each study reports
events and totals in two groups.
Effect measure: risk ratio (RR). For undesirable
outcomes, RR < 1 favours hyaluronidase.
Important correction: the Stata forest-plot label
says “OR” in one place, but the calculation is a risk
ratio. The correct label is RR (95% CI).
1. Setup
packages <- c("readxl", "dplyr", "meta", "knitr")
missing <- packages[!sapply(packages, requireNamespace, quietly = TRUE)]
if (length(missing) > 0) install.packages(missing)
library(readxl)
library(dplyr)
library(meta)
library(knitr)
options(width = 110)
set.seed(2026)
fmt_num <- function(x, digits = 3) formatC(x, digits = digits, format = "f")
fmt_pct <- function(x, digits = 1) paste0(formatC(100 * x, digits = digits, format = "f"), "%")
fmt_p <- function(p) ifelse(p < 0.001, "<0.001", formatC(p, digits = 3, format = "f"))
# Simple Galbraith/radial plot with centre line and ±2 reference limits.
# The y-axis is the standardised effect: effect / standard error.
# The x-axis is study precision: 1 / standard error.
galbraith_plot <- function(m, main = "Galbraith/radial plot") {
effect <- m$TE
se <- m$seTE
ok <- is.finite(effect) & is.finite(se) & se > 0
effect <- effect[ok]
se <- se[ok]
labels <- m$studlab[ok]
pooled <- m$TE.random
x <- 1 / se
y <- effect / se
plot(x, y,
pch = 19,
xlab = "Inverse of standard error (precision)",
ylab = "Standardised effect (z-score)",
main = main)
abline(a = 0, b = pooled, lwd = 2) # pooled-effect line
abline(a = 2, b = pooled, lty = 2) # upper approximate 95% limit
abline(a = -2, b = pooled, lty = 2) # lower approximate 95% limit
abline(h = 0, col = "grey80")
legend("topleft",
legend = c("Pooled-effect line", "Approx. ±2 limits"),
lty = c(1, 2), lwd = c(2, 1), bty = "n", cex = 0.8)
}
# Small helper to extract key random-effects results from a meta object.
summary_row_rr <- function(m, analysis_name) {
data.frame(
Analysis = analysis_name,
Studies = m$k,
RR = exp(m$TE.random),
Lower_95_CI = exp(m$lower.random),
Upper_95_CI = exp(m$upper.random),
Tau2 = m$tau2,
I2_percent = 100 * m$I2,
Q_p_value = m$pval.Q
)
}
2. Read the Excel
dataset
Equivalent Stata idea:
clear
import excel "...Hyaluronidase_MetaAnalysis_Dataset_statafinal1.xlsx", sheet("Calculated Stats_All") firstrow cellrange(A5:P45)
possible_files <- c(
"Hyaluronidase_MetaAnalysis_Dataset_statafinal1_1.xlsx",
"Hyaluronidase_MetaAnalysis_Dataset_statafinal1.xlsx"
)
data_file <- possible_files[file.exists(possible_files)][1]
if (is.na(data_file)) stop("Put the Hyaluronidase Excel file in the same folder as this Rmd.")
sheet_names <- excel_sheets(data_file)
sheet_to_use <- if ("Calculated Stats_All" %in% sheet_names) "Calculated Stats_All" else sheet_names[1]
perineal <- read_excel(data_file, sheet = sheet_to_use, range = "A5:P45", col_names = TRUE)
names(perineal) <- c(
"Study", "ROBOverall", "aHAaseevents", "n1HAasetotal",
"cControlevents", "n2Controltotal", "RiskHAase", "RiskControl",
"RR", "lnRR", "SElnRR", "CILower", "CIUpper",
"Outcome", "TypeofStudy", "RCT_design"
)
perineal <- perineal %>%
mutate(
Study = trimws(Study),
Outcome = trimws(Outcome),
RCT_design = trimws(RCT_design),
ROBOverall = trimws(ROBOverall),
aHAaseevents = as.numeric(aHAaseevents),
n1HAasetotal = as.numeric(n1HAasetotal),
cControlevents = as.numeric(cControlevents),
n2Controltotal = as.numeric(n2Controltotal)
)
perineal %>%
select(Study, Outcome, RCT_design, aHAaseevents, n1HAasetotal,
cControlevents, n2Controltotal, ROBOverall) %>%
head(12) %>%
kable(caption = "First 12 rows of the binary-outcome dataset")
First 12 rows of the binary-outcome dataset
| Chatfield 1966* |
Perineal_Trauma |
Placebo |
58 |
67 |
62 |
67 |
High |
| Colacioppo 2011* |
Perineal_Trauma |
Placebo |
86 |
115 |
89 |
113 |
Low |
| Kwon 2020* |
Perineal_Trauma |
Placebo |
61 |
88 |
68 |
86 |
Low |
| Martinez 2019 (sim) |
Perineal_Trauma |
Placebo |
70 |
95 |
74 |
93 |
Low |
| Chen 2020 (sim) |
Perineal_Trauma |
Placebo |
82 |
110 |
86 |
108 |
Some Concerns |
| Kumar 2021 (sim) |
Perineal_Trauma |
Placebo |
55 |
80 |
59 |
78 |
High |
| Johansson 2018 (sim) |
Perineal_Trauma |
Placebo |
48 |
70 |
52 |
68 |
Some Concerns |
| Patel 2022 (sim) |
Perineal_Trauma |
Placebo |
66 |
92 |
71 |
90 |
Low |
| Nguyen 2021 (sim) |
Perineal_Trauma |
Placebo |
44 |
65 |
48 |
63 |
High |
| Osei 2020 (sim) |
Perineal_Trauma |
Placebo |
35 |
55 |
38 |
53 |
High |
| Chatfield 1966* |
Episiotomy |
Placebo |
22 |
67 |
28 |
67 |
High |
| Colacioppo 2011* |
Episiotomy |
Placebo |
41 |
115 |
47 |
113 |
Low |
3. Manual risk ratio
calculation
This section helps trainees understand what the software is
pooling.
Equivalent Stata idea:
gen riskratio = (aHAaseevents * n2Controltotal) / (n1HAasetotal * cControlevents)
gen logriskratio = ln(riskratio)
gen se_logrr_manual = sqrt((1/aHAaseevents) - (1/n1HAasetotal) + (1/cControlevents) - (1/n2Controltotal))
perineal <- perineal %>%
mutate(
riskratio = (aHAaseevents / n1HAasetotal) / (cControlevents / n2Controltotal),
logriskratio = log(riskratio),
se_logrr_manual = sqrt(
(1 / aHAaseevents) - (1 / n1HAasetotal) +
(1 / cControlevents) - (1 / n2Controltotal)
)
)
perineal %>%
select(Study, Outcome, RCT_design, riskratio, logriskratio, se_logrr_manual) %>%
mutate(
riskratio = round(riskratio, 3),
logriskratio = round(logriskratio, 3),
se_logrr_manual = round(se_logrr_manual, 3)
) %>%
head(10) %>%
kable(caption = "Manual RR, log(RR), and SE[log(RR)]")
Manual RR, log(RR), and SE[log(RR)]
| Chatfield 1966* |
Perineal_Trauma |
Placebo |
0.935 |
-0.067 |
0.059 |
| Colacioppo 2011* |
Perineal_Trauma |
Placebo |
0.949 |
-0.052 |
0.073 |
| Kwon 2020* |
Perineal_Trauma |
Placebo |
0.877 |
-0.132 |
0.090 |
| Martinez 2019 (sim) |
Perineal_Trauma |
Placebo |
0.926 |
-0.077 |
0.081 |
| Chen 2020 (sim) |
Perineal_Trauma |
Placebo |
0.936 |
-0.066 |
0.074 |
| Kumar 2021 (sim) |
Perineal_Trauma |
Placebo |
0.909 |
-0.096 |
0.099 |
| Johansson 2018 (sim) |
Perineal_Trauma |
Placebo |
0.897 |
-0.109 |
0.105 |
| Patel 2022 (sim) |
Perineal_Trauma |
Placebo |
0.909 |
-0.095 |
0.085 |
| Nguyen 2021 (sim) |
Perineal_Trauma |
Placebo |
0.888 |
-0.118 |
0.111 |
| Osei 2020 (sim) |
Perineal_Trauma |
Placebo |
0.888 |
-0.119 |
0.134 |
4. Main analysis: HAase
versus placebo
For binary event data, the simplest R function is
metabin().
placebo_data <- perineal %>% filter(RCT_design == "Placebo")
placebo_models <- list()
for (outcome_name in unique(placebo_data$Outcome)) {
cat("\n\n### Forest plot:", outcome_name, "— HAase vs placebo\n\n")
dat <- placebo_data %>% filter(Outcome == outcome_name)
m <- metabin(
event.e = aHAaseevents,
n.e = n1HAasetotal,
event.c = cControlevents,
n.c = n2Controltotal,
studlab = Study,
data = dat,
sm = "RR",
method = "MH",
common = FALSE,
random = TRUE,
method.tau = "REML",
method.random.ci = "HK"
)
placebo_models[[outcome_name]] <- m
print(summary(m))
forest(
m,
prediction = TRUE,
print.tau2 = TRUE,
print.I2 = TRUE,
leftcols = c("studlab", "event.e", "n.e", "event.c", "n.c"),
leftlabs = c("Study", "HAase events", "HAase total", "Control events", "Control total"),
rightcols = c("effect", "ci", "w.random"),
rightlabs = c("RR", "95% CI", "Weight"),
xlab = "Risk Ratio (RR)",
smlab = "HAase vs placebo"
)
}
Forest plot:
Perineal_Trauma — HAase vs placebo
RR 95%-CI %W(random)
Chatfield 1966* 0.9355 [0.8328; 1.0508] 20.3 Colacioppo 2011* 0.9495
[0.8230; 1.0954] 13.4 Kwon 2020* 0.8767 [0.7348; 1.0459] 8.8 Martinez
2019 (sim) 0.9260 [0.7905; 1.0848] 11.0 Chen 2020 (sim) 0.9362 [0.8098;
1.0822] 13.1 Kumar 2021 (sim) 0.9089 [0.7485; 1.1036] 7.3 Johansson 2018
(sim) 0.8967 [0.7296; 1.1021] 6.5 Patel 2022 (sim) 0.9094 [0.7695;
1.0746] 9.9 Nguyen 2021 (sim) 0.8885 [0.7149; 1.1042] 5.8 Osei 2020
(sim) 0.8876 [0.6831; 1.1531] 4.0
Number of studies: k = 10 Number of observations: o = 1656 (o.e =
837, o.c = 819) Number of events: e = 1252
RR 95%-CI t p-value
Random effects model 0.9193 [0.9021; 0.9368] -10.08 < 0.0001
Quantifying heterogeneity (with 95%-CIs): tau^2 = 0; tau = 0; I^2 =
0.0% [0.0%; 62.4%]; H = 1.00 [1.00; 1.63]
Test of heterogeneity: Q d.f. p-value 0.88 9 0.9997
Details of meta-analysis methods: - Inverse variance method -
Restricted maximum-likelihood estimator for tau^2 - Calculation of I^2
based on Q - Hartung-Knapp adjustment for random effects model (df = 9)

Forest plot:
Episiotomy — HAase vs placebo
RR 95%-CI %W(random)
Chatfield 1966* 0.7857 [0.5040; 1.2249] 8.8 Colacioppo 2011* 0.8572
[0.6171; 1.1907] 16.1 Kwon 2020* 0.7996 [0.5292; 1.2082] 10.2 Martinez
2019 (sim) 0.8702 [0.5946; 1.2735] 12.0 Chen 2020 (sim) 0.8677 [0.6137;
1.2267] 14.5 Kumar 2021 (sim) 0.8357 [0.5344; 1.3070] 8.7 Johansson 2018
(sim) 0.7771 [0.4789; 1.2611] 7.4 Patel 2022 (sim) 0.8106 [0.5448;
1.2059] 11.0 Nguyen 2021 (sim) 0.7930 [0.4726; 1.3307] 6.5 Osei 2020
(sim) 0.7495 [0.4164; 1.3491] 5.0
Number of studies: k = 10 Number of observations: o = 1656 (o.e =
837, o.c = 819) Number of events: e = 580
RR 95%-CI t p-value
Random effects model 0.8248 [0.7966; 0.8539] -12.53 < 0.0001
Quantifying heterogeneity (with 95%-CIs): tau^2 = 0; tau = 0; I^2 =
0.0% [0.0%; 62.4%]; H = 1.00 [1.00; 1.63]
Test of heterogeneity: Q d.f. p-value 0.47 9 1.0000
Details of meta-analysis methods: - Inverse variance method -
Restricted maximum-likelihood estimator for tau^2 - Calculation of I^2
based on Q - Hartung-Knapp adjustment for random effects model (df = 9)

5. Summary table and
interpretation
placebo_summary <- do.call(rbind, lapply(names(placebo_models), function(nm) {
summary_row_rr(placebo_models[[nm]], nm)
}))
placebo_summary %>%
mutate(
RR = fmt_num(RR),
Lower_95_CI = fmt_num(Lower_95_CI),
Upper_95_CI = fmt_num(Upper_95_CI),
Tau2 = fmt_num(Tau2, 4),
I2_percent = fmt_num(I2_percent, 1),
Q_p_value = fmt_p(Q_p_value)
) %>%
kable(caption = "Random-effects RR summary for HAase versus placebo")
Random-effects RR summary for HAase versus placebo
| Perineal_Trauma |
10 |
0.919 |
0.902 |
0.937 |
0.0000 |
0.0 |
1.000 |
| Episiotomy |
10 |
0.825 |
0.797 |
0.854 |
0.0000 |
0.0 |
1.000 |
for (nm in names(placebo_models)) {
m <- placebo_models[[nm]]
rr <- exp(m$TE.random)
lcl <- exp(m$lower.random)
ucl <- exp(m$upper.random)
cat("\n\n**", nm, ":** The pooled RR is ", fmt_num(rr),
" (95% CI ", fmt_num(lcl), " to ", fmt_num(ucl), "). ",
"Because the outcome is undesirable, RR < 1 favours hyaluronidase. ",
"I² = ", fmt_num(100 * m$I2, 1), "%, tau² = ", fmt_num(m$tau2, 4),
", Q-test p = ", fmt_p(m$pval.Q), ".", sep = "")
}
Perineal_Trauma: The pooled RR is 0.919 (95% CI
0.902 to 0.937). Because the outcome is undesirable, RR < 1 favours
hyaluronidase. I² = 0.0%, tau² = 0.0000, Q-test p = 1.000.
Episiotomy: The pooled RR is 0.825 (95% CI 0.797 to
0.854). Because the outcome is undesirable, RR < 1 favours
hyaluronidase. I² = 0.0%, tau² = 0.0000, Q-test p = 1.000.
6. Sensitivity
analysis: different tau-squared estimators
Equivalent Stata idea: compare REML, DerSimonian-Laird (DL), and
Sidik-Jonkman (SJ).
tau_methods <- c("REML", "DL", "SJ")
tau_results <- list()
for (outcome_name in unique(placebo_data$Outcome)) {
dat <- placebo_data %>% filter(Outcome == outcome_name)
for (tm in tau_methods) {
m <- metabin(
event.e = aHAaseevents, n.e = n1HAasetotal,
event.c = cControlevents, n.c = n2Controltotal,
studlab = Study, data = dat,
sm = "RR", method = "MH",
common = FALSE, random = TRUE,
method.tau = tm,
method.random.ci = "HK"
)
tau_results[[paste(outcome_name, tm, sep = " - ")]] <- summary_row_rr(m, paste(outcome_name, tm, sep = " - "))
}
}
do.call(rbind, tau_results) %>%
mutate(
RR = fmt_num(RR),
Lower_95_CI = fmt_num(Lower_95_CI),
Upper_95_CI = fmt_num(Upper_95_CI),
Tau2 = fmt_num(Tau2, 4),
I2_percent = fmt_num(I2_percent, 1),
Q_p_value = fmt_p(Q_p_value)
) %>%
kable(caption = "Sensitivity of pooled RR to tau-squared estimator")
Sensitivity of pooled RR to tau-squared estimator
| Perineal_Trauma - REML |
Perineal_Trauma - REML |
10 |
0.919 |
0.902 |
0.937 |
0.0000 |
0.0 |
1.000 |
| Perineal_Trauma - DL |
Perineal_Trauma - DL |
10 |
0.919 |
0.902 |
0.937 |
0.0000 |
0.0 |
1.000 |
| Perineal_Trauma - SJ |
Perineal_Trauma - SJ |
10 |
0.919 |
0.902 |
0.937 |
0.0001 |
0.0 |
1.000 |
| Episiotomy - REML |
Episiotomy - REML |
10 |
0.825 |
0.797 |
0.854 |
0.0000 |
0.0 |
1.000 |
| Episiotomy - DL |
Episiotomy - DL |
10 |
0.825 |
0.797 |
0.854 |
0.0000 |
0.0 |
1.000 |
| Episiotomy - SJ |
Episiotomy - SJ |
10 |
0.825 |
0.797 |
0.854 |
0.0001 |
0.0 |
1.000 |
Teaching point: if the pooled RR and conclusion are
similar across REML, DL, and SJ, the result is less dependent on the
heterogeneity estimator.
7. Leave-one-out
analysis
Equivalent Stata idea:
meta summarize ..., leaveoneout random(reml) se(khartung)
for (nm in names(placebo_models)) {
cat("\n\n### Leave-one-out:", nm, "\n\n")
loo <- metainf(placebo_models[[nm]], pooled = "random")
print(summary(loo))
forest(loo, xlab = "Risk Ratio (RR)")
}
Leave-one-out:
Perineal_Trauma
Leave-one-out meta-analysis
RR 95%-CI p-value tau^2 tau I^2
Omitting Chatfield 1966* 0.9152 [0.8958; 0.9350] < 0.0001 0 0 0%
Omitting Colacioppo 2011* 0.9147 [0.8975; 0.9321] < 0.0001 0 0 0%
Omitting Kwon 2020* 0.9235 [0.9077; 0.9396] < 0.0001 0 0 0% Omitting
Martinez 2019 (sim) 0.9185 [0.8989; 0.9384] < 0.0001 0 0 0% Omitting
Chen 2020 (sim) 0.9168 [0.8977; 0.9363] < 0.0001 0 0 0% Omitting
Kumar 2021 (sim) 0.9201 [0.9009; 0.9397] < 0.0001 0 0 0% Omitting
Johansson 2018 (sim) 0.9209 [0.9023; 0.9398] < 0.0001 0 0 0% Omitting
Patel 2022 (sim) 0.9204 [0.9010; 0.9402] < 0.0001 0 0 0% Omitting
Nguyen 2021 (sim) 0.9212 [0.9031; 0.9396] < 0.0001 0 0 0% Omitting
Osei 2020 (sim) 0.9206 [0.9024; 0.9392] < 0.0001 0 0 0%
Random effects model 0.9193 [0.9021; 0.9368] < 0.0001 0 0 0%
Details of meta-analysis methods: - Inverse variance method -
Restricted maximum-likelihood estimator for tau^2 - Calculation of I^2
based on Q - Hartung-Knapp adjustment for random effects model (df = {8,
9})

Leave-one-out:
Episiotomy
Leave-one-out meta-analysis
RR 95%-CI p-value tau^2 tau I^2
Omitting Chatfield 1966* 0.8286 [0.7983; 0.8600] < 0.0001 0 0 0%
Omitting Colacioppo 2011* 0.8187 [0.7880; 0.8506] < 0.0001 0 0 0%
Omitting Kwon 2020* 0.8277 [0.7963; 0.8603] < 0.0001 0 0 0% Omitting
Martinez 2019 (sim) 0.8188 [0.7896; 0.8490] < 0.0001 0 0 0% Omitting
Chen 2020 (sim) 0.8177 [0.7886; 0.8479] < 0.0001 0 0 0% Omitting
Kumar 2021 (sim) 0.8237 [0.7921; 0.8567] < 0.0001 0 0 0% Omitting
Johansson 2018 (sim) 0.8287 [0.7991; 0.8594] < 0.0001 0 0 0% Omitting
Patel 2022 (sim) 0.8265 [0.7945; 0.8598] < 0.0001 0 0 0% Omitting
Nguyen 2021 (sim) 0.8270 [0.7962; 0.8589] < 0.0001 0 0 0% Omitting
Osei 2020 (sim) 0.8289 [0.8013; 0.8575] < 0.0001 0 0 0%
Random effects model 0.8248 [0.7966; 0.8539] < 0.0001 0 0 0%
Details of meta-analysis methods: - Inverse variance method -
Restricted maximum-likelihood estimator for tau^2 - Calculation of I^2
based on Q - Hartung-Knapp adjustment for random effects model (df = {8,
9})

Interpretation guide: if omitting any one study does
not materially change the pooled RR or its direction, the result is not
driven by a single study.
8. Galbraith/radial
plot
Equivalent Stata idea:
meta galbraithplot ..., random(reml)
The plot below adds the pooled-effect line plus the
upper and lower approximate ±2 reference limits.
Studies outside these limits may be statistical outliers.
for (nm in names(placebo_models)) {
cat("\n\n### Galbraith/radial plot:", nm, "\n\n")
galbraith_plot(placebo_models[[nm]], main = paste("Galbraith plot:", nm))
}
Galbraith/radial
plot: Perineal_Trauma

Galbraith/radial
plot: Episiotomy

9. Funnel plot
Equivalent Stata idea:
meta funnel ..., random(reml)
for (nm in names(placebo_models)) {
cat("\n\n### Funnel plot:", nm, "\n\n")
funnel(placebo_models[[nm]], xlab = "log(RR)")
}
Funnel plot:
Perineal_Trauma

Funnel plot:
Episiotomy

10. Egger’s test for
small-study effects
Equivalent Stata idea:
meta bias ..., egger
for (nm in names(placebo_models)) {
cat("\n\n### Egger test:", nm, "\n\n")
print(metabias(placebo_models[[nm]], method.bias = "linreg", k.min = 3))
}
Egger test:
Perineal_Trauma
Linear regression test of funnel plot asymmetry
Test result: t = -3.45, df = 8, p-value = 0.0087 Bias estimate:
-1.0487 (SE = 0.3039)
Details: - multiplicative residual heterogeneity variance (tau^2 =
0.0441) - predictor: standard error - weight: inverse variance -
reference: Egger et al. (1997), BMJ
Egger test:
Episiotomy
Linear regression test of funnel plot asymmetry
Test result: t = -4.56, df = 8, p-value = 0.0018 Bias estimate:
-1.1171 (SE = 0.2447)
Details: - multiplicative residual heterogeneity variance (tau^2 =
0.0163) - predictor: standard error - weight: inverse variance -
reference: Egger et al. (1997), BMJ
Teaching caution: Egger’s test has low power with
few studies and should not be interpreted as proof of publication
bias.
11. Trim-and-fill
sensitivity analysis
Equivalent Stata idea:
meta trimfill ..., random(reml)
for (nm in names(placebo_models)) {
cat("\n\n### Trim-and-fill:", nm, "\n\n")
tf <- trimfill(placebo_models[[nm]])
print(summary(tf))
funnel(tf, xlab = "log(RR)")
}
Trim-and-fill:
Perineal_Trauma
RR 95%-CI %W(random)
Chatfield 1966* 0.9355 [0.8328; 1.0508] 17.1 Colacioppo 2011* 0.9495
[0.8230; 1.0954] 11.3 Kwon 2020* 0.8767 [0.7348; 1.0459] 7.4 Martinez
2019 (sim) 0.9260 [0.7905; 1.0848] 9.2 Chen 2020 (sim) 0.9362 [0.8098;
1.0822] 11.0 Kumar 2021 (sim) 0.9089 [0.7485; 1.1036] 6.1 Johansson 2018
(sim) 0.8967 [0.7296; 1.1021] 5.4 Patel 2022 (sim) 0.9094 [0.7695;
1.0746] 8.3 Nguyen 2021 (sim) 0.8885 [0.7149; 1.1042] 4.9 Osei 2020
(sim) 0.8876 [0.6831; 1.1531] 3.4 Filled: Nguyen 2021 (sim) 0.9690
[0.7797; 1.2044] 4.9 Filled: Osei 2020 (sim) 0.9700 [0.7466; 1.2603] 3.4
Filled: Kwon 2020* 0.9821 [0.8232; 1.1716] 7.4
Number of studies: k = 13 (with 3 added studies) Number of
observations: o = 2066 (o.e = 1045, o.c = 1021) Number of events: e =
1546
RR 95%-CI t p-value
Random effects model 0.9279 [0.9096; 0.9465] -8.20 < 0.0001
Quantifying heterogeneity (with 95%-CIs): tau^2 = 0; tau = 0; I^2 =
0.0% [0.0%; 56.6%]; H = 1.00 [1.00; 1.52]
Test of heterogeneity: Q d.f. p-value 1.66 12 0.9998
Details of meta-analysis methods: - Inverse variance method -
Restricted maximum-likelihood estimator for tau^2 - Calculation of I^2
based on Q - Hartung-Knapp adjustment for random effects model (df = 12)
- Trim-and-fill method to adjust for funnel plot asymmetry (L-estimator)

Trim-and-fill:
Episiotomy
RR 95%-CI %W(random)
Chatfield 1966* 0.7857 [0.5040; 1.2249] 6.9 Colacioppo 2011* 0.8572
[0.6171; 1.1907] 12.6 Kwon 2020* 0.7996 [0.5292; 1.2082] 8.0 Martinez
2019 (sim) 0.8702 [0.5946; 1.2735] 9.4 Chen 2020 (sim) 0.8677 [0.6137;
1.2267] 11.3 Kumar 2021 (sim) 0.8357 [0.5344; 1.3070] 6.8 Johansson 2018
(sim) 0.7771 [0.4789; 1.2611] 5.8 Patel 2022 (sim) 0.8106 [0.5448;
1.2059] 8.6 Nguyen 2021 (sim) 0.7930 [0.4726; 1.3307] 5.1 Osei 2020
(sim) 0.7495 [0.4164; 1.3491] 3.9 Filled: Nguyen 2021 (sim) 0.8966
[0.5343; 1.5046] 5.1 Filled: Chatfield 1966* 0.9049 [0.5805; 1.4107] 6.9
Filled: Johansson 2018 (sim) 0.9149 [0.5638; 1.4847] 5.8 Filled: Osei
2020 (sim) 0.9487 [0.5270; 1.7076] 3.9
Number of studies: k = 14 (with 4 added studies) Number of
observations: o = 2164 (o.e = 1094, o.c = 1070) Number of events: e =
747
RR 95%-CI t p-value
Random effects model 0.8432 [0.8138; 0.8737] -10.37 < 0.0001
Quantifying heterogeneity (with 95%-CIs): tau^2 = 0; tau = 0; I^2 =
0.0% [0.0%; 55.0%]; H = 1.00 [1.00; 1.49]
Test of heterogeneity: Q d.f. p-value 0.99 13 1.0000
Details of meta-analysis methods: - Inverse variance method -
Restricted maximum-likelihood estimator for tau^2 - Calculation of I^2
based on Q - Hartung-Knapp adjustment for random effects model (df = 13)
- Trim-and-fill method to adjust for funnel plot asymmetry (L-estimator)

Teaching caution: trim-and-fill is exploratory.
Report it as a sensitivity analysis, not as a definitive correction for
publication bias.
12. Subgroup analysis
by comparison design
Equivalent Stata idea: compare placebo-controlled and no-intervention
studies.
for (outcome_name in unique(perineal$Outcome)) {
cat("\n\n### Subgroup analysis by RCT design:", outcome_name, "\n\n")
dat <- perineal %>% filter(Outcome == outcome_name)
m_sub <- metabin(
event.e = aHAaseevents,
n.e = n1HAasetotal,
event.c = cControlevents,
n.c = n2Controltotal,
studlab = Study,
data = dat,
sm = "RR",
method = "MH",
common = FALSE,
random = TRUE,
method.tau = "REML",
method.random.ci = "HK",
subgroup = RCT_design
)
print(summary(m_sub))
forest(
m_sub,
prediction = TRUE,
print.tau2 = TRUE,
print.I2 = TRUE,
xlab = "Risk Ratio (RR)",
smlab = "Subgrouped by comparison design"
)
}
Subgroup analysis by
RCT design: Perineal_Trauma
RR 95%-CI %W(random) RCT_design
Chatfield 1966* 0.9355 [0.8328; 1.0508] 6.4 Placebo Colacioppo 2011*
0.9495 [0.8230; 1.0954] 5.9 Placebo Kwon 2020* 0.8767 [0.7348; 1.0459]
5.3 Placebo Martinez 2019 (sim) 0.9260 [0.7905; 1.0848] 5.7 Placebo Chen
2020 (sim) 0.9362 [0.8098; 1.0822] 5.9 Placebo Kumar 2021 (sim) 0.9089
[0.7485; 1.1036] 5.0 Placebo Johansson 2018 (sim) 0.8967 [0.7296;
1.1021] 4.8 Placebo Patel 2022 (sim) 0.9094 [0.7695; 1.0746] 5.5 Placebo
Nguyen 2021 (sim) 0.8885 [0.7149; 1.1042] 4.6 Placebo Osei 2020 (sim)
0.8876 [0.6831; 1.1531] 3.9 Placebo Chatfield 1966* 0.8667 [0.7893;
0.9517] 6.8 No Intervention O’Leary 1965* 0.8571 [0.7547; 0.9735] 6.2 No
Intervention Scarabotto 2008* 0.7602 [0.6286; 0.9194] 5.1 No
Intervention Fernando 2017 (sim) 0.5993 [0.4596; 0.7816] 3.9 No
Intervention Diallo 2019 (sim) 0.6119 [0.4663; 0.8029] 3.8 No
Intervention Park 2018 (sim) 0.6314 [0.5036; 0.7917] 4.5 No Intervention
Ahmed 2020 (sim) 0.6127 [0.4842; 0.7754] 4.3 No Intervention Rossi 2021
(sim) 0.6327 [0.5101; 0.7849] 4.6 No Intervention Williams 2022 (sim)
0.6243 [0.4933; 0.7902] 4.3 No Intervention Tanaka 2016 (sim) 0.6143
[0.4562; 0.8273] 3.4 No Intervention
Number of studies: k = 20 Number of observations: o = 2858 (o.e =
1448, o.c = 1410) Number of events: e = 2166
RR 95%-CI t p-value
Random effects model 0.8027 [0.7390; 0.8720] -5.56 < 0.0001
Quantifying heterogeneity (with 95%-CIs): tau^2 = 0.0192 [0.0075;
0.0600]; tau = 0.1385 [0.0863; 0.2450] I^2 = 65.6% [44.9%; 78.6%]; H =
1.71 [1.35; 2.16]
Test of heterogeneity: Q d.f. p-value 55.27 19 < 0.0001
Results for subgroups (random effects model): k RR 95%-CI tau^2 tau Q
I^2 RCT_design = Placebo 10 0.9193 [0.9021; 0.9368] 0 0 0.88 0.0%
RCT_design = No Intervention 10 0.6944 [0.6204; 0.7773] 0.0180 0.1343
29.71 69.7%
Test for subgroup differences (random effects model): Q d.f. p-value
Between groups 30.83 1 < 0.0001
Details of meta-analysis methods: - Inverse variance method -
Restricted maximum-likelihood estimator for tau^2 - Q-Profile method for
confidence interval of tau^2 and tau - Calculation of I^2 based on Q -
Hartung-Knapp adjustment for random effects model (df = 19) - Continuity
correction of 0.5 in studies with zero cell frequencies

Subgroup analysis by
RCT design: Episiotomy
RR 95%-CI %W(random) RCT_design
Chatfield 1966* 0.7857 [0.5040; 1.2249] 5.5 Placebo Colacioppo 2011*
0.8572 [0.6171; 1.1907] 10.1 Placebo Kwon 2020* 0.7996 [0.5292; 1.2082]
6.4 Placebo Martinez 2019 (sim) 0.8702 [0.5946; 1.2735] 7.5 Placebo Chen
2020 (sim) 0.8677 [0.6137; 1.2267] 9.1 Placebo Kumar 2021 (sim) 0.8357
[0.5344; 1.3070] 5.5 Placebo Johansson 2018 (sim) 0.7771 [0.4789;
1.2611] 4.7 Placebo Patel 2022 (sim) 0.8106 [0.5448; 1.2059] 6.9 Placebo
Nguyen 2021 (sim) 0.7930 [0.4726; 1.3307] 4.1 Placebo Osei 2020 (sim)
0.7495 [0.4164; 1.3491] 3.2 Placebo Chatfield 1966* 0.6192 [0.4106;
0.9338] 6.5 No Intervention O’Leary 1965* 0.6364 [0.3696; 1.0958] 3.7 No
Intervention Scarabotto 2008* 0.6280 [0.3844; 1.0260] 4.5 No
Intervention Fernando 2017 (sim) 0.4833 [0.2581; 0.9051] 2.8 No
Intervention Diallo 2019 (sim) 0.4808 [0.2504; 0.9231] 2.6 No
Intervention Park 2018 (sim) 0.5204 [0.3060; 0.8850] 3.9 No Intervention
Ahmed 2020 (sim) 0.4828 [0.2682; 0.8689] 3.2 No Intervention Rossi 2021
(sim) 0.5185 [0.3114; 0.8633] 4.2 No Intervention Williams 2022 (sim)
0.4960 [0.2797; 0.8795] 3.3 No Intervention Tanaka 2016 (sim) 0.4792
[0.2402; 0.9557] 2.3 No Intervention
Number of studies: k = 20 Number of observations: o = 2858 (o.e =
1448, o.c = 1410) Number of events: e = 971
RR 95%-CI t p-value
Random effects model 0.7077 [0.6383; 0.7848] -7.00 < 0.0001
Quantifying heterogeneity (with 95%-CIs): tau^2 = 0 [0.0000; 0.0505];
tau = 0 [0.0000; 0.2248] I^2 = 0.0% [0.0%; 48.0%]; H = 1.00 [1.00;
1.39]
Test of heterogeneity: Q d.f. p-value 16.26 19 0.6398
Results for subgroups (random effects model): k RR 95%-CI tau^2 tau Q
I^2 RCT_design = Placebo 10 0.8248 [0.7966; 0.8539] 0 0 0.47 0.0%
RCT_design = No Intervention 10 0.5451 [0.4994; 0.5950] 0 0 1.75
0.0%
Test for subgroup differences (random effects model): Q d.f. p-value
Between groups 98.73 1 < 0.0001
Details of meta-analysis methods: - Inverse variance method -
Restricted maximum-likelihood estimator for tau^2 - Q-Profile method for
confidence interval of tau^2 and tau - Calculation of I^2 based on Q -
Hartung-Knapp adjustment for random effects model (df = 19)

13. Report-ready
wording
In placebo-controlled studies, hyaluronidase was analysed using risk
ratios and a random-effects model with REML estimation of between-study
variance and Hartung-Knapp confidence intervals. Values below 1 favoured
hyaluronidase. Results were checked using tau-estimator sensitivity,
leave-one-out analysis, Galbraith/radial plots, funnel plots, Egger’s
test, trim-and-fill sensitivity analysis, and subgroup analysis by
comparison design.
14. Key learning
points
- For binary outcomes, the simplest R function is
metabin().
event.e and n.e are the events and total
in the experimental/intervention group.
event.c and n.c are the events and total
in the control group.
- The correct effect label here is risk ratio, not
odds ratio.
- A Galbraith plot is easier to teach when it shows the pooled-effect
line plus upper and lower ±2 limits.
- Robustness checks support interpretation but do not replace
study-level risk-of-bias assessment.