0% fanden dieses Dokument nützlich (0 Abstimmungen)
2 Ansichten34 Seiten

March

Das Dokument demonstriert verschiedene Methoden zur Korrektur von Mehrfachtests in R, einschließlich der Bonferroni-, Holm- und Benjamini-Hochberg-Korrekturen. Es enthält auch Simulationen zur Untersuchung von falsch positiven Ergebnissen und zur Analyse von ANOVA-Tests mit verschiedenen Hypothesen. Zudem wird eine Analyse des ToothGrowth-Datensatzes durchgeführt, um additive und multiplikative Modelle sowie deren Interaktionen zu vergleichen.

Hochgeladen von

kulkarni22
Copyright
© All Rights Reserved
Wir nehmen die Rechte an Inhalten ernst. Wenn Sie vermuten, dass dies Ihr Inhalt ist, beanspruchen Sie ihn hier.
Verfügbare Formate
Als PDF, TXT herunterladen oder online auf Scribd lesen
0% fanden dieses Dokument nützlich (0 Abstimmungen)
2 Ansichten34 Seiten

March

Das Dokument demonstriert verschiedene Methoden zur Korrektur von Mehrfachtests in R, einschließlich der Bonferroni-, Holm- und Benjamini-Hochberg-Korrekturen. Es enthält auch Simulationen zur Untersuchung von falsch positiven Ergebnissen und zur Analyse von ANOVA-Tests mit verschiedenen Hypothesen. Zudem wird eine Analyse des ToothGrowth-Datensatzes durchgeführt, um additive und multiplikative Modelle sowie deren Interaktionen zu vergleichen.

Hochgeladen von

kulkarni22
Copyright
© All Rights Reserved
Wir nehmen die Rechte an Inhalten ernst. Wenn Sie vermuten, dass dies Ihr Inhalt ist, beanspruchen Sie ihn hier.
Verfügbare Formate
Als PDF, TXT herunterladen oder online auf Scribd lesen

11 Mar

############################################################
# MULTIPLE TESTING CORRECTION DEMONSTRATION
# Purpose: Demonstrate multiple testing and corrections in R
############################################################

############################################################
# 1. SETUP
############################################################

[Link](123)

cat("Multiple Testing Demonstration\n")


cat("------------------------------\n")

############################################################
# 2. FALSE POSITIVES UNDER THE NULL
############################################################

# Number of hypothesis tests


n_tests <- 1000

# Simulate p-values when the null hypothesis is true


pvals <- runif(n_tests)

# Count how many are significant


false_positives <- sum(pvals < 0.05)

cat("Number of tests:", n_tests, "\n")


cat("Significant at p < 0.05:", false_positives, "\n\n")

############################################################
# 3. VISUALIZE P-VALUE DISTRIBUTION
############################################################

hist(pvals,
breaks = 20,
main = "P-value distribution under the null",
xlab = "p-value",
col = "lightblue")
############################################################
# 4. HOW FALSE POSITIVES SCALE
############################################################

test_sizes <- c(100, 1000, 10000)

for(n in test_sizes)
{
pvals <- runif(n)
fp <- sum(pvals < 0.05)

cat("Tests:", n,
" False positives:", fp,
" Expected:", n*0.05, "\n")
}

############################################################
# 5. FAMILY-WISE ERROR RATE (FWER)
############################################################

# Probability of at least one false positive

alpha <- 0.0005


n <- 100

fwer <- 1 - (1 - alpha)^n

cat("\nProbability of >=1 false positive with",


n, "tests:", fwer, "\n\n")

############################################################
# 6. BONFERRONI CORRECTION
############################################################

n_tests <- 1000


pvals <- runif(n_tests)

pvals<-c(0.0000000000000001,pvals)

# Bonferroni adjusted p-values


p_bonf <- [Link](pvals, method = "bonferroni")

significant_bonf <- sum(p_bonf < 0.05)

cat("Significant after Bonferroni:", significant_bonf, "\n")

par(mfrow=c(1,2))
hist(pvals,
breaks = 20,
main = "P-value distribution under the null",
xlab = "p-value",
col = "lightblue")

hist(p_bonf,
breaks = 200,
main = "P-value distribution after Bonferroni",
xlab = "p-value",
col = "lightblue")

c(p_bonf[p_bonf<1],0.0000000000000001)
############################################################
# 7. HOLM CORRECTION
############################################################

p_holm <- [Link](pvals, method = "holm")

significant_holm <- sum(p_holm < 0.05)

cat("Significant after Holm:", significant_holm, "\n")

c(p_holm[p_holm<1],p_bonf[p_bonf<1],0.0000000000000001)

############################################################
# 8. FALSE DISCOVERY RATE (BENJAMINI-HOCHBERG)
############################################################

p_bh <- [Link](pvals, method = "BH")

significant_bh <- sum(p_bh < 0.05)

cat("Significant after BH (FDR):", significant_bh, "\n")


############################################################
# 9. COMPARISON OF METHODS
############################################################

methods <- c("bonferroni", "holm", "BH")

for(m in methods)
{
adj <- [Link](pvals, method = m)
sig <- sum(adj < 0.05)

cat("Method:", m,
" Significant:", sig, "\n")
}

############################################################
# 10. PLOT RAW VS ADJUSTED P-VALUES
############################################################

adj <- [Link](pvals, method = "BH")

plot(pvals,
adj,
pch = 19,
cex = 0.5,
xlab = "Raw p-values",
ylab = "BH adjusted p-values",
main = "Raw vs Adjusted p-values")

abline(0,1,col="red")

############################################################
# 11. SIMULATION WITH TRUE SIGNAL
############################################################

[Link](123)

n_tests <- 1000


n_signal <- 100
n_null <- n_tests - n_signal
# null p-values
p_null <- runif(n_null)

# signal p-values (skewed toward zero)


p_signal <- rbeta(n_signal, 0.3, 1)

pvals <- c(p_null, p_signal)

############################################################
# 12. VISUALIZE SIGNAL VS NULL
############################################################

hist(pvals,
breaks = 30,
col = "lightgreen",
main = "P-values with True Signal",
xlab = "p-value")

############################################################
# 13. APPLY MULTIPLE CORRECTIONS
############################################################

methods <- c("bonferroni", "holm", "BH")

results <- [Link]()

for(m in methods)
{
adj <- [Link](pvals, method = m)

discoveries <- sum(adj < 0.05)

results <- rbind(results,


[Link](method=m,
discoveries=discoveries))
}

print(results)

############################################################
# 14. ESTIMATE FALSE DISCOVERIES
############################################################

# Suppose first 900 tests are null

null_indices <- 1:n_null

for(m in methods)
{
adj <- [Link](pvals, method = m)

sig <- which(adj < 0.05)

false_discoveries <- sum(sig %in% null_indices)

cat("\nMethod:", m, "\n")
cat("Discoveries:", length(sig), "\n")
cat("False discoveries:", false_discoveries, "\n")

if(length(sig) > 0)
{
cat("False discovery rate:",
false_discoveries/length(sig), "\n")
}
}

############################################################
# 15. GENOMICS SCALE EXAMPLE
############################################################

[Link](123)

n_genes <- 20000


n_true <- 500

p_null <- runif(n_genes - n_true)


p_signal <- rbeta(n_true, 0.2, 1)

pvals <- c(p_null, p_signal)

p_adj <- [Link](pvals, method = "BH")

discoveries <- sum(p_adj < 0.05)


cat("\nGENOME SCALE EXAMPLE\n")
cat("Genes tested:", n_genes, "\n")
cat("True signals:", n_true, "\n")
cat("Significant genes (FDR<0.05):", discoveries, "\n")

############################################################
# 16. SUMMARY TABLE
############################################################

methods <- c("bonferroni","holm","BH")

summary_table <- [Link]()

for(m in methods)
{
adj <- [Link](pvals, method=m)

discoveries <- sum(adj < 0.05)

summary_table <- rbind(summary_table,


[Link](Method=m,
Discoveries=discoveries))
}

print(summary_table)

############################################################
# END OF SCRIPT
############################################################

############################################################
# INTERACTIVE MULTIPLE TESTING SIMULATION
############################################################

simulate_tests <- function(n_tests = 10000,


prop_signal = 0.05,
effect_strength = 0.3,
alpha = 0.05)
{
n_signal <- round(n_tests * prop_signal)
n_null <- n_tests - n_signal

# Null p-values
p_null <- runif(n_null)

# Signal p-values
p_signal <- rbeta(n_signal, effect_strength, 1)

pvals <- c(p_null, p_signal)

methods <- c("none","bonferroni","holm","BH")

results <- [Link]()

for(m in methods)
{
if(m == "none")
{
adj <- pvals
} else {
adj <- [Link](pvals, method=m)
}

sig <- which(adj < alpha)

discoveries <- length(sig)


false_discoveries <- sum(sig <= n_null)

if(discoveries > 0)
{
fdr <- false_discoveries / discoveries
} else {
fdr <- 0
}

results <- rbind(results,


[Link](Method=m,
Discoveries=discoveries,
False_Discoveries=false_discoveries,
FDR=round(fdr,3)))
}

print(results)
# Plot p-value distribution
hist(pvals,
breaks=40,
col="lightblue",
main=paste("P-value Distribution (",n_tests,"tests)",sep=""),
xlab="p-value")

############################################################
# RUN THE SIMULATION
############################################################

simulate_tests(
n_tests = 20000,
prop_signal = 0.05,
effect_strength = 0.3
)

13 Mar

#A=B=C:

x = rnorm(10, mean = 5, sd = 10)


y = rnorm(10, mean = 5, sd = 10)
z = rnorm(10, mean = 5, sd = 10)

abcBox <- [Link](


values = c(x,y,z),
variable = c(
rep("A", length(x)),
rep("B", length(y)),
rep("C", length(z))
)
)

anova_result <- aov(values ~ variable, data = abcBox)


summary(anova_result)

#A>B=C
x = rnorm(10, mean = 10, sd = 10)
y = rnorm(10, mean = 5, sd = 10)
z = rnorm(10, mean = 5, sd = 10)
abcBox <- [Link](
values = c(x,y,z),
variable = c(
rep("A", length(x)),
rep("B", length(y)),
rep("C", length(z))
)
)

anova_result <- aov(values ~ variable, data = abcBox)


summary(anova_result)
#A>B>C
x = rnorm(10, mean = 15, sd = 10)
y = rnorm(10, mean = 10, sd = 10)
z = rnorm(10, mean = 5, sd = 10)
abcBox <- [Link](
values = c(x,y,z),
variable = c(
rep("A", length(x)),
rep("B", length(y)),
rep("C", length(z))
)
)

anova_result <- aov(values ~ variable, data = abcBox)


summary(anova_result)

#A>B<C
x = rnorm(10, mean = 12, sd = 10)
y = rnorm(10, mean = 5, sd = 10)
z = rnorm(10, mean = 11, sd = 10)
abcBox <- [Link](
values = c(x,y,z),
variable = c(
rep("A", length(x)),
rep("B", length(y)),
rep("C", length(z))
)
)

anova_result <- aov(values ~ variable, data = abcBox)


summary(anova_result)
#A=B>C
x = rnorm(10, mean = 10, sd = 10)
y = rnorm(10, mean = 10, sd = 10)
z = rnorm(10, mean = 5, sd = 10)
abcBox <- [Link](
values = c(x,y,z),
variable = c(
rep("A", length(x)),
rep("B", length(y)),
rep("C", length(z))
)
)

anova_result <- aov(values ~ variable, data = abcBox)


summary(anova_result)

#A=C>B
x = rnorm(10, mean = 10, sd = 10)
y = rnorm(10, mean = 5, sd = 10)
z = rnorm(10, mean = 10, sd = 10)
abcBox <- [Link](
values = c(x,y,z),
variable = c(
rep("A", length(x)),
rep("B", length(y)),
rep("C", length(z))
)
)

anova_result <- aov(values ~ variable, data = abcBox)


summary(anova_result)

par(mfrow = c(2,3))

# 1) A = B = C
x = rnorm(10,5,10)
y = rnorm(10,5,10)
z = rnorm(10,5,10)

abcBox = [Link](
values=c(x,y,z),
variable=c(rep("A",10),rep("B",10),rep("C",10))
)
boxplot(values~variable,data=abcBox,main="A=B=C",
col = "pink")

# 2) A > B = C
x = rnorm(10,10,10)
y = rnorm(10,5,10)
z = rnorm(10,5,10)

abcBox = [Link](
values=c(x,y,z),
variable=c(rep("A",10),rep("B",10),rep("C",10))
)

boxplot(values~variable,data=abcBox,main="A>B=C",
col = "blue")

# 3) A > B > C
x = rnorm(10,15,10)
y = rnorm(10,10,10)
z = rnorm(10,5,10)

abcBox = [Link](
values=c(x,y,z),
variable=c(rep("A",10),rep("B",10),rep("C",10))
)

boxplot(values~variable,data=abcBox,main="A>B>C",
col = "yellow")

# 4) A > B < C
x = rnorm(10,12,10)
y = rnorm(10,5,10)
z = rnorm(10,11,10)

abcBox = [Link](
values=c(x,y,z),
variable=c(rep("A",10),rep("B",10),rep("C",10))
)

boxplot(values~variable,data=abcBox,main="A>B<C",
col = "purple")

# 5) A = B > C
x = rnorm(10,10,10)
y = rnorm(10,10,10)
z = rnorm(10,5,10)

abcBox = [Link](
values=c(x,y,z),
variable=c(rep("A",10),rep("B",10),rep("C",10))
)

boxplot(values~variable,data=abcBox,main="A=B>C",
col = "green")

# 6) A = C > B
x = rnorm(10,10,10)
y = rnorm(10,5,10)
z = rnorm(10,10,10)

abcBox = [Link](
values=c(x,y,z),
variable=c(rep("A",10),rep("B",10),rep("C",10))
)

boxplot(values~variable,data=abcBox,main="A=C>B",
col = "red")

One way anova

16 Mar

##16th March 2026*#

data(ToothGrowth)

# Convert to factors
ToothGrowth$dose <- [Link](ToothGrowth$dose)
ToothGrowth$supp <- [Link](ToothGrowth$supp)

# Create extra factor for 3-way ANOVA


[Link](1)
ToothGrowth$group <- factor(sample(c("G1","G2"), nrow(ToothGrowth), replace=TRUE))

head(ToothGrowth)

#Additive Model (no interaction)


model_2_add <- aov(len ~ dose + supp, data=ToothGrowth)
summary(model_2_add)

#Multiplicative Model
model_2_mul <- aov(len ~ dose * supp, data=ToothGrowth)
summary(model_2_mul)

#Compare additive vs multiplicative


anova(model_2_add, model_2_mul)

#Pairwise t-test:
[Link](ToothGrowth$len,
interaction(ToothGrowth$dose, ToothGrowth$supp),
[Link]="bonferroni")

#Tukey Post-Hoc test


TukeyHSD(model_2_mul)

#THREE WAY ANOVA:


#Additive:
model_3_add <- aov(len ~ dose + supp + group, data=ToothGrowth)
summary(model_3_add)

#Multiplicative (with interactions)


model_3_mul <- aov(len ~ dose * supp * group, data=ToothGrowth)
summary(model_3_mul)

#Comparing:
anova(model_3_add, model_3_mul)

#Multiway ANOVA:
model_multi <- aov(len ~ dose * supp * group, data=ToothGrowth)
summary(model_multi)

#Tukey for multiway


TukeyHSD(model_multi)

summary(model_2_mul)
summary(model_3_mul)

anova(model_2_add, model_2_mul)
summary(model_2_mul)
#Two-way ANOVA was fitted using additive and multiplicative models.
#The interaction term dose:supp was significant (p < 0.05),
#therefore the multiplicative model was preferred over the additive model.

summary(model_3_mul)

##Three-way ANOVA was fitted using dose, supp and group.


#The factor group and all interactions involving group were not significant.
#Only the interaction between dose and supp was significant (p < 0.05).
#Therefore the full multiplicative three-way model was not required.
#The best model was the two-way multiplicative model with dose and supp.

par(mfrow=c(2,2))
plot(model_2_mul)
par(mfrow=c(1,1))
res <- residuals(model_2_mul)
fit <- fitted(model_2_mul)
[Link](res)
qqnorm(res)
qqline(res)

#Homogeneity of variance (Bartlett test)


[Link](len ~ interaction(dose,supp), data=ToothGrowth)

#Levene test (better than Bartlett)


library(car)

leveneTest(len ~ dose*supp, data=ToothGrowth)

#Final ANOVA validation check


anova(model_2_mul)
summary(model_2_mul)
TukeyHSD(model_2_mul)

#Diagnostic plots were examined for the multiplicative two-way ANOVA model.
#Residual plots showed no serious deviation from assumptions.
#Shapiro-Wilk test indicated normality of residuals.
#Bartlett / Levene test showed homogeneity of variance.
#Therefore ANOVA assumptions were satisfied.
#The final selected model was the two-way multiplicative ANOVA with interaction between dose
and supp.

#Interaction Plot:
[Link](ToothGrowth$dose,
ToothGrowth$supp,
ToothGrowth$len)
#Means plot
plot(tapply(ToothGrowth$len,
interaction(ToothGrowth$dose, ToothGrowth$supp),
mean),
type="b",
xlab="Dose-Supp",
ylab="Mean length")

#Boxplots by factors:
boxplot(len ~ dose * supp,
data = ToothGrowth,
col="lightblue")

#Residual vs fitted (separate)


plot(fitted(model_2_mul),
residuals(model_2_mul),
xlab="Fitted",
ylab="Residuals")

abline(h=0)

18 Mar

##ADDITIONAL------p value simulations


####################################
simulate_pvalues <- function(effect_size = 10, n_sim = 1000, add_outliers = FALSE) {

p_anova <- numeric(n_sim)


p_kw <- numeric(n_sim)

for (i in 1:n_sim) {

# Generate data
a <- rnorm(30, mean = 50, sd = 10)
b <- rnorm(30, mean = 50 + effect_size, sd = 10)
c <- rnorm(30, mean = 50 + 2*effect_size, sd = 10)
# Add outliers if required
if (add_outliers) {
a <- c(a, 300)
b <- c(b, -200)
c <- c(c, 400)
}

data <- [Link](


values = c(a, b, c),
group = factor(rep(c("A","B","C"),
times = c(length(a), length(b), length(c))))
)

# ANOVA
p_anova[i] <- summary(aov(values ~ group, data = data))[[1]][["Pr(>F)"]][1]

# Kruskal-Wallis
p_kw[i] <- [Link](values ~ group, data = data)$[Link]
}

return(list(anova = p_anova, kw = p_kw))


}
[Link](123)

# Effect size = 10
res_small <- simulate_pvalues(effect_size = 10)

# Effect size = 50
res_large <- simulate_pvalues(effect_size = 50)

# With outliers
res_outliers <- simulate_pvalues(effect_size = 10, add_outliers = TRUE)
par(mfrow = c(3,2))

# Small effect size


hist(res_small$anova, main="ANOVA (Effect=10)", col="lightblue")
hist(res_small$kw, main="Kruskal (Effect=10)", col="lightgreen")

# Large effect size


hist(res_large$anova, main="ANOVA (Effect=50)", col="lightblue")
hist(res_large$kw, main="Kruskal (Effect=50)", col="lightgreen")

# With outliers
hist(res_outliers$anova, main="ANOVA (Outliers)", col="lightblue")
hist(res_outliers$kw, main="Kruskal (Outliers)", col="lightgreen")
mean(res_small$anova < 0.05)
mean(res_small$kw < 0.05)

mean(res_large$anova < 0.05)


mean(res_large$kw < 0.05)

mean(res_outliers$anova < 0.05)


mean(res_outliers$kw < 0.05)

20 Mar

library(ggplot2)
library(dplyr)

[Link](42)

# =========================================
# PART A: STATISTICAL POWER COMPARISON
# =========================================

simulate_power <- function(n = 20, effect_sizes = seq(0, 1.5, by = 0.15), n_sim = 500) {

results <- [Link]()

for (d in effect_sizes) {

t_sig <- 0
w_sig <- 0

for (i in 1:n_sim) {
a <- rnorm(n, mean = 0, sd = 1)
b <- rnorm(n, mean = d, sd = 1)

t_p <- [Link](a, b)$[Link]


w_p <- [Link](a, b)$[Link]

if (t_p < 0.05) t_sig <- t_sig + 1


if (w_p < 0.05) w_sig <- w_sig + 1
}

results <- rbind(results, [Link](


effect_size = d,
test = "t-test",
power = t_sig / n_sim
))

results <- rbind(results, [Link](


effect_size = d,
test = "Wilcoxon",
power = w_sig / n_sim
))
}

return(results)
}

power_data <- simulate_power()

ggplot(power_data, aes(x = effect_size, y = power, color = test)) +


geom_line(size = 1.2) +
geom_point() +
labs(title = "Higher Statistical Power of Parametric vs Non-parametric Tests",
x = "Effect Size (Mean Difference)",
y = "Power") +
theme_minimal()

# =========================================
# PART B: OUTLIER SENSITIVITY
# =========================================

generate_outliers <- function(data, type) {

if (type == "extreme_high") {
data <- c(data, 100, 120, 150)
}

if (type == "extreme_low") {
data <- c(data, -100, -120, -150)
}

if (type == "both_sides") {
data <- c(data, -120, -100, 100, 120)
}

if (type == "skewed_cluster") {
data <- c(data, rnorm(5, mean = 80, sd = 2))
}

if (type == "mild_outliers") {
data <- c(data, 65, 70, 75)
}

return(data)
}

outlier_types <- c("extreme_high", "extreme_low", "both_sides",


"skewed_cluster", "mild_outliers")

plot_list <- list()

for (otype in outlier_types) {

# BASE DATA (your structure)


a <- rnorm(20, mean = 50, sd = 10)
b <- rnorm(20, mean = 55, sd = 10)

# ADD OUTLIERS
a_out <- generate_outliers(a, otype)
b_out <- generate_outliers(b, otype)

# TESTS
t_p <- [Link](a_out, b_out)$[Link]
w_p <- [Link](a_out, b_out)$[Link]

df <- [Link](
values = c(a_out, b_out),
group = rep(c("A", "B"), c(length(a_out), length(b_out)))
)

p <- ggplot(df, aes(x = group, y = values)) +


geom_boxplot([Link] = NA) +
geom_jitter(width = 0.2, alpha = 0.5) +
ggtitle(paste0(otype,
"\n t-test p=", round(t_p, 4),
" | Wilcoxon p=", round(w_p, 4))) +
theme_minimal()

plot_list[[otype]] <- p
}

# Display plots
for (p in plot_list) print(p)

plot_list$extreme_high
plot_list$extreme_low
plot_list$both_sides
plot_list$skewed_cluster
plot_list$mild_outliers

# =========================================
# BONUS: DISTRIBUTION DISTORTION
# =========================================

for (otype in outlier_types) {

base <- rnorm(100, mean = 50, sd = 10)


altered <- generate_outliers(base, otype)

df <- [Link](
values = c(base, altered),
type = rep(c("Original", otype), c(length(base), length(altered)))
)

p <- ggplot(df, aes(x = values, fill = type)) +


geom_density(alpha = 0.4) +
ggtitle(paste("Distribution distortion:", otype)) +
theme_minimal()

print(p)
}

30 Mar

############################################################
# POLISHED TEACHING SCRIPT:
# Slope, spread, correlation coefficient, Spearman correlation,
# p-values, sample size, and nonlinear relationships
#
# Goal:
# Visually demonstrate how changes in:
# - slope
# - spread around a line
# - sample size
# - relationship shape
# affect:
# - Pearson correlation (r)
# - Spearman correlation (rho)
# - p-values
#
# Output:
# Figure 1: Conceptual overview panels
# Figure 2: Sample-size sweep
# Figure 3: Noise sweep
# Figure 4: Repeated simulation summary
#
# Author: ChatGPT
############################################################

##############################
# 1. Packages
##############################

# Install if needed:
# [Link](c("ggplot2", "dplyr", "patchwork", "tibble"))

library(ggplot2)
library(dplyr)
library(patchwork)
library(tibble)

##############################
# 2. Global settings
##############################

[Link](1234)

theme_teaching <- function(base_size = 12) {


theme_bw(base_size = base_size) +
theme(
[Link] = element_blank(),
[Link] = element_line(linewidth = 0.25),
[Link] = element_rect(fill = "grey95", colour = "black"),
[Link] = element_text(face = "bold"),
[Link] = element_text(face = "bold"),
[Link] = element_text(size = base_size * 0.95),
[Link] = element_text(face = "bold"),
[Link] = element_text(face = "bold", size = base_size * 1.25)
)
}

##############################
# 3. Helper functions
##############################

simulate_linear <- function(n = 50,


slope = 1,
intercept = 0,
noise_sd = 1,
x_min = 0,
x_max = 10) {
x <- seq(x_min, x_max, [Link] = n)
y <- intercept + slope * x + rnorm(n, mean = 0, sd = noise_sd)
tibble(x = x, y = y)
}

simulate_nonlinear_quadratic <- function(n = 80,


noise_sd = 3,
x_min = -5,
x_max = 5) {
x <- seq(x_min, x_max, [Link] = n)
y <- x^2 + rnorm(n, mean = 0, sd = noise_sd)
tibble(x = x, y = y)
}

simulate_nonlinear_monotonic <- function(n = 80,


noise_sd = 0.25,
x_min = 0.5,
x_max = 10) {
x <- seq(x_min, x_max, [Link] = n)
y <- log(x) + rnorm(n, mean = 0, sd = noise_sd)
tibble(x = x, y = y)
}

get_stats <- function(df) {


pearson_test <- [Link](df$x, df$y, method = "pearson")
spearman_test <- [Link](df$x, df$y, method = "spearman", exact = FALSE)
lm_fit <- lm(y ~ x, data = df)

tibble(
slope_est = coef(lm_fit)[2],
intercept_est = coef(lm_fit)[1],
pearson_r = unname(pearson_test$estimate),
pearson_p = pearson_test$[Link],
spearman_rho = unname(spearman_test$estimate),
spearman_p = spearman_test$[Link]
)
}

format_p <- function(p) {


if ([Link](p)) return("p = NA")
if (p < 2.2e-16) {
"p < 2.2e-16"
} else if (p < 0.001) {
paste0("p = ", format(p, scientific = TRUE, digits = 2))
} else {
paste0("p = ", signif(p, 3))
}
}

make_annotation <- function(stats_row,


include_slope = TRUE,
include_spearman = TRUE) {
pieces <- c()
if (include_slope) {
pieces <- c(pieces, paste0("slope = ", round(stats_row$slope_est, 2)))
}
pieces <- c(
pieces,
paste0("Pearson r = ", round(stats_row$pearson_r, 2)),
format_p(stats_row$pearson_p)
)
if (include_spearman) {
pieces <- c(
pieces,
paste0("Spearman rho = ", round(stats_row$spearman_rho, 2))
)
}
paste(pieces, collapse = "\n")
}

panel_plot <- function(df,


title = "",
subtitle = NULL,
show_lm = TRUE,
show_loess = FALSE,
annotate_text = NULL,
tag = NULL) {

p <- ggplot(df, aes(x = x, y = y)) +


geom_point(size = 2, alpha = 0.8)

if (show_lm) {
p <- p + geom_smooth(method = "lm", se = FALSE, linewidth = 0.9)
}

if (show_loess) {
p <- p + geom_smooth(method = "loess", se = FALSE, linewidth = 0.9, linetype = 2)
}

p <- p +
labs(
title = title,
subtitle = subtitle,
x = "X",
y = "Y",
tag = tag
)+
theme_teaching()

if (![Link](annotate_text)) {
x_pos <- min(df$x) + 0.03 * diff(range(df$x))
y_pos <- max(df$y) - 0.04 * diff(range(df$y))
p <- p + annotate(
"label",
x = x_pos,
y = y_pos,
hjust = 0,
vjust = 1,
size = 3.5,
label = annotate_text
)
}

p
}

##############################
# 4. Figure 1:
# Conceptual overview panels
##############################

# A. Same high correlation, shallow slope


df_A <- simulate_linear(n = 70, slope = 0.4, noise_sd = 0.30)
st_A <- get_stats(df_A)
ann_A <- make_annotation(st_A)

# B. Similar correlation, steeper slope


# Scaling both slope and noise keeps the pattern similarly tight
df_B <- simulate_linear(n = 70, slope = 1.8, noise_sd = 1.35)
st_B <- get_stats(df_B)
ann_B <- make_annotation(st_B)

# C. Same slope as B, but much more spread, so lower correlation


df_C <- simulate_linear(n = 70, slope = 1.8, noise_sd = 4.5)
st_C <- get_stats(df_C)
ann_C <- make_annotation(st_C)

# D. Nonlinear quadratic relationship:


# strong structure, but Pearson can be near 0
df_D <- simulate_nonlinear_quadratic(n = 90, noise_sd = 2.8)
st_D <- get_stats(df_D)
ann_D <- make_annotation(st_D)

pA <- panel_plot(
df_A,
title = "Shallow slope, tight spread",
subtitle = "High linear association despite shallow slope",
show_lm = TRUE,
annotate_text = ann_A,
tag = "A"
)

pB <- panel_plot(
df_B,
title = "Steeper slope, similarly tight spread",
subtitle = "Slope changes, but correlation can remain high",
show_lm = TRUE,
annotate_text = ann_B,
tag = "B"
)

pC <- panel_plot(
df_C,
title = "Same slope, much wider spread",
subtitle = "More scatter around the line reduces correlation",
show_lm = TRUE,
annotate_text = ann_C,
tag = "C"
)

pD <- panel_plot(
df_D,
title = "Nonlinear relationship",
subtitle = "Pearson may be small even when the relationship is strong",
show_lm = TRUE,
show_loess = TRUE,
annotate_text = ann_D,
tag = "D"
)

fig1 <- (pA | pB) / (pC | pD) +


plot_annotation(
title = "Figure 1. Slope, spread, and the meaning of correlation",
subtitle = paste(
"Panels A and B show that slope alone does not determine correlation.",
"Panel C shows that greater spread around the line lowers correlation.",
"Panel D shows that Pearson correlation can miss nonlinear structure."
)
)

print(fig1)

ggsave("Figure1_conceptual_correlation_panels.png", fig1, width = 13, height = 10, dpi = 300)


ggsave("Figure1_conceptual_correlation_panels.pdf", fig1, width = 13, height = 10)

##############################
# 5. Figure 2:
# Sample-size sweep
##############################

sample_sizes <- c(8, 12, 20, 40, 80, 160)

sample_panels <- lapply(sample_sizes, function(nn) {


df <- simulate_linear(n = nn, slope = 0.8, noise_sd = 2.4)
st <- get_stats(df)
df$panel <- paste0("n = ", nn)
df$annotation <- make_annotation(st)
df
})

sample_df <- bind_rows(sample_panels)

sample_labels <- sample_df %>%


group_by(panel) %>%
summarise(
x = min(x) + 0.03 * diff(range(x)),
y = max(y) - 0.04 * diff(range(y)),
annotation = first(annotation),
.groups = "drop"
)

fig2 <- ggplot(sample_df, aes(x = x, y = y)) +


geom_point(size = 1.8, alpha = 0.8) +
geom_smooth(method = "lm", se = FALSE, linewidth = 0.85) +
facet_wrap(~ panel, ncol = 3, scales = "fixed") +
geom_label(
data = sample_labels,
aes(x = x, y = y, label = annotation),
[Link] = FALSE,
hjust = 0,
vjust = 1,
size = 3.2,
[Link] = 0.25
)+
labs(
title = "Figure 2. Effect of sample size on correlation and p-value",
subtitle = paste(
"Slope and noise are similar across panels.",
"Observed p-values often become smaller as sample size increases,",
"even when the underlying pattern is conceptually similar."
),
x = "X",
y = "Y"
)+
theme_teaching()

print(fig2)

ggsave("Figure2_sample_size_effect.png", fig2, width = 12, height = 8.5, dpi = 300)


ggsave("Figure2_sample_size_effect.pdf", fig2, width = 12, height = 8.5)

##############################
# 6. Figure 3:
# Noise sweep
##############################

noise_levels <- c(0.3, 0.7, 1.5, 2.5, 4.0, 6.0)

noise_panels <- lapply(noise_levels, function(ns) {


df <- simulate_linear(n = 50, slope = 1.2, noise_sd = ns)
st <- get_stats(df)

df$panel <- paste0("noise SD = ", ns)


df$annotation <- make_annotation(st)
df
})

noise_df <- bind_rows(noise_panels)

noise_labels <- noise_df %>%


group_by(panel) %>%
summarise(
x = min(x) + 0.03 * diff(range(x)),
y = max(y) - 0.04 * diff(range(y)),
annotation = first(annotation),
.groups = "drop"
)

fig3 <- ggplot(noise_df, aes(x = x, y = y)) +


geom_point(size = 1.8, alpha = 0.8) +
geom_smooth(method = "lm", se = FALSE, linewidth = 0.85) +
facet_wrap(~ panel, ncol = 3, scales = "fixed") +
geom_label(
data = noise_labels,
aes(x = x, y = y, label = annotation),
[Link] = FALSE,
hjust = 0,
vjust = 1,
size = 3.2,
[Link] = 0.25
)+
labs(
title = "Figure 3. Effect of spread around the line",
subtitle = paste(
"Slope is held constant. Increasing noise weakens the alignment of points",
"with the line, which usually lowers Pearson r and often also affects significance."
),
x = "X",
y = "Y"
)+
theme_teaching()

print(fig3)

ggsave("Figure3_noise_effect.png", fig3, width = 12, height = 8.5, dpi = 300)


ggsave("Figure3_noise_effect.pdf", fig3, width = 12, height = 8.5)

##############################
# 7. Figure 4:
# Repeated simulation summary
##############################

repeat_sim <- function(n = 20, slope = 0.6, noise_sd = 3, reps = 500) {


out <- vector("list", reps)

for (i in seq_len(reps)) {
df <- simulate_linear(n = n, slope = slope, noise_sd = noise_sd)
st <- get_stats(df)

out[[i]] <- tibble(


rep = i,
pearson_r = st$pearson_r,
pearson_p = st$pearson_p,
spearman_rho = st$spearman_rho,
spearman_p = st$spearman_p,
significant = st$pearson_p < 0.05
)
}

bind_rows(out)
}

rep_df <- repeat_sim(n = 20, slope = 0.6, noise_sd = 3, reps = 500)

p_r_hist <- ggplot(rep_df, aes(x = pearson_r)) +


geom_histogram(bins = 30) +
labs(
title = "Observed Pearson correlation across repeated simulations",
subtitle = "The same underlying process can yield a range of observed r values",
x = "Observed Pearson r",
y = "Count"
)+
theme_teaching()

p_p_hist <- ggplot(rep_df, aes(x = pearson_p)) +


geom_histogram(bins = 30) +
geom_vline(xintercept = 0.05, linetype = 2, linewidth = 0.9) +
labs(
title = "Observed p-values across repeated simulations",
subtitle = "Significance can vary from sample to sample",
x = "Observed p-value",
y = "Count"
)+
theme_teaching()

fig4 <- p_r_hist | p_p_hist


print(fig4)

ggsave("Figure4_repeated_simulations.png", fig4, width = 12, height = 5.5, dpi = 300)


ggsave("Figure4_repeated_simulations.pdf", fig4, width = 12, height = 5.5)

##############################
# 8. Optional extra:
# Monotonic nonlinear example
# Pearson may still work somewhat, Spearman often tracks monotonicity well
##############################

df_extra <- simulate_nonlinear_monotonic(n = 80, noise_sd = 0.18)


st_extra <- get_stats(df_extra)
ann_extra <- make_annotation(st_extra)

fig_extra <- panel_plot(


df_extra,
title = "Optional example: monotonic but nonlinear relationship",
subtitle = "Spearman often captures monotonic structure better than Pearson",
show_lm = TRUE,
show_loess = TRUE,
annotate_text = ann_extra,
tag = "E"
)
print(fig_extra)

ggsave("Figure_extra_monotonic_nonlinear.png", fig_extra, width = 6.5, height = 5.5, dpi = 300)


ggsave("Figure_extra_monotonic_nonlinear.pdf", fig_extra, width = 6.5, height = 5.5)

##############################
# 9. Summary tables
##############################

summarise_case <- function(name, df, scenario) {


st <- get_stats(df)
tibble(
figure = name,
scenario = scenario,
n = nrow(df),
fitted_slope = round(st$slope_est, 3),
pearson_r = round(st$pearson_r, 3),
pearson_p = signif(st$pearson_p, 3),
spearman_rho = round(st$spearman_rho, 3),
spearman_p = signif(st$spearman_p, 3)
)
}

summary_table <- bind_rows(


summarise_case("Figure 1A", df_A, "Shallow slope, tight spread"),
summarise_case("Figure 1B", df_B, "Steep slope, tight spread"),
summarise_case("Figure 1C", df_C, "Steep slope, wide spread"),
summarise_case("Figure 1D", df_D, "Quadratic nonlinear"),
summarise_case("Extra", df_extra, "Monotonic nonlinear")
)

cat("\n================ SUMMARY TABLE ================\n")


print(summary_table)

sample_summary <- sample_df %>%


group_by(panel) %>%
summarise(
n = n(),
pearson_r = round(cor(x, y, method = "pearson"), 3),
spearman_rho = round(cor(x, y, method = "spearman"), 3),
pearson_p = signif([Link](x, y, method = "pearson")$[Link], 3),
.groups = "drop"
)
cat("\n=========== SAMPLE SIZE SUMMARY ===========\n")
print(sample_summary)

noise_summary <- noise_df %>%


group_by(panel) %>%
summarise(
n = n(),
pearson_r = round(cor(x, y, method = "pearson"), 3),
spearman_rho = round(cor(x, y, method = "spearman"), 3),
pearson_p = signif([Link](x, y, method = "pearson")$[Link], 3),
.groups = "drop"
)

cat("\n============= NOISE SUMMARY ===============\n")


print(noise_summary)

cat("\n======= REPEATED SIMULATION SUMMARY =======\n")


cat("Mean observed Pearson r: ", round(mean(rep_df$pearson_r), 3), "\n")
cat("SD observed Pearson r: ", round(sd(rep_df$pearson_r), 3), "\n")
cat("Mean observed p-value: ", round(mean(rep_df$pearson_p), 3), "\n")
cat("Proportion p < 0.05: ", round(mean(rep_df$significant), 3), "\n")

##############################
# 10. Interpretation notes
##############################

cat("
INTERPRETATION NOTES

1. Correlation is not the same thing as slope.


A shallow slope can still have very high correlation if the points lie tightly around a line.

2. Increasing spread around the line usually lowers the magnitude of Pearson correlation.

3. P-values depend on both:


- the strength of the observed association
- the sample size

4. A moderate correlation may be non-significant when n is small,


but highly significant when n is large.

5. Pearson correlation measures linear association.


It can miss strong nonlinear structure.
6. Spearman correlation is often useful when the relationship is monotonic
but not strictly linear.
\n")

Das könnte Ihnen auch gefallen