risk-of-bias

star 1

Risk of bias and quality assessment for systematic reviews

matheus-rech By matheus-rech schedule Updated 12/29/2025

name: risk-of-bias version: 1.0.0 description: Risk of bias and quality assessment for systematic reviews author: NeuroResearch Agent license: MIT

triggers:

  • pattern: "risk of bias"
  • pattern: "RoB"
  • pattern: "quality assessment"
  • pattern: "Newcastle.Ottawa"
  • pattern: "ROBINS"
  • pattern: "bias assessment"

requires:

  • r-execute
  • filesystem

tools:

  • name: assess_rob2 description: RoB 2 assessment for RCTs
  • name: assess_nos description: Newcastle-Ottawa Scale for cohort/case-control
  • name: assess_robins_i description: ROBINS-I for non-randomized interventions
  • name: generate_rob_plots description: Generate traffic light and summary plots

Risk of Bias Assessment Skill

Tool Selection Guide

Study Design Tool Score Range
RCT RoB 2 Low / Some concerns / High
Cohort Newcastle-Ottawa Scale 0-9 stars
Case-control Newcastle-Ottawa Scale 0-9 stars
Non-randomized intervention ROBINS-I Low to Critical
Diagnostic accuracy QUADAS-2 Low / High / Unclear
Case series JBI Checklist Yes / No / Unclear

RoB 2 (Cochrane Risk of Bias 2.0)

For Randomized Controlled Trials

rob2_assessment:
  study_id: Smith_2020
  
  domain1_randomization:
    question_1_1: "Was the allocation sequence random?"
    response_1_1: "Yes"  # Y, PY, PN, N, NI
    question_1_2: "Was the allocation sequence concealed?"
    response_1_2: "Probably yes"
    question_1_3: "Baseline differences suggest problem with randomization?"
    response_1_3: "No"
    judgment: "Low risk"  # Low, Some concerns, High
    support: "Computer-generated sequence, sealed envelopes"
    
  domain2_deviations:
    question_2_1: "Were participants aware of assignment?"
    response_2_1: "Yes"  # Surgical trial, blinding not possible
    question_2_2: "Were carers aware of assignment?"
    response_2_2: "Yes"
    question_2_3: "Deviations from intended intervention?"
    response_2_3: "No"
    question_2_4: "Analysis appropriate for deviations?"
    response_2_4: "Not applicable"
    judgment: "Some concerns"
    support: "Open-label due to nature of intervention"
    
  domain3_missing_data:
    question_3_1: "Outcome data available for all participants?"
    response_3_1: "Probably yes"
    question_3_2: "Evidence outcome not biased by missing data?"
    response_3_2: "Yes"
    question_3_3: "Missingness depend on true value?"
    response_3_3: "Probably no"
    judgment: "Low risk"
    support: "95% follow-up, missing equally distributed"
    
  domain4_measurement:
    question_4_1: "Was outcome measurement appropriate?"
    response_4_1: "Yes"
    question_4_2: "Did measurement differ between groups?"
    response_4_2: "No"
    question_4_3: "Were assessors aware of assignment?"
    response_4_3: "No"
    question_4_4: "Could assessment be influenced by knowledge?"
    response_4_4: "No"
    judgment: "Low risk"
    support: "mRS assessed by blinded neurologist"
    
  domain5_selection:
    question_5_1: "Were data selected from multiple analyses?"
    response_5_1: "No"
    question_5_2: "Were multiple measurements available?"
    response_5_2: "No"
    question_5_3: "Could selection have been influenced by results?"
    response_5_3: "No"
    judgment: "Low risk"
    support: "Pre-registered primary outcome at 6 months"
    
  overall:
    judgment: "Some concerns"
    direction: "Favors experimental"  # or control, unpredictable
    rationale: "Open-label design unavoidable for surgical intervention"

RoB 2 Data Format for robvis

Study,D1,D2,D3,D4,D5,Overall
Smith 2020,Low,Some concerns,Low,Low,Low,Some concerns
Jones 2021,Low,Low,Low,Low,Low,Low
Brown 2022,High,Some concerns,Low,High,Low,High
Lee 2023,Low,Low,Low,Low,Some concerns,Some concerns

Newcastle-Ottawa Scale

For Cohort Studies

nos_cohort:
  study_id: Martinez_2022
  
  selection:  # Max 4 stars
    representativeness:
      question: "Representativeness of exposed cohort"
      options:
        a: "Truly representative (★)"
        b: "Somewhat representative (★)"
        c: "Selected group"
        d: "No description"
      selected: "a"
      stars: 1
      
    selection_non_exposed:
      question: "Selection of non-exposed cohort"
      options:
        a: "From same community (★)"
        b: "From different source"
        c: "No description"
      selected: "a"
      stars: 1
      
    ascertainment_exposure:
      question: "Ascertainment of exposure"
      options:
        a: "Secure record (★)"
        b: "Structured interview (★)"
        c: "Written self-report"
        d: "No description"
      selected: "a"
      stars: 1
      
    outcome_not_present:
      question: "Outcome not present at start"
      options:
        a: "Yes (★)"
        b: "No"
      selected: "a"
      stars: 1
      
  comparability:  # Max 2 stars
    main_factor:
      question: "Comparability based on design/analysis"
      control_for: "Age"
      stars: 1
      
    additional_factor:
      question: "Additional factor controlled"
      control_for: "Baseline severity (NIHSS)"
      stars: 1
      
  outcome:  # Max 3 stars
    assessment:
      question: "Assessment of outcome"
      options:
        a: "Independent blind assessment (★)"
        b: "Record linkage (★)"
        c: "Self-report"
        d: "No description"
      selected: "a"
      stars: 1
      
    follow_up_length:
      question: "Follow-up long enough for outcome"
      threshold: "≥6 months"
      adequate: true
      stars: 1
      
    follow_up_adequacy:
      question: "Adequacy of follow-up"
      options:
        a: "Complete follow-up (★)"
        b: ">80% with description of lost (★)"
        c: "Follow-up <80%"
        d: "No statement"
      selected: "b"
      stars: 1
      
  total_stars: 9
  quality_rating: "Good"  # Good: 7-9, Fair: 4-6, Poor: 0-3

NOS Data Format

Study,S1,S2,S3,S4,C1,C2,O1,O2,O3,Total,Quality
Martinez 2022,1,1,1,1,1,1,1,1,1,9,Good
Kim 2021,1,1,1,1,1,0,1,1,0,7,Good
Wilson 2020,1,0,1,1,1,1,0,1,1,7,Good
Chen 2019,0,1,1,0,1,0,1,1,1,6,Fair

ROBINS-I

For Non-Randomized Studies of Interventions

robins_i:
  study_id: Thompson_2023
  
  pre_intervention:
    domain1_confounding:
      question: "Bias due to confounding"
      considerations:
        - "Was there potential for confounding?"
        - "Did authors use appropriate analysis?"
        - "Were confounders measured and adjusted?"
      judgment: "Moderate"
      support: "Propensity score matching used, but unmeasured confounders possible"
      
    domain2_selection:
      question: "Bias in selection of participants"
      considerations:
        - "Selection related to intervention AND outcome?"
        - "Start of follow-up coincides with intervention start?"
      judgment: "Low"
      support: "All consecutive patients included from defined start date"
      
  at_intervention:
    domain3_classification:
      question: "Bias in classification of interventions"
      considerations:
        - "Intervention status well defined?"
        - "Classification affected by outcome knowledge?"
      judgment: "Low"
      support: "Surgical vs. medical clearly distinguished"
      
  post_intervention:
    domain4_deviations:
      question: "Bias due to deviations from intended interventions"
      considerations:
        - "Were there deviations from intended intervention?"
        - "Were deviations balanced between groups?"
      judgment: "Low"
      support: "Protocol-driven care in both groups"
      
    domain5_missing_data:
      question: "Bias due to missing data"
      considerations:
        - "Were outcome data reasonably complete?"
        - "Were participants excluded due to missing data?"
      judgment: "Low"
      support: "Complete outcome data for 94% of participants"
      
    domain6_measurement:
      question: "Bias in measurement of outcomes"
      considerations:
        - "Could measurement differ between groups?"
        - "Were assessors aware of intervention?"
      judgment: "Moderate"
      support: "Outcome assessors not formally blinded"
      
    domain7_selection_reporting:
      question: "Bias in selection of reported result"
      considerations:
        - "Multiple analyses possible?"
        - "Result selected from multiple measurements?"
      judgment: "Low"
      support: "Pre-specified primary outcome reported"
      
  overall:
    judgment: "Moderate"
    rationale: "Some concerns about confounding and outcome measurement"

ROBINS-I Data Format

Study,D1,D2,D3,D4,D5,D6,D7,Overall
Thompson 2023,Moderate,Low,Low,Low,Low,Moderate,Low,Moderate
Garcia 2022,Serious,Low,Low,Low,Moderate,Moderate,Low,Serious
Ahmed 2021,Low,Low,Low,Low,Low,Low,Low,Low

R Code for Visualization

Generate All Plots

library(robvis)
library(ggplot2)

# ============================================
# ROB 2 VISUALIZATION
# ============================================

rob2_data <- read.csv("quality_assessment/rob2_data.csv")

# Traffic light plot
rob2_traffic <- rob_traffic_light(
  data = rob2_data,
  tool = "ROB2",
  colour = "cochrane",
  psize = 10
)

ggsave("figures/rob2_traffic_light.png", rob2_traffic, 
       width = 12, height = nrow(rob2_data) * 0.5 + 2, dpi = 300)

# Summary plot
rob2_summary <- rob_summary(
  data = rob2_data,
  tool = "ROB2",
  overall = TRUE,
  colour = "cochrane"
)

ggsave("figures/rob2_summary.png", rob2_summary, 
       width = 10, height = 6, dpi = 300)

# ============================================
# ROBINS-I VISUALIZATION
# ============================================

robins_data <- read.csv("quality_assessment/robins_data.csv")

robins_traffic <- rob_traffic_light(
  data = robins_data,
  tool = "ROBINS-I",
  colour = "cochrane"
)

ggsave("figures/robins_traffic_light.png", robins_traffic,
       width = 14, height = nrow(robins_data) * 0.5 + 2, dpi = 300)

# ============================================
# NEWCASTLE-OTTAWA VISUALIZATION
# ============================================

nos_data <- read.csv("quality_assessment/nos_data.csv")

# Custom bar chart
nos_plot <- ggplot(nos_data, aes(x = reorder(Study, Total), y = Total)) +
  geom_col(aes(fill = Quality), width = 0.7) +
  geom_hline(yintercept = c(4, 7), linetype = "dashed", alpha = 0.5) +
  geom_text(aes(label = Total), hjust = -0.3, size = 4) +
  coord_flip() +
  scale_fill_manual(
    values = c("Good" = "#4CAF50", "Fair" = "#FFC107", "Poor" = "#F44336"),
    name = "Quality"
  ) +
  scale_y_continuous(limits = c(0, 10), breaks = 0:9) +
  labs(
    x = "",
    y = "Newcastle-Ottawa Scale Score",
    title = "Risk of Bias: Newcastle-Ottawa Scale",
    subtitle = "Good: 7-9 stars | Fair: 4-6 stars | Poor: 0-3 stars"
  ) +
  theme_minimal() +
  theme(
    legend.position = "bottom",
    panel.grid.major.y = element_blank(),
    axis.text.y = element_text(size = 11)
  )

ggsave("figures/nos_scores.png", nos_plot, 
       width = 10, height = max(6, nrow(nos_data) * 0.4), dpi = 300)

# Stacked bar by domain
nos_long <- nos_data %>%
  tidyr::pivot_longer(
    cols = c(S1, S2, S3, S4, C1, C2, O1, O2, O3),
    names_to = "Domain",
    values_to = "Stars"
  ) %>%
  mutate(
    Category = case_when(
      Domain %in% c("S1", "S2", "S3", "S4") ~ "Selection",
      Domain %in% c("C1", "C2") ~ "Comparability",
      TRUE ~ "Outcome"
    )
  )

nos_stacked <- ggplot(nos_long, aes(x = Study, y = Stars, fill = Category)) +
  geom_col() +
  coord_flip() +
  scale_fill_brewer(palette = "Set2") +
  labs(x = "", y = "Stars", title = "NOS by Domain") +
  theme_minimal()

ggsave("figures/nos_domains.png", nos_stacked, 
       width = 10, height = 6, dpi = 300)

Weighted Analysis by Quality

# Weight meta-analysis by study quality
library(meta)

# Add quality weights
data <- merge(pooled_data, nos_data[, c("Study", "Total")], 
              by.x = "study", by.y = "Study")

# Quality-adjusted analysis
ma_weighted <- metabin(
  event.e = events_int,
  n.e = n_int,
  event.c = events_ctrl,
  n.c = n_ctrl,
  studlab = study,
  data = data,
  sm = "OR",
  random = TRUE,
  # Use quality score as additional weight
  byvar = ifelse(data$Total >= 7, "High quality", "Lower quality")
)

# Sensitivity: exclude low-quality studies
ma_high_quality <- metabin(
  event.e = events_int,
  n.e = n_int,
  event.c = events_ctrl,
  n.c = n_ctrl,
  studlab = study,
  data = subset(data, Total >= 7),
  sm = "OR",
  random = TRUE
)
Install via CLI
npx skills add https://github.com/matheus-rech/meta-agent --skill risk-of-bias
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
matheus-rech
matheus-rech Explore all skills →