source

#Zeugs, das wir irgendwie benötigen
library(tidyverse)
library(ggplot2)

N <- 1e4 # festlegen, wie oft (später) zufällig "Proben" entnommen werden, eine
         # 1 mit 4 Nullen: 10.000

Ziele

Vorgehen

  1. Ein Modell mit guten Parametern aufstellen, das realistische Mittelwerte generiert
  2. Parameter festlegen
    • Mittelwerte1 für reguläre Überquerungszeiten: a
      • regulär, im Sinne von “ohne Manipulation”
    • den Effekt, wie stark starren die Überquerungszeiten verändert: b
  3. Modell auf Plausibilität prüfen, vorzugsweise anhand von Grafiken
    • Ist die mittlere Überquerungszeit zu langsam? Variiert die Überquerungszeit stärker als im Plot zu sehen?
    • Ist die Effektgröße stärker, als wir es erwarten?
  4. u.a. Poweranalyse zur Stichprobenschätzung

1. Modell aufstellen

Wir stellen ein (vereinfachtes) Modell der Wirklichkeit auf.

Vorannahme: Die zeit die Menschen benötigen um eine Ampel zu überqueren unterliegt einer Normalverteilung.

zeit ~ dnorm(mu, sigma)

Eine Normalverteilung wird durch zwei Werte beschrieben, den Mittelwert mu und die Standardabweichung sigma.

Da wir nicht sonderlich daran interessiert sind, wie viel Zeit Menschen zur Überquerung von Ampeln benötigen müssen wir noch unseren Prädiktor das Starren hinzufügen: mu sollte (laut Hypothese) unterschiedlich sein, je nach dem, ob jemand angestarrt wird, oder nicht.

Parameter

Wir gehen von einem linearen Einfluss des Starrens auf mu aus.

mu

Wir nehmen an, dass mu sich aus der durchschnittlichen Zeit zur Überquerung (der Straße) und einem Effekt des Starrens ergibt.

durchschnittliche Zeit zur Überquerung a

Auch hier gehen wir davon aus, dass die Werte einer Normalverteilung entspringen: a ~ dnorm(m, sd)

  • m = Mittelwert der Zeit die Menschen benötigen um die Straße zu überqueren.
    • quasi der Wert, den wir bekommen würden, wenn wir nur die Zeit nehmen, die Menschen benötigen um die Straße zu überqueren.
  • sd = Abweichungen, wie unterschiedlich lange Menschen benötigen, um die Straße zu überqueren.

Frage 1: Wie kommen wir an eine gute Schätzung von m?

Effekt des Starrens b*x

Zu a müssen wir also noch den Effekt des Starrens addieren, und zwar die Veränderung der Zeit durch das Starren: b * x

Starren x

x ist von uns als eine dichotome Variable definiert: 1 = angestarrt werden und 0 = nicht angestarrt werden

  • Frage 2: Warum nicht 1 und 0? Was wären die Konsequenzen?
Veränderung durch Starren b

Grundsätzlich gehen wir auch hier davon aus, dass die Veränderung durch das Starren (“der Effekt”) aus einer Normalverteilung kommt. Dadurch, dass wir aber ausschließlich positiven Einfluss auf die Verkürzung erwarten2 definieren wir b als log-normalverteilt (d.h., der Logarithmus von b ist normalverteilt): b ~ dlnorm(mean, sd).

2. Parameter finden

mu ergibt sich nun also aus einem lineare Model mit dem wir innerhalb unseres Modells berechnen können, wie viel weniger Zeit Menschen benötigen, die angestarrt werden: mu = a + b * x

Jetzt können wir in das Model möglichst realistische Zahlen einsetzen. Beispielsweise, dass Menschen zur Überquerung der Straße im Schnitt 12 Sekunden (mit einer SD von .4 Sekunden) benötigen.


zeit ~ dnorm(mu, sigma)

mu <- a + b * x

a ~ dnorm( 12 , .4 )
b ~ dlnorm( 0 , .02 )

sigma ~ dunif( 0 , 2 ) # Stanardabweichung

Effekt skalieren

effect_size <- -1 # 1 Sekunde weniger Zeit

Bisher zeigt der Term b * x nur die Veränderung von einer Einheit an. Wie groß diese ist, müssen wir uns überlegen, um darauf basierend die nötige Stichprobengröße zu bestimmen, s.u..

Um den Effekt in Sekunden zu berechnen, muss x mit dem erwarteten Effekt multipliziert werden: Bei einem erwarteten Effekt von 5 Sekunden: b * x * 5

3. Plausibilität überprüfen

Wenn unser Modell perfekt die Welt beschreibt, müssen nur noch die wahren Parameter dem Model hinzugefügt werden, und wir bräuchten keine Versuchsperson mehr erheben.

Beides kennen wir nicht, aber wir können unser Model so optimieren, dass die synthetischen Daten die es produziert, möglichst ähnlich den empirisch Daten sind. Dazu vergleichen wir, unsere Parameter mit erwartbaren Werten. Los geht’s!

Plausibilität von a

# mittlere Überquerungsgeschwindigkeit ist 'mean' und variiert um 'sd'
sample_a <- rnorm( N , mean=12 , sd=.4 )

ggplot() +
    aes(x=sample_a) +
    stat_density(
        adjust = .5,
        fill = random_color(),
        alpha = .5) +
    geom_vline(xintercept = mean(sample_a), color = random_color()) +
    theme_minimal()

Frage 3: Sind die Werte für a plausibel, und was bedeutet die Linie?

Plausibilität von b

# mittlerer Effekt 'meanlog', variiert um 'sd_log'
sample_b <- rlnorm( n=N , meanlog=0 , sdlog=.02 )

ggplot() +
    stat_density(
        aes(x=sample_b),
        adjust = .5,
        fill = random_color(),
        alpha = .5) +
    geom_vline(xintercept = mean(sample_b), color = random_color())+
    theme_minimal()

Frage 4: Sind die Werte für b plausibel?

Plausibilität von mu

# Unabhängige Variable
# x = starren beschleunigt in Sekunden
x_0 = 0  # kein starren: kein Effekt
x_1 = 1 # starren: eine Einheit schneller, als sonst

# x_bar = mean(c(x_0, x_1))
# x_bar

mu_1 <- sample_a + sample_b * (x_1 * effect_size) # den Effekt in Sekunden umrechnen
mu_0 <- sample_a + sample_b * (x_0 * effect_size)

avg_mu_sim <- tibble(sample_a, sample_b, mu_1, mu_0)

ggplot(data=avg_mu_sim) +
    stat_density(aes(x=mu_0), fill = random_color(), alpha = .5) +
    geom_vline(xintercept = mean(mu_0), color = random_color()) +
    stat_density(aes(x=mu_1), fill = random_color(), alpha = .5) +
    geom_vline(xintercept = mean(mu_1), color = random_color())+
    theme_minimal()

Plausibilität von sigma

# wird erst später benötigt
sample_sigma <- runif( n=N , min=0 , max=2) # wird erst später relevant

ggplot() +
    stat_density(
        aes(x=sample_sigma),
        adjust = .5,
        fill = random_color(),
        alpha = .5) +
    geom_vline(xintercept = mean(sample_sigma), color = random_color())+
    theme_minimal()

folgt

4. Stichprobengröße bestimmen

Um die Stichprobengröße zu bestimmen haben wir jetzt alles, was wir benötigen.

Nun können wir uns Daten beliebig oft simulieren und statistisch auswerten. Da wir die Wahrheit über unser Model kennen (H1 ist wahr) wissen wir, dass jeder nicht signifikante Test ein Fehler 2. Art ist.

Zuerst legen wir ein paar Parameter für die Simulation fest.

  • Wie viele Experimente wollen wir simulieren?
  • Jede Simulation ist ein Experiment, wie viele Versuchspersonen wollen wir jedes mal erheben?
  • Was ist eine Fehlerquote, die wir akzeptieren würden, fälschlicherweise H1 anzunehmen (Fehler 1. Art)?
  • Was ist eine Fehlerquote, die wir akzeptieren würden, fälschlicherweise H0 anzunehmen (Fehler 2. Art)?
  • Wie groß ist der Effekt, den wir erwarten?

Die Antwort zu diesen Fragen hängt von 4 Faktoren ab und kann in jede Richtung gelöst werden:

  1. Stichprobengröße
  2. Wahrscheinlichkeit Fehler 1. Art (Signifikanzniveau)
  3. Wahrscheinlichkeit Fehler 2. Art (1-Power)
  4. Effektgröße

Hier, in der Simulation, lösen wir zum Fehler 2. Art hin auf. Das heißt, dass wir festlegen, wie viele Versuchspersonen wir erheben, welche Wahrscheinlichkeit wir für den Fehler 1. Art akzeptieren und wie groß der Effekt ist, den wir erwarten. Das Ergebnis der Simulation ist die Wahrscheinlichkeit für einen Fehler 2. Art, gegeben den 3 Faktoren.

Wenn uns das Ergebnis nicht gefällt, müssen wir unsere Voraussetzungen überprüfen.

Parameter für die Simulation

# Anzahl der Simulationen
nsims <- 10000 # hat nur was mit der anzunehmenden Genauigkeit der Power zutun

# 1. Stichprobengröße
nsubjects <- 40

# 2. Signifikanzniveau
type_I_error <- .01 # alpha

# 4. Effektgröße
effect_size <- effect_size # oben schon festgelegt
  • H1: Menschen benötigen -1 Sekunden weniger Zeit die Straße zu überqueren, wenn sie angestarrt werden), wir legen fest, dass wir in 1% der Fälle H1 fälschlicherweise akzeptieren und wir legen fest, dass wir 40 Versuchspersonen erheben.
  • nsims: Für ein Experiment liegt diese quasi bei 1.
    • Daher wollen wir natürlich eine möglichst hohe Power und strenges Signifikanzniveau haben. Wir möchten aber natürlich nicht so viele Versuchspersonen erheben und dabei muss die Effektstärke auch noch realistisch sein.

Simulation

Durch die Simulation können wir nun rumprobieren und entscheiden, was das für unsere Stichprobengröße bedeutet.

# Ein paar Dinge, die wir später benötigen
df_sim_data <- tibble() # Einen Datensatz für alle simulierten Daten
p_values <- tibble() # Einen Datensatz für alle p-Werte
starring <- c(x_0, x_1) # Eine Manipulation, s.o.

for (sim in 1:nsims){

    time_crossing <-
        sapply(
          starring ,
          function(stare)

            rnorm(

                # Wie viele Versuchspersonen sollen gezogen werden?
                n = nsubjects/2,
                # Das lineare Model (s.o.), welches die Zeit abhängig vom Starren vorhersagt
                mean = sample_a + sample_b * stare * effect_size ,
                # Die Varianz, wie unterschiedlich lange Versuchspersonen brauchen
                sd = sample_sigma ) )

    sim_data <-
        tibble(
          sim_n = sim,
          no_stare=time_crossing[,1],
          stare=time_crossing[,2])

    # t-test rechnen
    sim_ttest <-
      t.test(
        sim_data$stare,
        sim_data$no_stare,
        conf.level = 1 - type_I_error, # zu 1 - Fehlerrate wird H_1 korrekt angenommen
        var.equal = TRUE, # per Definition (s.o.)
        paired = FALSE) # keine Messwiederholung)

    # # oder als lineares model
    # sim_data_lm <-  gather(
    #   sim_data, key = "stare", value = "crossing_time", stare:no_stare)
    #
    # sim_lm <-
    #   summary(
    #     lm(crossing_time ~ 1 + stare, data = sim_data_lm))

    # p-werte sammeln
    sim_p_value <-
        tibble(
          sim_n =sim,
          # p_value = sim_lm$coefficients["starestare", "Pr(>|t|)"],
          p_value = sim_ttest$p.value, # nur den p-Wert behalten
          t_value = sim_ttest$statistic,
          critical_t_value = qt(1-type_I_error/2, sim_ttest$parameter))

    df_sim_data <- rbind(df_sim_data, sim_data)
    p_values <- rbind(p_values, sim_p_value)

    }

Ergebnis

In dem Model legen wir einen Unterschied zwischen angestarrt und nicht angestarrt auf -1 Sekunden fest, H1 ist in unserem Model wahr.

  • Aufgrund von (zufälligen) Schwankungen (bspw. durch Messfehler), wird der kritische p-Wert (0.01), nicht immer unterboten.
    • Genauso, wie der p-Wert auch manchmal unterboten wird, obwohl H0 wahr ist, Fehler 1. Art!
    • Die Wahrscheinlichkeit versuchen wir anhand der Simulation herauszufinden.

Wie viele p-Werte sind nicht <0.01?

p_values$below_alpha <- ifelse(p_values$p_value <= type_I_error, 1, 0)

type_II_error <- sum(p_values$below_alpha)/nrow(p_values)

Für unsere Studie, für eine Effektstärke von -1 Sekunden, bei n = 40 Versuchspersonen und einem Signifikanzniveau von 1% erreichen wir eine Power von 43.93%.

Die Wahrscheinlichkeit einen Fehler 2. Art zu begehen, bei einem nicht-signifikanten Ergebnis, liegt mit den oben festgelegt Werten bei 56.07%!

ggplot(data = p_values, aes(x = p_value)) +
    stat_density(
          adjust = .5,
          fill = random_color(),
          alpha = .5) + # anderes alpha!
    geom_vline(
        xintercept = type_I_error,
        color = random_color())+
    theme_minimal()

Frage 5: Was bedeutet das, und ist das akzeptabel?

# # 1000 simulierte Studien
# df_sim_data <-
#     df_sim_data %>%
#     group_by(sim_n) %>%
#     summarise(
#         mean_stare = mean(stare),
#         # sd_stare = sd(stare),
#         mean_no_stare = mean(no_stare),
#         # sd_no_stare = sd(no_stare),
#         effect_size = mean_stare - mean_no_stare) %>%
#     cbind(p_values)
#
# DT::datatable(df_sim_data)

Credit

Inspiriert von McElreath, R. (2018). Statistical rethinking: A Bayesian course with examples in R and Stan. Chapman and Hall/CRC, Kapitel 3 [url].


  1. inkl. Standardabweichung, Voraussetzung um Normalverteilungen zu nutzen (siehe unten)

  2. Achtung: Das ist natürlich abhängig davon, wie wir den Effekt x definieren! Siehe Frage 2. Hier gehen wir davon aus, dass jemand weniger Zeit benötigt. Der Term b * x sollte entsprechend negativ werden, wenn er zu a hinzuaddiert wird wenn gestarrt wird und keinen Einfluss auf a haben, wenn nicht gestarrt wird. Das erreichen wir durch die Kodierung 1:0.