# =============================================================================
# HSCI 230: Evaluating Epidemiological Research  -  Lesson 9: Information Bias and Data Quality
# Answer key for the in-lesson R activities
# Data file(s): none  (the 10,000-person cohort is simulated with set.seed(230))
# Packages: none beyond base R
# Reproduces every code block in the lesson, then answers each activity question.
# =============================================================================

# ==== Section 1: Misclassification Bias / Watch a true RR of 2.0 attenuate under misclassification ====

set.seed(230)
n <- 10000
exposed <- rbinom(n, 1, 0.5)
# True risks: 0.20 in exposed, 0.10 in unexposed -> true RR = 2.0
disease <- rbinom(n, 1, prob = ifelse(exposed == 1, 0.20, 0.10))

# Truth from clean data
risk_t <- tapply(disease, exposed, mean)
risk_t["1"] / risk_t["0"]                  # ~ 2.0

# Non-differential misclassification of EXPOSURE (20% flipped each way)
flip <- rbinom(n, 1, 0.20)
exposed_obs <- ifelse(flip == 1, 1 - exposed, exposed)

risk_o <- tapply(disease, exposed_obs, mean)
risk_o["1"] / risk_o["0"]                  # attenuated < 2.0

# Stretch: DIFFERENTIAL misclassification (a recall-bias analogue).
# Now the error depends on DISEASE status, not exposure: diseased
# people over-report exposure, so truly-unexposed cases are recorded
# as exposed more often (25%) than truly-unexposed non-cases (5%).
fp_rate <- ifelse(disease == 1, 0.25, 0.05)
over_report <- rbinom(n, 1, fp_rate)
exposed_d <- ifelse(exposed == 1, 1, over_report)
risk_d <- tapply(disease, exposed_d, mean)
risk_d["1"] / risk_d["0"]

# ---- Activity questions (rActivity-230-9-1) ----
RR_t <- unname(risk_t["1"] / risk_t["0"])
RR_o <- unname(risk_o["1"] / risk_o["0"])
RR_d <- unname(risk_d["1"] / risk_d["0"])
cat("Q1: clean RR =", round(RR_t, 2), "(sampling noise around the true 2.0);",
    "after 20% non-differential misclassification RR =", round(RR_o, 2),
    ": biased toward the null (1.0).\n")

cat("Q2: observed risks:", "exposed", round(risk_o["1"], 3), "vs unexposed", round(risk_o["0"], 3),
    "(true:", round(risk_t["1"], 3), "vs", round(risk_t["0"], 3), ").",
    "Each observed group is a 80/20 mixture of the two true groups, so the two risks move",
    "toward each other and any ratio of them moves toward 1.\n")
mix <- table(true = exposed, observed = exposed_obs)
cat("    Cross-classification of true vs observed exposure:\n"); print(mix)

cat("Q3: recall bias (differential misclassification, higher sensitivity in cases).",
    "With cases over-reporting exposure the RR is", round(RR_d, 2), ", above the truth",
    "and away from the null; in a case-control study the OR would be overestimated the same way.\n")

# ==== Section 4: Final Assessment / Activity: Watching a true RR attenuate under non-differential misclassification ====

set.seed(230)
n <- 10000
exposed <- rbinom(n, 1, 0.5)
# True risks: 0.20 in exposed, 0.10 in unexposed -> true RR = 2.0
disease <- rbinom(n, 1, prob = ifelse(exposed == 1, 0.20, 0.10))

# Truth from clean data
risk_t <- tapply(disease, exposed, mean)
risk_t["1"] / risk_t["0"]                  # ~ 2.0

# Add nondifferential misclassification of EXPOSURE (20% flipped each way)
flip <- rbinom(n, 1, 0.20)
exposed_obs <- ifelse(flip == 1, 1 - exposed, exposed)

risk_o <- tapply(disease, exposed_obs, mean)
risk_o["1"] / risk_o["0"]                  # attenuated < 2.0
