Review: Maximum Inspiratory Pressure (MIP) generation in wind instrument ...
Number of studies: k = 4
Number of observations: o = 211 (o.e = 120, o.c = 91)
MD 95% CI z p-value
Common effect model 20.8060 [13.7368; 27.8753] 5.77 < 0.0001
Quantifying heterogeneity (with 95% CIs):
tau^2 = 322.5526 [68.0641; >3225.5258]; tau = 17.9597 [8.2501; >56.7937]
I^2 = 86.9% [68.4%; 94.6%]; H = 2.76 [1.78; 4.29]
Test of heterogeneity:
Q d.f. p-value
22.87 3 < 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
Code
# Display summary statistics for MIPcat("\nSummary for Maximum Inspiratory Pressure (MIP):\n")
Summary for Maximum Inspiratory Pressure (MIP):
Code
cat("Random-effects model (with Hartung-Knapp adjustment):\n")
Random-effects model (with Hartung-Knapp adjustment):
Code
print(MIP_rma)
Random-Effects Model (k = 4; tau^2 estimator: REML)
tau^2 (estimated amount of total heterogeneity): 322.5526 (SE = 309.7010)
tau (square root of estimated tau^2 value): 17.9597
I^2 (total heterogeneity / total variability): 85.74%
H^2 (total variability / sampling variability): 7.01
Test for Heterogeneity:
Q(df = 3) = 22.8665, p-val < .0001
Model Results:
estimate se tval df pval ci.lb ci.ub
17.5094 9.6336 1.8175 3 0.1667 -13.1490 48.1678
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Code
cat("\nFixed-effect model:\n")
Fixed-effect model:
Code
print(MIP_fe)
Fixed-Effects Model (k = 4)
I^2 (total heterogeneity / total variability): 86.88%
H^2 (total variability / sampling variability): 7.62
Test for Heterogeneity:
Q(df = 3) = 22.8665, p-val < .0001
Model Results:
estimate se zval pval ci.lb ci.ub
20.8060 3.6068 5.7685 <.0001 13.7368 27.8753 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Review: Maximum Expiratory Pressure (MEP) generation in wind instrumenta ...
Number of studies: k = 3
Number of observations: o = 185 (o.e = 107, o.c = 78)
MD 95% CI z p-value
Common effect model 15.3436 [7.0355; 23.6518] 3.62 0.0003
Quantifying heterogeneity (with 95% CIs):
tau^2 = 122.8165 [0.0000; >1228.1648]; tau = 11.0823 [0.0000; >35.0452]
I^2 = 50.2% [0.0%; 85.6%]; H = 1.42 [1.00; 2.64]
Test of heterogeneity:
Q d.f. p-value
4.02 2 0.1341
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
Code
# Display summary statistics for MEPcat("\nSummary for Maximum Expiratory Pressure (MEP):\n")
Summary for Maximum Expiratory Pressure (MEP):
Code
cat("Random-effects model (with Hartung-Knapp adjustment):\n")
Random-effects model (with Hartung-Knapp adjustment):
Code
print(MEP_rma)
Random-Effects Model (k = 3; tau^2 estimator: REML)
tau^2 (estimated amount of total heterogeneity): 122.8165 (SE = 251.1952)
tau (square root of estimated tau^2 value): 11.0823
I^2 (total heterogeneity / total variability): 50.13%
H^2 (total variability / sampling variability): 2.01
Test for Heterogeneity:
Q(df = 2) = 4.0189, p-val = 0.1341
Model Results:
estimate se tval df pval ci.lb ci.ub
21.1392 8.9177 2.3705 2 0.1412 -17.2307 59.5091
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Code
cat("\nFixed-effect model:\n")
Fixed-effect model:
Code
print(MEP_meta)
Review: Maximum Expiratory Pressure (MEP) generation in wind instrumenta ...
Number of studies: k = 3
Number of observations: o = 185 (o.e = 107, o.c = 78)
MD 95% CI z p-value
Common effect model 15.3436 [7.0355; 23.6518] 3.62 0.0003
Quantifying heterogeneity (with 95% CIs):
tau^2 = 122.8165 [0.0000; >1228.1648]; tau = 11.0823 [0.0000; >35.0452]
I^2 = 50.2% [0.0%; 85.6%]; H = 1.42 [1.00; 2.64]
Test of heterogeneity:
Q d.f. p-value
4.02 2 0.1341
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
# Risk of Bias Visualization with Symbols ----library(ggplot2)library(tidyr)library(dplyr)# Read the CSV filedata1 <-read.csv("C:/Users/sjmor/OneDrive - The University of Sydney (Staff)/PhD Studies/Project 1. SysRev/New Review/Base/Figures/ROB_data.csv", header =TRUE, check.names =FALSE)# Define output directoryoutput_dir <-"C:/Users/sjmor/OneDrive - The University of Sydney (Staff)/PhD Studies/Project 1. SysRev/New Review/Base/Figures"# Get all RoB columns (columns 2 through 19, excluding "Overall")rob_columns <-names(data1)[2:18] # All columns except Study and Overall# Create a subset with just the RoB datarob_subset <- data1[1:4, c("Study", rob_columns)]# Prepare data for plottingrob_long <- rob_subset %>%pivot_longer(cols =-Study, names_to ="Domain", values_to ="Risk") %>%mutate(Risk_category =factor(Risk, levels =c("Low", "Unclear", "High")),Symbol =case_when( Risk =="Low"~"+", # Green plus Risk =="Unclear"~"—", # Orange minus Risk =="High"~"X", # Red XTRUE~"?"# Question mark for missing ),# Preserve original column orderDomain =factor(Domain, levels = rob_columns) )# Create color mapping for risk levelsrisk_colors <-c("Low"="#02C100", # Green"Unclear"="#FFA500", # Orange"High"="#BF0000") # Red# Create the risk of bias plot with symbolsrob_plot <-ggplot(rob_long, aes(x = Domain, y = Study)) +geom_tile(aes(fill = Risk_category), color ="white", size =1) +geom_text(aes(label = Symbol), size =5, fontface ="bold") +scale_fill_manual(values = risk_colors, na.value ="gray90",name ="Risk of Bias",labels =c("Low (+)", "Unclear (-)", "High (X)")) +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1, size =14),axis.text.y =element_text(size =14),axis.title =element_blank(),legend.position ="bottom",legend.text =element_text(size =12),legend.title =element_text(size =14, face ="bold"),panel.grid =element_blank(),plot.title =element_text(hjust =0.5, size =18, face ="bold")) +labs(title ="Risk of Bias Summary")# Display the plotprint(rob_plot)
Code
# Save the plot in multiple formatsggsave(file.path(output_dir, "Figure 4. ROB Plot.svg"),plot = rob_plot,width =14, height =5, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 4. ROB Plot.tiff"),plot = rob_plot,width =14, height =5, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 4. ROB Plot.png"),plot = rob_plot,width =14, height =5, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 4. ROB Plot.jpeg"),plot = rob_plot,width =14, height =5, units ="in", dpi =300, quality =100)cat("Risk of bias plots saved to:", output_dir, "\n")
Risk of bias plots saved to: C:/Users/sjmor/OneDrive - The University of Sydney (Staff)/PhD Studies/Project 1. SysRev/New Review/Base/Figures
Code
# Optional: Create a summary bar chart showing proportion of each risk levelrob_summary <- rob_long %>%filter(!is.na(Risk)) %>%group_by(Domain, Risk_category) %>%summarise(count =n(), .groups ="drop") %>%group_by(Domain) %>%mutate(proportion = count /sum(count) *100)rob_summary_plot <-ggplot(rob_summary, aes(x = Domain, y = proportion, fill = Risk_category)) +geom_bar(stat ="identity", color ="white") +scale_fill_manual(values = risk_colors,name ="Risk of Bias",labels =c("Low (+)", "Unclear (-)", "High (X)")) +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1, size =14),axis.text.y =element_text(size =14),axis.title.x =element_blank(),axis.title.y =element_text(size =14),legend.position ="bottom",legend.text =element_text(size =12),legend.title =element_text(size =14, face ="bold"),plot.title =element_text(hjust =0.5, size =18, face ="bold")) +labs(title ="Risk of Bias Summary - Proportion by Domain",y ="Percentage (%)") +coord_flip()# Display summary plotprint(rob_summary_plot)
Code
# Save summary plot in multiple formatsggsave(file.path(output_dir, "Figure 5. ROB Summary.svg"),plot = rob_summary_plot,width =10, height =8, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 5. ROB Summary.tiff"),plot = rob_summary_plot,width =10, height =8, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 5. ROB Summary.png"),plot = rob_summary_plot,width =10, height =8, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 5. ROB Summary.jpeg"),plot = rob_summary_plot,width =10, height =8, units ="in", dpi =300, quality =100)cat("Risk of bias summary plots saved to:", output_dir, "\n")
Risk of bias summary plots saved to: C:/Users/sjmor/OneDrive - The University of Sydney (Staff)/PhD Studies/Project 1. SysRev/New Review/Base/Figures
Source Code
---title: "Respiratory Muslce Performance in Wind Instrumentalists: Outputs from a Systematic Review and Meta-Analysis"author: "Sarah Morris"date: "2025-27-09"format: html: toc: true toc-depth: 2 toc-title: "Table of Contents" toc-location: right number-sections: true theme: cosmo code-fold: true code-tools: true highlight-style: githubexecute: echo: true warning: false error: false---# Meta-Analyses```{r}#### 1. Load packages ----library(svglite)library(metafor)library(meta)library(metaforest)library(dplyr)library(tidyverse)library(readxl)library(grid)library(gridExtra)#### 2. Load the data ----data1 <-read.csv("data/WI_MA_data_30.05.25.csv")#### 3. Process the data ----# Calculate effect sizes using escalc() for all datadata1 <-escalc(measure="MD", m1i=mn_exp, sd1i=std_exp, n1i=n_exp, m2i=mn_ctl, sd2i=std_ctl, n2i=n_ctl, data=data1, slab=paste(author, year, sep=", "))# Split data into MIP and MEP datasetsdata_MIP <- data1 %>%filter(RMP =="MIP")data_MEP <- data1 %>%filter(RMP =="MEP")#### 4. Meta-analysis for MIP ----# Random-effects model with Hartung-Knapp adjustmentMIP_rma <-rma(yi, vi, data = data_MIP, method ="REML", test ="knha")# Fixed-effect modelMIP_fe <-rma(yi, vi, data = data_MIP, method ="FE")# Sort data by effect size (MD) from largest to smallestdata_MIP <- data_MIP[order(-data_MIP$yi), ] # Create meta object for forest plotMIP_meta <-metacont(n.e = data_MIP$n_exp, mean.e = data_MIP$mn_exp, sd.e = data_MIP$std_exp, n.c = data_MIP$n_ctl, mean.c = data_MIP$mn_ctl, sd.c = data_MIP$std_ctl, data = data_MIP, studlab = data_MIP$author,common =TRUE, random =FALSE,method.random.ci =FALSE, # Hartung-Knapp adjustmentprediction =FALSE, # Include prediction intervalsm ="MD",title ="Maximum Inspiratory Pressure (MIP) generation in wind instrumentalists vs. controls")#### 5. Meta-analysis for MEP ----# Random-effects model with Hartung-Knapp adjustmentMEP_rma <-rma(yi, vi, data = data_MEP, method ="REML", test ="knha")# Fixed-effect modelMEP_fe <-rma(yi, vi, data = data_MEP, method ="FE")# Sort data by effect size (MD) from largest to smallestdata_MEP <- data_MEP[order(-data_MEP$yi), ] # Create meta object for forest plotMEP_meta <-metacont(n.e = data_MEP$n_exp, mean.e = data_MEP$mn_exp, sd.e = data_MEP$std_exp, n.c = data_MEP$n_ctl, mean.c = data_MEP$mn_ctl, sd.c = data_MEP$std_ctl, data = data_MEP, studlab = data_MEP$author,common =TRUE, random =FALSE,method.random.ci =FALSE, # Hartung-Knapp adjustmentprediction =FALSE, # Include prediction intervalsm ="MD",title ="Maximum Expiratory Pressure (MEP) generation in wind instrumentalists vs. controls")```## MIP MA Results```{r}#### 6. Create Forest Plots ----#### MIP Forest Plot ----# Define output directoryoutput_dir <-"C:/Users/sjmor/OneDrive - The University of Sydney (Staff)/PhD Studies/Project 1. SysRev/New Review/Base/Figures"# Create directory if it doesn't existif (!dir.exists(output_dir)) {dir.create(output_dir, recursive =TRUE)}# Function to create the MIP forest plotcreate_mip_plot <-function() {par(mar =c(2, 4, 1, 2))forest(MIP_meta,leftcols =c("studlab", "year", "n.e", "mean.e", "sd.e", "n.c", "mean.c", "sd.c"),leftlabs =c("Author", "Year", "n", "Mean", "SD", "n", "Mean", "SD"),rightcols =c("effect", "ci"),rightlabs =c("MD", "95% CI"),digits =1,digits.mean =1,digits.sd =1,comb.fixed =TRUE,comb.random =FALSE,prediction =FALSE,print.tau2 =TRUE,print.I2 =TRUE,print.H =TRUE,col.predict ="red",col.diamond ="blue",hetstat =TRUE,overall =TRUE,overall.hetstat =TRUE,test.overall.common =TRUE,test.overall.random =FALSE,main ="Maximum Inspiratory Pressure (MIP) generation in wind instrumentalists vs. controls",fontsize =8,cex =0.8,xlim =c(-30, 60),header.height =0.5)}# Save as PNGpng(file.path(output_dir, "Figure 2. MIP forest plot.png"), width =8, height =6, units ="in", res =300)create_mip_plot()dev.off()# Save as JPEGjpeg(file.path(output_dir, "Figure 2. MIP forest plot.jpeg"), width =8, height =6, units ="in", res =300)create_mip_plot()dev.off()# Save as TIFFtiff(file.path(output_dir, "Figure 2. MIP forest plot.tiff"), width =8, height =6, units ="in", res =300)create_mip_plot()dev.off()# Save as PDFpdf(file.path(output_dir, "Figure 2. MIP forest plot.pdf"), width =8, height =6)create_mip_plot()dev.off()# Save as SVGsvg(file.path(output_dir, "Figure 2. MIP forest plot.svg"), width =8, height =6)create_mip_plot()dev.off()# Display in R Markdownknitr::include_graphics(file.path(output_dir, "Figure 2. MIP forest plot.png"), dpi =300)#### 7. Summary Statistics ----# Print AnalysesMIP_meta# Display summary statistics for MIPcat("\nSummary for Maximum Inspiratory Pressure (MIP):\n")cat("Random-effects model (with Hartung-Knapp adjustment):\n")print(MIP_rma)cat("\nFixed-effect model:\n")print(MIP_fe)cat("\nHeterogeneity statistics:\n")cat("I² =", formatC(MIP_rma$I2, digits=1, format="f"), "%\n")cat("H² =", formatC(MIP_rma$H2, digits=2, format="f"), "\n")cat("τ² =", formatC(MIP_rma$tau2, digits=4, format="f"), "\n\n")```## MEP MA Results```{r}#### 6. Create Forest Plots ----# MEP Forest Plot# Function to create the MEP forest plotcreate_mep_plot <-function() {par(mar =c(2, 4, 3, 2))forest(MEP_meta,leftcols =c("studlab", "year", "n.e", "mean.e", "sd.e", "n.c", "mean.c", "sd.c"),leftlabs =c("Author", "Year", "n", "Mean", "SD", "n", "Mean", "SD"),rightcols =c("effect", "ci"),rightlabs =c("MD", "95% CI"),digits =1,digits.mean =1,digits.sd =1,comb.fixed =TRUE,comb.random =FALSE,prediction =FALSE,print.tau2 =TRUE,print.I2 =TRUE,print.H =TRUE,col.predict ="red",col.diamond ="blue",hetstat =TRUE,overall =TRUE,overall.hetstat =TRUE,test.overall.common =TRUE,test.overall.random =FALSE,main ="Maximum Expiratory Pressure (MEP) generation in wind instrumentalists vs. controls",fontsize =7,cex =0.7,xlim =c(-20, 90),header.height =0.5)}# Save as PNGpng(file.path(output_dir, "Figure 3. MEP forest plot.png"), width =8, height =6, units ="in", res =300)create_mep_plot()dev.off()# Save as JPEGjpeg(file.path(output_dir, "Figure 3. MEP forest plot.jpeg"), width =8, height =6, units ="in", res =300)create_mep_plot()dev.off()# Save as TIFFtiff(file.path(output_dir, "Figure 3. MEP forest plot.tiff"), width =8, height =6, units ="in", res =300)create_mep_plot()dev.off()# Save as PDFpdf(file.path(output_dir, "Figure 3. MEP forest plot.pdf"), width =8, height =6)create_mep_plot()dev.off()# Save as SVGsvg(file.path(output_dir, "Figure 3. MEP forest plot.svg"), width =8, height =6)create_mep_plot()dev.off()# Display in R Markdownknitr::include_graphics(file.path(output_dir, "Figure 3. MEP forest plot.png"), dpi =300)#### 7. Summary Statistics ----# Print AnalysesMEP_meta# Display summary statistics for MEPcat("\nSummary for Maximum Expiratory Pressure (MEP):\n")cat("Random-effects model (with Hartung-Knapp adjustment):\n")print(MEP_rma)cat("\nFixed-effect model:\n")print(MEP_meta)cat("\nHeterogeneity statistics:\n")cat("I² =", formatC(MEP_rma$I2, digits=1, format="f"), "%\n")cat("H² =", formatC(MEP_rma$H2, digits=2, format="f"), "\n")cat("τ² =", formatC(MEP_rma$tau2, digits=4, format="f"), "\n")```# Risk of Bias```{r}# Risk of Bias Visualization with Symbols ----library(ggplot2)library(tidyr)library(dplyr)# Read the CSV filedata1 <-read.csv("C:/Users/sjmor/OneDrive - The University of Sydney (Staff)/PhD Studies/Project 1. SysRev/New Review/Base/Figures/ROB_data.csv", header =TRUE, check.names =FALSE)# Define output directoryoutput_dir <-"C:/Users/sjmor/OneDrive - The University of Sydney (Staff)/PhD Studies/Project 1. SysRev/New Review/Base/Figures"# Get all RoB columns (columns 2 through 19, excluding "Overall")rob_columns <-names(data1)[2:18] # All columns except Study and Overall# Create a subset with just the RoB datarob_subset <- data1[1:4, c("Study", rob_columns)]# Prepare data for plottingrob_long <- rob_subset %>%pivot_longer(cols =-Study, names_to ="Domain", values_to ="Risk") %>%mutate(Risk_category =factor(Risk, levels =c("Low", "Unclear", "High")),Symbol =case_when( Risk =="Low"~"+", # Green plus Risk =="Unclear"~"—", # Orange minus Risk =="High"~"X", # Red XTRUE~"?"# Question mark for missing ),# Preserve original column orderDomain =factor(Domain, levels = rob_columns) )# Create color mapping for risk levelsrisk_colors <-c("Low"="#02C100", # Green"Unclear"="#FFA500", # Orange"High"="#BF0000") # Red# Create the risk of bias plot with symbolsrob_plot <-ggplot(rob_long, aes(x = Domain, y = Study)) +geom_tile(aes(fill = Risk_category), color ="white", size =1) +geom_text(aes(label = Symbol), size =5, fontface ="bold") +scale_fill_manual(values = risk_colors, na.value ="gray90",name ="Risk of Bias",labels =c("Low (+)", "Unclear (-)", "High (X)")) +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1, size =14),axis.text.y =element_text(size =14),axis.title =element_blank(),legend.position ="bottom",legend.text =element_text(size =12),legend.title =element_text(size =14, face ="bold"),panel.grid =element_blank(),plot.title =element_text(hjust =0.5, size =18, face ="bold")) +labs(title ="Risk of Bias Summary")# Display the plotprint(rob_plot)# Save the plot in multiple formatsggsave(file.path(output_dir, "Figure 4. ROB Plot.svg"),plot = rob_plot,width =14, height =5, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 4. ROB Plot.tiff"),plot = rob_plot,width =14, height =5, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 4. ROB Plot.png"),plot = rob_plot,width =14, height =5, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 4. ROB Plot.jpeg"),plot = rob_plot,width =14, height =5, units ="in", dpi =300, quality =100)cat("Risk of bias plots saved to:", output_dir, "\n")# Optional: Create a summary bar chart showing proportion of each risk levelrob_summary <- rob_long %>%filter(!is.na(Risk)) %>%group_by(Domain, Risk_category) %>%summarise(count =n(), .groups ="drop") %>%group_by(Domain) %>%mutate(proportion = count /sum(count) *100)rob_summary_plot <-ggplot(rob_summary, aes(x = Domain, y = proportion, fill = Risk_category)) +geom_bar(stat ="identity", color ="white") +scale_fill_manual(values = risk_colors,name ="Risk of Bias",labels =c("Low (+)", "Unclear (-)", "High (X)")) +theme_minimal() +theme(axis.text.x =element_text(angle =45, hjust =1, size =14),axis.text.y =element_text(size =14),axis.title.x =element_blank(),axis.title.y =element_text(size =14),legend.position ="bottom",legend.text =element_text(size =12),legend.title =element_text(size =14, face ="bold"),plot.title =element_text(hjust =0.5, size =18, face ="bold")) +labs(title ="Risk of Bias Summary - Proportion by Domain",y ="Percentage (%)") +coord_flip()# Display summary plotprint(rob_summary_plot)# Save summary plot in multiple formatsggsave(file.path(output_dir, "Figure 5. ROB Summary.svg"),plot = rob_summary_plot,width =10, height =8, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 5. ROB Summary.tiff"),plot = rob_summary_plot,width =10, height =8, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 5. ROB Summary.png"),plot = rob_summary_plot,width =10, height =8, units ="in", dpi =300)ggsave(file.path(output_dir, "Figure 5. ROB Summary.jpeg"),plot = rob_summary_plot,width =10, height =8, units ="in", dpi =300, quality =100)cat("Risk of bias summary plots saved to:", output_dir, "\n")```