Effect size computations: phelan2014evidencestakesmatter

Computes standardized mean differences (d) and sampling variances (v) for the extraction YAML papers/phelan2014evidencestakesmatter/phelan2014evidencestakesmatter.yaml.

Inputs and methods

paper_key <- "phelan2014evidencestakesmatter"

sign_convention <- "d = mean(low) - mean(high)"

effects <- list(
  # Study 1: First experiment (Passerby; UNC students)
  list(
    study_id = 1,
    effect_id = "s1_e1",
    method_used = "between_reported_d_t_df",
    mean_low = 5.11,
    mean_high = 4.93,
    t_value = 0.435,
    df = 55,
    reported_d = 0.12,
    notes_on_assumptions = "Non-juxtaposed Important vs Unimportant; split Ns not reported; v computed from reported d + t(df)."
  ),
  list(
    study_id = 1,
    effect_id = "s1_e2",
    method_used = "within_smcrp_t",
    mean_low = 5.32,
    mean_high = 4.50,
    sd_low = 1.27,
    sd_high = 1.39,
    t_value = 4.499,
    df = 49,
    notes_on_assumptions = "Two Cases (paired); recover within-person r from paired t(df) + means/SDs."
  ),

  # Study 2: Second experiment (Single drunk; mTurk)
  list(
    study_id = 2,
    effect_id = "s2_e1",
    method_used = "between_reported_d_t_df",
    mean_low = 4.29,
    mean_high = 4.08,
    t_value = 0.63,
    df = 72,
    reported_d = 0.15,
    notes_on_assumptions = "Non-juxtaposed Important vs Unimportant; split Ns not reported; v computed from reported d + t(df)."
  ),
  list(
    study_id = 2,
    effect_id = "s2_e2",
    method_used = "within_smcrp_t",
    mean_low = 4.14,
    mean_high = 3.64,
    sd_low = 1.55,
    sd_high = 1.79,
    t_value = 2.24,
    df = 35,
    notes_on_assumptions = "Two Cases (paired); recover within-person r from paired t(df) + means/SDs."
  ),

  # Study 3: Third experiment (Street signs; UNC students)
  list(
    study_id = 3,
    effect_id = "s3_e1",
    method_used = "between_reported_d_t_df",
    mean_low = 5.61,
    mean_high = 5.79,
    t_value = 0.675,
    df = 73,
    reported_d = 0.15,
    notes_on_assumptions = "Non-juxtaposed Important vs Unimportant; sign set by means; split Ns not reported; v computed from reported d + t(df)."
  ),
  list(
    study_id = 3,
    effect_id = "s3_e2",
    method_used = "within_smcrp_t",
    mean_low = 5.76,
    mean_high = 5.26,
    sd_low = 1.55,
    sd_high = 1.44,
    t_value = 2.92,
    df = 65,
    notes_on_assumptions = "Two Cases (paired); recover within-person r from paired t(df) + means/SDs."
  ),

  # Study 4: Superseded prior study (Two drunks; UNC students)
  list(
    study_id = 4,
    effect_id = "s4_e1",
    method_used = "between_reported_d_t_df",
    mean_low = 4.72,
    mean_high = 4.39,
    t_value = 0.622,
    df = 34,
    reported_d = 0.20,
    notes_on_assumptions = "Superseded two-drunks study; split Ns not reported; v computed from reported d + t(df)."
  ),
  list(
    study_id = 4,
    effect_id = "s4_e2",
    method_used = "within_smcrp_t",
    mean_low = 4.53,
    mean_high = 3.69,
    sd_low = 1.32,
    sd_high = 1.47,
    t_value = 4.14,
    df = 35,
    notes_on_assumptions = "Superseded two-drunks study; Two Cases (paired); recover within-person r from paired t(df) + means/SDs."
  )
)

Computation

default_or <- function(x, default) {
  if (is.null(x)) default else x
}

stop_if_missing <- function(x, name) {
  if (is.na(x)) stop(sprintf("Missing required input: %s", name), call. = FALSE)
}

# Exact small-sample correction factor used by metafor (.cmicalc).
hedges_correction <- function(df) {
  ifelse(df <= 1, NA_real_, exp(lgamma(df/2) - log(sqrt(df/2)) - lgamma((df - 1)/2)))
}

pooled_sd <- function(n_high, n_low, sd_high, sd_low) {
  sqrt(((n_high - 1) * sd_high^2 + (n_low - 1) * sd_low^2) / (n_high + n_low - 2))
}

d_from_groups_independent <- function(n_high, n_low, mean_high, mean_low, sd_high, sd_low) {
  s <- pooled_sd(n_high, n_low, sd_high, sd_low)
  (mean_low - mean_high) / s
}

d_from_t_independent <- function(t_value, n_high, n_low) {
  t_value * sqrt((n_high + n_low) / (n_high * n_low))
}

d_from_f_independent <- function(f_value, n_high, n_low) {
  # Assumes a 2-group contrast where F = t^2
  sqrt(f_value * (n_high + n_low) / (n_high * n_low))
}

d_from_chi2_independent <- function(chi2_value, n_high, n_low) {
  sqrt(chi2_value * (n_high + n_low) / (n_high * n_low))
}

var_d_independent <- function(d, n_high, n_low) {
  n <- n_high + n_low
  (n / (n_high * n_low)) + (d^2 / (2 * (n - 2)))
}

# Between-subjects: compute v without split group Ns using reported d + t(df).
var_d_between_from_d_t_df <- function(d, t_value, df) {
  (d / t_value)^2 + (d^2) / (2 * df)
}

# Within-subjects (SMCRP-style): standardize by pooled SD across occasions.
sd_pooled_within <- function(sd_low, sd_high) {
  sqrt((sd_low^2 + sd_high^2) / 2)
}

d_within_smcrp <- function(mean_low, mean_high, sd_low, sd_high) {
  (mean_low - mean_high) / sd_pooled_within(sd_low, sd_high)
}

# Recover within-person correlation r from paired t + means/SDs (when r is not reported).
r_from_paired_t <- function(mean_low, mean_high, sd_low, sd_high, t_value, n_total) {
  mean_diff <- mean_low - mean_high
  sd_diff <- abs(mean_diff) * sqrt(n_total) / abs(t_value)
  (sd_low^2 + sd_high^2 - sd_diff^2) / (2 * sd_low * sd_high)
}

var_d_within_smcrp <- function(d, r, n_total) {
  (2 * (1 - r) / n_total) + (d^2) * (1 + r^2) / (4 * n_total)
}

infer_sign <- function(mean_low, mean_high, sign_d) {
  if (!is.na(mean_low) && !is.na(mean_high) && mean_low != mean_high) {
    return(sign(mean_low - mean_high))
  }
  stop_if_missing(sign_d, "sign_d (cannot infer sign from means)")
  if (!is.element(sign_d, c(-1, 1))) stop("sign_d must be +1 or -1", call. = FALSE)
  sign_d
}

compute_effect <- function(effect_inputs) {
  study_id <- effect_inputs$study_id
  effect_id <- effect_inputs$effect_id
  method_used <- effect_inputs$method_used

  # Sign convention (matches extraction instructions).
  sign_convention_local <- sign_convention

  # Defaults (mirror the template).
  n_high <- default_or(effect_inputs$n_high, NA_integer_)
  n_low <- default_or(effect_inputs$n_low, NA_integer_)
  n_total <- default_or(effect_inputs$n_total, NA_integer_)
  mean_high <- default_or(effect_inputs$mean_high, NA_real_)
  mean_low <- default_or(effect_inputs$mean_low, NA_real_)
  sd_high <- default_or(effect_inputs$sd_high, NA_real_)
  sd_low <- default_or(effect_inputs$sd_low, NA_real_)
  t_value <- default_or(effect_inputs$t_value, NA_real_)
  f_value <- default_or(effect_inputs$f_value, NA_real_)
  chi2_value <- default_or(effect_inputs$chi2_value, NA_real_)
  df <- default_or(effect_inputs$df, NA_real_)
  reported_d <- default_or(effect_inputs$reported_d, NA_real_)
  r_within <- default_or(effect_inputs$r_within, NA_real_)
  sign_d <- default_or(effect_inputs$sign_d, NA_real_)
  notes_on_assumptions <- default_or(effect_inputs$notes_on_assumptions, "")
  imputed_flag <- default_or(effect_inputs$imputed_flag, FALSE)
  needs_sensitivity <- default_or(effect_inputs$needs_sensitivity, TRUE)

  # Compute d/v (and small-sample corrected g/v_g) based on method_used.
  d <- NA_real_
  v <- NA_real_
  g <- NA_real_
  v_g <- NA_real_
  computed_from_suggested <- NA_character_
  design_used <- if (startsWith(method_used, "between_")) "Between-Subjects" else if (startsWith(method_used, "within_")) "Within-Subjects" else NA_character_

  if (method_used == "between_groups") {
    computed_from_suggested <- "groups"
    stop_if_missing(n_high, "n_high")
    stop_if_missing(n_low, "n_low")
    stop_if_missing(mean_high, "mean_high")
    stop_if_missing(mean_low, "mean_low")
    stop_if_missing(sd_high, "sd_high")
    stop_if_missing(sd_low, "sd_low")
    d <- d_from_groups_independent(n_high, n_low, mean_high, mean_low, sd_high, sd_low)
    v <- var_d_independent(d, n_high, n_low)
    df_used <- n_high + n_low - 2
    J <- hedges_correction(df_used)
    g <- J * d
    v_g <- (J^2) * v
  } else if (method_used == "between_t") {
    computed_from_suggested <- "t_df"
    stop_if_missing(n_high, "n_high")
    stop_if_missing(n_low, "n_low")
    stop_if_missing(t_value, "t_value")
    sign_used <- infer_sign(mean_low, mean_high, sign_d)
    d <- sign_used * abs(d_from_t_independent(t_value, n_high, n_low))
    v <- var_d_independent(d, n_high, n_low)
    df_used <- n_high + n_low - 2
    J <- hedges_correction(df_used)
    g <- J * d
    v_g <- (J^2) * v
  } else if (method_used == "between_f") {
    computed_from_suggested <- "f_df"
    stop_if_missing(n_high, "n_high")
    stop_if_missing(n_low, "n_low")
    stop_if_missing(f_value, "f_value")
    sign_used <- infer_sign(mean_low, mean_high, sign_d)
    d <- sign_used * abs(d_from_f_independent(f_value, n_high, n_low))
    v <- var_d_independent(d, n_high, n_low)
    df_used <- n_high + n_low - 2
    J <- hedges_correction(df_used)
    g <- J * d
    v_g <- (J^2) * v
  } else if (method_used == "between_chi2") {
    computed_from_suggested <- "other"
    stop_if_missing(n_high, "n_high")
    stop_if_missing(n_low, "n_low")
    stop_if_missing(chi2_value, "chi2_value")
    sign_used <- infer_sign(mean_low, mean_high, sign_d)
    d <- sign_used * abs(d_from_chi2_independent(chi2_value, n_high, n_low))
    v <- var_d_independent(d, n_high, n_low)
    df_used <- n_high + n_low - 2
    J <- hedges_correction(df_used)
    g <- J * d
    v_g <- (J^2) * v
  } else if (method_used == "between_reported_d_t_df") {
    computed_from_suggested <- "reported_d"
    stop_if_missing(reported_d, "reported_d")
    stop_if_missing(t_value, "t_value")
    stop_if_missing(df, "df")
    sign_used <- infer_sign(mean_low, mean_high, sign_d)
    d <- sign_used * abs(reported_d)
    v <- var_d_between_from_d_t_df(d = d, t_value = abs(t_value), df = df)
    J <- hedges_correction(df)
    g <- J * d
    v_g <- (J^2) * v
  } else if (method_used == "within_smcrp_r") {
    computed_from_suggested <- "groups"
    stop_if_missing(n_total, "n_total")
    stop_if_missing(mean_high, "mean_high")
    stop_if_missing(mean_low, "mean_low")
    stop_if_missing(sd_high, "sd_high")
    stop_if_missing(sd_low, "sd_low")
    stop_if_missing(r_within, "r_within")
    if (abs(r_within) > 1) stop("r_within must be between -1 and 1", call. = FALSE)
    d <- d_within_smcrp(mean_low, mean_high, sd_low, sd_high)
    v <- var_d_within_smcrp(d = d, r = r_within, n_total = n_total)
    df_used <- 2 * (n_total - 1) / (1 + r_within^2)
    J <- hedges_correction(df_used)
    g <- J * d
    v_g <- (2 * (1 - r_within) / n_total) + (g^2) * (1 + r_within^2) / (4 * n_total)
  } else if (method_used == "within_smcrp_t") {
    computed_from_suggested <- "groups"
    stop_if_missing(mean_high, "mean_high")
    stop_if_missing(mean_low, "mean_low")
    stop_if_missing(sd_high, "sd_high")
    stop_if_missing(sd_low, "sd_low")
    stop_if_missing(t_value, "t_value")

    if (is.na(n_total)) {
      stop_if_missing(df, "df (or provide n_total)")
      n_total <- df + 1
    }
    if (!is.na(df) && df != (n_total - 1)) {
      warning("For a paired t-test, df should equal n_total - 1; check inputs.")
    }

    r_est <- r_from_paired_t(mean_low, mean_high, sd_low, sd_high, t_value, n_total)
    if (abs(r_est) > 1) stop(sprintf("Recovered r=%.4f outside [-1,1]; check inputs or provide r_within directly.", r_est), call. = FALSE)

    d <- d_within_smcrp(mean_low, mean_high, sd_low, sd_high)
    v <- var_d_within_smcrp(d = d, r = r_est, n_total = n_total)
    df_used <- 2 * (n_total - 1) / (1 + r_est^2)
    J <- hedges_correction(df_used)
    g <- J * d
    v_g <- (2 * (1 - r_est) / n_total) + (g^2) * (1 + r_est^2) / (4 * n_total)
  } else {
    stop(sprintf("Unknown method_used: %s", method_used), call. = FALSE)
  }

  # Build a compact, human-auditable summary of inputs actually used.
  inputs_used <- paste(
    c(
      sprintf("method=%s", method_used),
      sprintf("sign_convention=%s", sign_convention_local),
      if (!is.na(n_low)) sprintf("n_low=%s", n_low) else NULL,
      if (!is.na(n_high)) sprintf("n_high=%s", n_high) else NULL,
      if (!is.na(n_total)) sprintf("n_total=%s", n_total) else NULL,
      if (!is.na(df)) sprintf("df=%s", df) else NULL,
      if (!is.na(mean_low)) sprintf("mean_low=%s", mean_low) else NULL,
      if (!is.na(mean_high)) sprintf("mean_high=%s", mean_high) else NULL,
      if (!is.na(sd_low)) sprintf("sd_low=%s", sd_low) else NULL,
      if (!is.na(sd_high)) sprintf("sd_high=%s", sd_high) else NULL,
      if (!is.na(t_value)) sprintf("t=%s", t_value) else NULL,
      if (!is.na(f_value)) sprintf("f=%s", f_value) else NULL,
      if (!is.na(chi2_value)) sprintf("chi2=%s", chi2_value) else NULL,
      if (!is.na(reported_d)) sprintf("reported_d=%s", reported_d) else NULL,
      if (!is.na(r_within)) sprintf("r_within=%s", r_within) else NULL
    ),
    collapse = ", "
  )

  data.frame(
    paper_key = paper_key,
    study_id = study_id,
    effect_id = effect_id,
    design = design_used,
    method_used = method_used,
    computed_from_suggested = computed_from_suggested,
    inputs_used = inputs_used,
    d = d,
    v = v,
    g = g,
    v_g = v_g,
    notes_on_assumptions = notes_on_assumptions,
    imputed_flag = imputed_flag,
    needs_sensitivity = needs_sensitivity,
    stringsAsFactors = FALSE
  )
}

audit <- do.call(rbind, lapply(effects, compute_effect))
audit
                       paper_key study_id effect_id           design
1 phelan2014evidencestakesmatter        1     s1_e1 Between-Subjects
2 phelan2014evidencestakesmatter        1     s1_e2  Within-Subjects
3 phelan2014evidencestakesmatter        2     s2_e1 Between-Subjects
4 phelan2014evidencestakesmatter        2     s2_e2  Within-Subjects
5 phelan2014evidencestakesmatter        3     s3_e1 Between-Subjects
6 phelan2014evidencestakesmatter        3     s3_e2  Within-Subjects
7 phelan2014evidencestakesmatter        4     s4_e1 Between-Subjects
8 phelan2014evidencestakesmatter        4     s4_e2  Within-Subjects
              method_used computed_from_suggested
1 between_reported_d_t_df              reported_d
2          within_smcrp_t                  groups
3 between_reported_d_t_df              reported_d
4          within_smcrp_t                  groups
5 between_reported_d_t_df              reported_d
6          within_smcrp_t                  groups
7 between_reported_d_t_df              reported_d
8          within_smcrp_t                  groups
                                                                                                                                             inputs_used
1             method=between_reported_d_t_df, sign_convention=d = mean(low) - mean(high), df=55, mean_low=5.11, mean_high=4.93, t=0.435, reported_d=0.12
2 method=within_smcrp_t, sign_convention=d = mean(low) - mean(high), n_total=50, df=49, mean_low=5.32, mean_high=4.5, sd_low=1.27, sd_high=1.39, t=4.499
3              method=between_reported_d_t_df, sign_convention=d = mean(low) - mean(high), df=72, mean_low=4.29, mean_high=4.08, t=0.63, reported_d=0.15
4 method=within_smcrp_t, sign_convention=d = mean(low) - mean(high), n_total=36, df=35, mean_low=4.14, mean_high=3.64, sd_low=1.55, sd_high=1.79, t=2.24
5             method=between_reported_d_t_df, sign_convention=d = mean(low) - mean(high), df=73, mean_low=5.61, mean_high=5.79, t=0.675, reported_d=0.15
6 method=within_smcrp_t, sign_convention=d = mean(low) - mean(high), n_total=66, df=65, mean_low=5.76, mean_high=5.26, sd_low=1.55, sd_high=1.44, t=2.92
7              method=between_reported_d_t_df, sign_convention=d = mean(low) - mean(high), df=34, mean_low=4.72, mean_high=4.39, t=0.622, reported_d=0.2
8 method=within_smcrp_t, sign_convention=d = mean(low) - mean(high), n_total=36, df=35, mean_low=4.53, mean_high=3.69, sd_low=1.32, sd_high=1.47, t=4.14
           d          v          g        v_g
1  0.1200000 0.07623079  0.1183549 0.07415501
2  0.6159149 0.02109188  0.6098358 0.02104402
3  0.1500000 0.05684559  0.1484311 0.05566271
4  0.2986312 0.01829309  0.2938918 0.01826438
5 -0.1500000 0.04953683 -0.1484527 0.04852013
6  0.3342221 0.01361459  0.3316615 0.01360604
7  0.2000000 0.10397840  0.1955500 0.09940287
8  0.6012822 0.02438206  0.5922800 0.02427840
                                                                                                    notes_on_assumptions
1                    Non-juxtaposed Important vs Unimportant; split Ns not reported; v computed from reported d + t(df).
2                                             Two Cases (paired); recover within-person r from paired t(df) + means/SDs.
3                    Non-juxtaposed Important vs Unimportant; split Ns not reported; v computed from reported d + t(df).
4                                             Two Cases (paired); recover within-person r from paired t(df) + means/SDs.
5 Non-juxtaposed Important vs Unimportant; sign set by means; split Ns not reported; v computed from reported d + t(df).
6                                             Two Cases (paired); recover within-person r from paired t(df) + means/SDs.
7                                Superseded two-drunks study; split Ns not reported; v computed from reported d + t(df).
8                Superseded two-drunks study; Two Cases (paired); recover within-person r from paired t(df) + means/SDs.
  imputed_flag needs_sensitivity
1        FALSE              TRUE
2        FALSE              TRUE
3        FALSE              TRUE
4        FALSE              TRUE
5        FALSE              TRUE
6        FALSE              TRUE
7        FALSE              TRUE
8        FALSE              TRUE

Paste-ready YAML snippets

for (i in seq_len(nrow(audit))) {
  row <- audit[i, ]
  cat(sprintf("\n# %s (study_id=%s)\n", row$effect_id, row$study_id))
  cat(sprintf(
    "effect_size:\n  metric: SMD\n  d: %.12f\n  v: %.12f\n  computed_from: %s\n  needs_review: false\n  notes: \"%s\"\n",
    row$d,
    row$v,
    row$computed_from_suggested,
    gsub("\"", "'", row$inputs_used)
  ))
}

# s1_e1 (study_id=1)
effect_size:
  metric: SMD
  d: 0.120000000000
  v: 0.076230790185
  computed_from: reported_d
  needs_review: false
  notes: "method=between_reported_d_t_df, sign_convention=d = mean(low) - mean(high), df=55, mean_low=5.11, mean_high=4.93, t=0.435, reported_d=0.12"

# s1_e2 (study_id=1)
effect_size:
  metric: SMD
  d: 0.615914928275
  v: 0.021091883797
  computed_from: groups
  needs_review: false
  notes: "method=within_smcrp_t, sign_convention=d = mean(low) - mean(high), n_total=50, df=49, mean_low=5.32, mean_high=4.5, sd_low=1.27, sd_high=1.39, t=4.499"

# s2_e1 (study_id=2)
effect_size:
  metric: SMD
  d: 0.150000000000
  v: 0.056845592404
  computed_from: reported_d
  needs_review: false
  notes: "method=between_reported_d_t_df, sign_convention=d = mean(low) - mean(high), df=72, mean_low=4.29, mean_high=4.08, t=0.63, reported_d=0.15"

# s2_e2 (study_id=2)
effect_size:
  metric: SMD
  d: 0.298631225039
  v: 0.018293092206
  computed_from: groups
  needs_review: false
  notes: "method=within_smcrp_t, sign_convention=d = mean(low) - mean(high), n_total=36, df=35, mean_low=4.14, mean_high=3.64, sd_low=1.55, sd_high=1.79, t=2.24"

# s3_e1 (study_id=3)
effect_size:
  metric: SMD
  d: -0.150000000000
  v: 0.049536825638
  computed_from: reported_d
  needs_review: false
  notes: "method=between_reported_d_t_df, sign_convention=d = mean(low) - mean(high), df=73, mean_low=5.61, mean_high=5.79, t=0.675, reported_d=0.15"

# s3_e2 (study_id=3)
effect_size:
  metric: SMD
  d: 0.334222060185
  v: 0.013614587204
  computed_from: groups
  needs_review: false
  notes: "method=within_smcrp_t, sign_convention=d = mean(low) - mean(high), n_total=66, df=65, mean_low=5.76, mean_high=5.26, sd_low=1.55, sd_high=1.44, t=2.92"

# s4_e1 (study_id=4)
effect_size:
  metric: SMD
  d: 0.200000000000
  v: 0.103978398754
  computed_from: reported_d
  needs_review: false
  notes: "method=between_reported_d_t_df, sign_convention=d = mean(low) - mean(high), df=34, mean_low=4.72, mean_high=4.39, t=0.622, reported_d=0.2"

# s4_e2 (study_id=4)
effect_size:
  metric: SMD
  d: 0.601282159377
  v: 0.024382063341
  computed_from: groups
  needs_review: false
  notes: "method=within_smcrp_t, sign_convention=d = mean(low) - mean(high), n_total=36, df=35, mean_low=4.53, mean_high=3.69, sd_low=1.32, sd_high=1.47, t=4.14"