Comparison & Meta-Analysis

Overview

Phases 2-3 compare two extracted datasets by computing stratified prevalence ratios and synthesizing them via multi-level random-effects meta-analysis.

The pipeline has 4 steps, each producing a more aggregated table:

yearly               concept x year x sex x age_group    (raw PR per stratum)
  |
  v  meta across years
meta_agegroups       concept x sex x age_group           (pooled across years)
  |
  v  meta across age groups
meta_by_sex          concept x sex                       (pooled across age groups)
  |
  v  meta across sexes
meta_summary         concept (sex = "Both")              (final summary)

Running a comparison

All domains

library(syrona)

# Both datasets must already be extracted (in data/sources/)
compare_all("Hospital_A", "Hospital_B")

This loads both datasets, runs the 4-step pipeline for each domain present in both, and saves results to data/comparisons/Hospital_A_vs_Hospital_B/.

Single domain

compare_all("Hospital_A", "Hospital_B", domains = "conditions")

Step-by-step usage

You can also run each step individually for more control:

d1 <- load_dataset("Hospital_A")
d2 <- load_dataset("Hospital_B")

# Step 1: Yearly comparison (inner join on concept x year x sex x age_group)
yearly <- compare_yearly(d1, d2, prev_table = "condition_prevalence")

# Step 2: Meta across years
meta_ag <- compare_meta_agegroups(yearly)

# Step 3: Meta across age groups
meta_sex <- compare_meta_by_sex(meta_ag)

# Step 4: Meta across sexes
meta_sum <- compare_meta_summary(meta_sex)

Prevalence ratio formula

For each matched stratum, Syrona computes:

Log2 prevalence ratio:

log2_pr = log2(p2 / p1)

Where p1 and p2 are the prevalence values in dataset 1 (reference) and dataset 2 (comparison).

Standard error (on log2 scale):

SE = sqrt((1 - p1) / (p1 * n1) + (1 - p2) / (p2 * n2)) / ln(2)

Where n1 and n2 are the denominators (persons observed in that stratum).

95% confidence interval:

CI = log2_pr +/- 1.96 * SE

Significance: A stratum is significant if the CI excludes zero on the log2 scale (i.e., the fold difference excludes 1.0).

Output columns

Each comparison table contains these columns:

Column Description
concept_id OMOP concept ID
log2_pr Log2 prevalence ratio
se Standard error (log2 scale)
ci_low, ci_high 95% CI bounds (log2 scale)
fold_diff Natural-scale fold difference (2^log2_pr)
fold_ci_low, fold_ci_high Natural-scale CI bounds
fold_symmetric max(fold, 1/fold) - useful for ranking
p_value Two-sided p-value
sig TRUE if CI excludes zero
meta_model_type “Random-Effects”, “Fixed-Effect”, or “Pass-Through”
n_strata Number of strata pooled
tau2 Between-study variance (heterogeneity)
I2 Percentage of variance due to heterogeneity
Q Cochran’s Q statistic
pval_Q P-value for Q test

Meta-analysis strategy

For each group of strata to be pooled, Syrona uses this fallback strategy:

  1. Single stratum - Pass-Through (returns the estimate as-is, no pooling)
  2. Multiple strata - Random-effects meta-analysis with Paule-Mandel tau estimator (meta::metagen)
  3. If RE fails - Fixed-effect (common-effect) model
  4. If both fail - Group is skipped

The Paule-Mandel estimator is preferred because it handles small numbers of studies better than DerSimonian-Laird.

Heterogeneity metrics

The meta-analysis cascade produces heterogeneity metrics at each aggregation level:

These metrics help identify concepts where the summary prevalence ratio masks meaningful variation across strata.

Loading and listing comparisons

# List available comparisons
list_comparisons()
#> [1] "Hospital_A_vs_Hospital_B"

# Load a comparison
comp <- load_comparison("Hospital_A", "Hospital_B")
names(comp)
#> [1] "condition_yearly" "condition_meta_agegroups"
#> [3] "condition_meta_by_sex" "condition_meta_summary"

mirror server hosted at Truenetwork, Russian Federation.