Modelle spezifizieren.
In dieser Studie wurden 17 Patienten mit Schizophrenie und 24 gesunde Kontrollprobanden untersucht. Die Aufgabe bestand aus einer Arbeitsgedächtnisaufgabe. Den Probanden wurden Wörter mit verschiedenen Farben präsentiert. Sie mussten sich die Wörter einer bestimmten Farbe merken.
Nach einer kurzen Pause sahen die Probanden ein Wort. Falls dieses in der vorherigen Präsentation die gewünschte Farbe gehabt hatte, sollten sie Ja drücken, sonst Nein. Interessiert waren die Forscher in die Reaktionszeit der richtigen Antworten.
Die Daten sind inspiriert von der Stude von Smith et al. (2011). In diesem Studie gingen die Forscher davon aus, dass die 17 Patienten eine verminderte Arbeitsgedächtnisleistung gegenüber den Kontrollprobanden haben würden. Sie interessieren sich aber nur dafür, ob sich die beiden Gruppen in der Antwortgeschwindigkeit unterscheiden.
Die Variablen im Datensatz sehen wie folgt aus:
id: Versuchspersonennummer
rt: mittlere Reaktionszeiten der richtigen Antworten in Millisekunden
group: Gruppenzugehörigkeit: entweder «schizophrenia» oder «control»
d1 <- read_csv("https://raw.githubusercontent.com/kogpsy/neuroscicomplab/main/data/schizophrenia-wm.csv")
glimpse(d1)
Rows: 41
Columns: 2
$ rt <dbl> 1261.5479, 1141.5464, 976.5514, 1222.1543, 1753.7832, …
$ group <chr> "schizophrenia", "schizophrenia", "schizophrenia", "sc…
Spezifieren Sie nun ein lineares Modell, mit dem Sie den Unterschied zwischen den Patienten und den Kontrollprobanden untersuchen können.
Spezifieren Sie zudem ein geeignetes Nullmodell.
Wir können das Modell entweder mit oder ohne Achsenabschnitt schätzen. Wenn die group
Variable dummy-kodiert ist, was die default Kodierung in R ist, dann erhalten wir ohne Achsenabschnitt (rt ~ 0 + group
) eine Schätzung der beiden Gruppenmittelwerte, während wir mit Achsenabschnitt (rt ~ 1 + group
oder abgekürzt rt ~ group
) den Achsenabschnitt, (Intercept)
, und einen additiven Term für die nicht-Referenzkategorie erhalten.
formula1 <- rt ~ group
formula2 <- rt ~ 0 + group
Wir können die mit der Funktion model.matrix()
überprüfen:
head(model.matrix(rt ~ group, data = d1))
(Intercept) groupschizophrenia
1 1 1
2 1 1
3 1 1
4 1 1
5 1 1
6 1 1
Der (Intercept)
Term ist immer 1, während der Term groupschizophrenia
nur den Wert 1 annimmt, wenn die Person in der schizophrenia
Gruppe ist, und 0 wenn die Person in der Kontrollgruppe ist. Dies führt dazu, dass groupschizophrenia
eine Schätzung für den Unterschied zwischen den Gruppen darstellt, oder anders ausgedrückt: die erwartete Änderung im Mittelwert der abhängigen Variablen wenn wir die Gruppenzugehörigkeit um eine Einheit erhöhen (von 0 auf 1 ändern).
Die Prior Verteilungen für die beiden Formelm sehen so aus:
get_prior(formula1, data = d1)
prior class coef group resp
(flat) b
(flat) b groupschizophrenia
student_t(3, 1261.5, 234.7) Intercept
student_t(3, 0, 234.7) sigma
dpar nlpar bound source
default
(vectorized)
default
default
get_prior(formula2, data = d1)
prior class coef group resp dpar
(flat) b
(flat) b groupcontrol
(flat) b groupschizophrenia
student_t(3, 0, 234.7) sigma
nlpar bound source
default
(vectorized)
(vectorized)
default
Wir entscheiden uns hier für die Variante mit dem Achsenabschnitt. Anstelle des flachen Priors für den Regressionskoeffizienten groupschizophrenia
nehmen wir einen normalverteilten Prior mit Mittelwert 0, und einer Standardabweichung von 100 Millisekunden.
summary(m1)
Family: gaussian
Links: mu = identity; sigma = identity
Formula: rt ~ group
Data: d1 (Number of observations: 41)
Samples: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
total post-warmup samples = 4000
Population-Level Effects:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
Intercept 1311.20 52.37 1208.20 1413.98 1.00 3600
groupschizophrenia 28.31 67.16 -106.02 156.77 1.00 3633
Tail_ESS
Intercept 2791
groupschizophrenia 2924
Family Specific Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 297.67 32.88 241.19 369.27 1.00 3728 2812
Samples were drawn using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
Im Output erhalten nun zwei Population-Level Effects
, Intercept
und groupschizophrenia
, und einen Family Specific Parameter
, die Standardabweichung sigma
.
Nochmal zur Wiederholung: das statistische Modell, das wir hier verwenden, lautet:
\[ y_{i} \sim \mathcal{N}(\beta_0 + \beta_1 \cdot \text{groupschizophrenia}, \sigma^2) \] groupschizophrenia
ist eine Indikatorvariable mit den Werten 0 und 1.
Im entsprechenden Nullmodell müssen wir die Gruppierungsvariable weglassen. Dies bedeutet, dass wir ein Modell mit nur einem Achsenabschnitt schätzen (2 Parameter insgesamt), so dass der Achsenabschnitt den Gesamtmittelwert darstellt. Mit dem Nullmodell behaupten wir also, dass der Unterschied zwischen den Gruppen 0 ist.
m1_null <- brm(rt ~ 1,
data = d1,
file = "models/solm1_null")
In dieser Studie wurden 34 ältere Personen mit anodaler (aktivierender) tDCS über dem linken temporoparietalen Kortex (TPJ) stimuliert. Die Probanden wurden instruiert, Assoziationen zwischen Bildern und Pseudowörtern zu lernen (angelehnt an (Antonenko et al. 2019)).
Die episodische Gedächtnisleistung wurde so anhand von %-korrekt erinnerten Wortpaaren erfasst. Ausserdem haben Sie die RT für korrekte Antworten.
Es gab 5 Durchgänge mit TPJ Stimulation und 5 Durchgänge mit Kontrollstimulation.
Die Variablen im Datensatz sehen wie folgt aus:
subject: Versuchspersonennummer
stimulation: TPJ oder control
block: Durchgang
age: Alter
correct: Anteil richtiger Antworten pro Durchgang (5 pro Bedingung, insgesamt 10 pro Person)
rt: mittlere Reaktionszeiten für korrekte Antworten
Sie interessieren sich dafür, ob die Personen während der tDCS Stimulation bessere Gedächtnisleistungen erbringen als in der Kontrollbedingung.
d2 <- read_csv("https://raw.githubusercontent.com/kogpsy/neuroscicomplab/main/data/tdcs-tpj.csv")
glimpse(d2)
Rows: 340
Columns: 6
$ subject <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, …
$ stimulation <chr> "control", "control", "control", "control", "con…
$ block <dbl> 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, …
$ age <dbl> 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 67, 67, …
$ correct <dbl> 0.8, 0.4, 0.4, 0.4, 0.6, 0.4, 0.4, 0.6, 0.6, 0.8…
$ rt <dbl> 1.650923, 1.537246, 1.235620, 1.671189, 1.408333…
d2 <- d2 %>%
mutate(across(c(subject, stimulation, block), ~as_factor(.))) %>%
drop_na()
Spezifieren Sie nun ein lineares Modell, mit dem Sie den Unterschied zwischen der tDCS Stimulation und den Kontrollbedingung untersuchen können.
Spezifieren Sie zudem ein geeignetes Nullmodell.
Versuchen Sie, für das Alter der Personen zu kontrollieren.
Wir wählen wieder die Kontrollgruppe als Referenzkategorie. Dies sollte schon der Fall sein, da die Reihenfolge der Faktorstufen per Default alphabetisch ist.
levels(d2$stimulation)
[1] "control" "TPJ"
Diesmal wählen wir wieder die Variante mit Achsenabschnitt, obwohl wir auch hier die Mittelwerte der beiden Stimulationarten schätzen könnten. Zusätzlich muss nun berücksichtigt werden, dass die Art der Stimulation eine within Manipulation ist—jede Person wird in beiden Bedingungen getestet. Aufgrund unserer Formel 1 + stimulation
repräsentiert der Achsenabschnitt die Kontrollbedingung, und der Koeffizient stimulation
repräsentiert den Unterschied zwischen den Bedingungen. Wir wollen als für beide Bedingungen sowohl den Mittelwert über alle Personen (population-level effect), als auch eine Abweichung (varying effect oder group-level effect) für jede Person. Dies erreichen wir mit mit dem Zusatz (1 + stimulation | subject)
.
Wir haben ein solches Modell in der vorletzten Sitzung als Partial Pooling Modell kennengelernt. Pooling heisst hier, dass wir eine Hierarchie einführen, so dass die Abweichungsterme der Personen aus einer Verteilungen kommen, mit einem Mittelwert von 0 (da es Abweichungen sind), und einer Standardabweichung (group-level SD). Dies wird hier für beide Bedingungen geschätzt. Zusätzlich gehen wir davon aus, dass die Abweichungen einer Person in den beiden Bedingungen korreliert sind. Deshalb werden die Abweichungsterme als multivariat normalverteilt modelliert, und die Korrelation zwischen dem Intercept
und dem stimulation
Effekt wird auch geschätzt.
formula3 <- rt ~ 1 + stimulation + (1 + stimulation | subject)
get_prior(formula3, data = d2)
prior class coef group resp dpar
(flat) b
(flat) b stimulationTPJ
lkj(1) cor
lkj(1) cor subject
student_t(3, 1.6, 2.5) Intercept
student_t(3, 0, 2.5) sd
student_t(3, 0, 2.5) sd subject
student_t(3, 0, 2.5) sd Intercept subject
student_t(3, 0, 2.5) sd stimulationTPJ subject
student_t(3, 0, 2.5) sigma
nlpar bound source
default
(vectorized)
default
(vectorized)
default
default
(vectorized)
(vectorized)
(vectorized)
default
Die Korrelation zwischen Intercept
und stimulationTPJ
taucht bei den Priors als lkj(1)
Prior auf. Die LKJ Verteilung ist eine Verteilung von Korrelationskoeffizienten, welche zwischen -1 und 1 liegen können. Der Parameter \(\eta\) der LKJ Verteilung gibt an, wie stark die Korrelationen sind. Für \(\eta>0\) erwarten wir niedrige Korrelationen, für \(\eta<0\) eher stärkere Korrelationen.
Wenn wir zusätzlich für das Alter der Personen kontrollieren möchten, dann nehmen wir auch die Variable age
als Prädiktor ins Modell.
formula4 <- rt ~ 1 + stimulation + age + (1 + stimulation | subject)
get_prior(formula4, data = d2)
prior class coef group resp dpar
(flat) b
(flat) b age
(flat) b stimulationTPJ
lkj(1) cor
lkj(1) cor subject
student_t(3, 1.6, 2.5) Intercept
student_t(3, 0, 2.5) sd
student_t(3, 0, 2.5) sd subject
student_t(3, 0, 2.5) sd Intercept subject
student_t(3, 0, 2.5) sd stimulationTPJ subject
student_t(3, 0, 2.5) sigma
nlpar bound source
default
(vectorized)
(vectorized)
default
(vectorized)
default
default
(vectorized)
(vectorized)
(vectorized)
default
Wir nehmen hier die Default Priors für alle Parameter ausser dem Unterschied zwischen den Bedingungen. Dafür nehmen wir einen normalverteilten Prior mit Erwartungswert 0 und einer Standardabweichung von 1.
Diese Parameter finden wir alle im Output: sd(Intercept)
, sd(stimulationTPJ
und cor(Intercept,stimulationTPJ)
sind die Standardabweichungen und Korrelationen zwischen den Group-Level Effects
, und der Achsenabschnitt, stimulationTPJ
und age
sind die mittleren Effekte, unabhängig von den Personen.
summary(m2)
Family: gaussian
Links: mu = identity; sigma = identity
Formula: rt ~ 1 + stimulation + age + (1 + stimulation | subject)
Data: d2 (Number of observations: 315)
Samples: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
total post-warmup samples = 4000
Group-Level Effects:
~subject (Number of levels: 34)
Estimate Est.Error l-95% CI u-95% CI
sd(Intercept) 0.07 0.05 0.00 0.20
sd(stimulationTPJ) 0.11 0.08 0.00 0.31
cor(Intercept,stimulationTPJ) -0.44 0.55 -0.99 0.86
Rhat Bulk_ESS Tail_ESS
sd(Intercept) 1.00 784 1976
sd(stimulationTPJ) 1.00 683 1616
cor(Intercept,stimulationTPJ) 1.00 952 2581
Population-Level Effects:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
Intercept 1.87 0.60 0.69 3.06 1.00 4865
stimulationTPJ -0.04 0.06 -0.16 0.08 1.00 5783
age -0.00 0.01 -0.02 0.02 1.00 4863
Tail_ESS
Intercept 2515
stimulationTPJ 3192
age 2216
Family Specific Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 0.51 0.02 0.47 0.55 1.00 4325 2787
Samples were drawn using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
Ein Nullmodell könnte hier das Modell ohne die Population und Group Level Effects von stimulation
sein.
Eine etwas einfachere, aber auch weniger elegante Methode, wäre, zuerst die mittleren RTs für jede Person in den beiden Bedingungen zu berechnen, und dann den Unterschied zwischen den Bedingungen.
d2sum <- d2 %>%
group_by(subject, stimulation) %>%
summarise(rt = mean(rt, na.rm = TRUE)) %>%
pivot_wider(names_from = "stimulation",
values_from = "rt") %>%
mutate(diff = control - TPJ)
Damit könnte man einen frequentistischen t-Test machen, oder ein lineares Modell mit Bayesianischer Schätzung, mit folgender Formel: diff ~ 1 + (1|subject)
.
In einem Experiment von Ganis and Kievit (2015) mussten 54 Vpn entscheiden ob zwei 3-D Stimuli, welche zuneinander um 0, 50, 100, oder 150 Grad rotiert waren, identisch oder nicht waren.
id: Versuchspersonennummer
angle: Rotationswinkel
rt: Reaktionszeiten
accuracy: Indikator für korrekte Antwort
Sie interessieren sich dafür, ob die Personen bei steigendem Rotationswinkel länger brauchen, um eine korrekte Antwort zu geben, und ob die Personen bei steigendem Rotationswinkel mehr Fehler machen.
d3 <- read_csv("https://raw.githubusercontent.com/kogpsy/neuroscicomplab/main/data/mental-rotation.csv")
Zuerst fassen wir die Rohdaten zusammen:
d3sum <- acc %>%
left_join(rts)
Spezifieren Sie ein lineares Modell, mit dem Sie den Zusamenhang zwischen Rotationswinkel und RT untersuchen können.
Spezifieren Sie ein lineares Modell, mit dem Sie den Zusamenhang zwischen Rotationswinkel und Fehlerrate untersuchen können.
Falls Sie das zu kompliziert finden, können Sie sich auf nur 2 der 5 Rotationswinkel konzentrieren, z.B. 50 und 150.
Wir untersuchen den Zusamenhang zwischen Rotationswinkel und rt. Wir können hier annehmen, dass die RT sich als lineare Funktion des Rotationswinkels ändert, oder wir können den Winkel als Faktor benutzen. Zuerst entscheiden wir uns hier für die erste Variante, aber wir definieren schon mal einen neuen Faktor, rot
.
d3sum <- d3sum %>%
mutate(rot = as_factor(angle))
formula5 <- rt ~ angle + (angle | id)
Mit dieser Formel erhalten wir einen Achsenabschnitt und einen Koeffizienten für den linearen Effekt des Winkels.
get_prior(formula5,
data = d3sum)
prior class coef group resp dpar
(flat) b
(flat) b angle
lkj(1) cor
lkj(1) cor id
student_t(3, 2630.3, 955.7) Intercept
student_t(3, 0, 955.7) sd
student_t(3, 0, 955.7) sd id
student_t(3, 0, 955.7) sd angle id
student_t(3, 0, 955.7) sd Intercept id
student_t(3, 0, 955.7) sigma
nlpar bound source
default
(vectorized)
default
(vectorized)
default
default
(vectorized)
(vectorized)
(vectorized)
default
Auch hier haben wir Priors für die Standardabweichungen und Korrelationen der Group Level Effects
. Wir schätzen das Modell diesmal mit default Priors.
m4 <- brm(formula5,
data = d3sum,
file = "models/sol3m4",
file_refit = "on_change")
summary(m4)
Family: gaussian
Links: mu = identity; sigma = identity
Formula: rt ~ angle + (angle | id)
Data: d3sum (Number of observations: 216)
Samples: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
total post-warmup samples = 4000
Group-Level Effects:
~id (Number of levels: 54)
Estimate Est.Error l-95% CI u-95% CI Rhat
sd(Intercept) 591.29 67.22 472.94 737.01 1.00
sd(angle) 3.06 0.56 1.97 4.21 1.00
cor(Intercept,angle) 0.32 0.21 -0.09 0.75 1.00
Bulk_ESS Tail_ESS
sd(Intercept) 1569 2467
sd(angle) 1161 1479
cor(Intercept,angle) 1234 1148
Population-Level Effects:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept 2011.84 87.62 1839.35 2182.93 1.00 948 1464
angle 9.37 0.54 8.30 10.43 1.00 2600 3391
Family Specific Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 282.75 20.05 246.96 325.52 1.00 1422 2054
Samples were drawn using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
In diesem Modell ist der Achsenabschnitt nun die erwartete RT bei einem Winkel von 0 Grad, und der Koeffizient von angle
ist die Änderung in der erwarteten RT, wenn wir angle
um eine Einheit erhöhen, d.h. jede Änderung um ein Grad resultiert in einer um ca. 9 Millisekunden längeren RT.
Das zweite Modell lässt sich so spezifizieren:
formula6 <- rt ~ rot + (rot | id)
Mit dieser Formel erhalten wir einen Achsenabschnitt, welcher die Referenzkategorie (Winkel von 0 Grad) repräsentiert, und Differenzen zu 0 Grad für die anderen Winkel.
Ich bevorzuge hier Reaktionszeiten in Sekunden anstatt Millisekunden, da es einfacher ist Priors für die Unterschiedskoeffizienten zu definieren.
d3sum <- d3sum %>%
mutate(rt = rt/1000)
get_prior(formula6,
data = d3sum)
prior class coef group resp dpar nlpar
(flat) b
(flat) b rot100
(flat) b rot150
(flat) b rot50
lkj(1) cor
lkj(1) cor id
student_t(3, 2.6, 2.5) Intercept
student_t(3, 0, 2.5) sd
student_t(3, 0, 2.5) sd id
student_t(3, 0, 2.5) sd Intercept id
student_t(3, 0, 2.5) sd rot100 id
student_t(3, 0, 2.5) sd rot150 id
student_t(3, 0, 2.5) sd rot50 id
student_t(3, 0, 2.5) sigma
bound source
default
(vectorized)
(vectorized)
(vectorized)
default
(vectorized)
default
default
(vectorized)
(vectorized)
(vectorized)
(vectorized)
(vectorized)
default
m5 <- brm(formula6,
prior = prior(normal(0, 2), class = b),
data = d3sum,
file = "models/sol3m5",
file_refit = "on_change",
cores = parallel::detectCores(),
control = list(adapt_delta = 0.95))
summary(m5)
Family: gaussian
Links: mu = identity; sigma = identity
Formula: rt ~ rot + (rot | id)
Data: d3sum (Number of observations: 216)
Samples: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
total post-warmup samples = 4000
Group-Level Effects:
~id (Number of levels: 54)
Estimate Est.Error l-95% CI u-95% CI Rhat
sd(Intercept) 0.57 0.06 0.46 0.72 1.01
sd(rot50) 0.30 0.06 0.18 0.42 1.07
sd(rot100) 0.49 0.07 0.36 0.64 1.04
sd(rot150) 0.52 0.07 0.39 0.68 1.04
cor(Intercept,rot50) 0.46 0.20 0.08 0.83 1.04
cor(Intercept,rot100) 0.23 0.16 -0.09 0.54 1.02
cor(rot50,rot100) 0.72 0.11 0.47 0.91 1.01
cor(Intercept,rot150) 0.05 0.16 -0.26 0.36 1.01
cor(rot50,rot150) 0.66 0.13 0.37 0.86 1.00
cor(rot100,rot150) 0.88 0.06 0.76 0.97 1.02
Bulk_ESS Tail_ESS
sd(Intercept) 494 444
sd(rot50) 52 129
sd(rot100) 83 225
sd(rot150) 86 227
cor(Intercept,rot50) 74 863
cor(Intercept,rot100) 288 1345
cor(rot50,rot100) 488 1145
cor(Intercept,rot150) 443 1739
cor(rot50,rot150) 624 1238
cor(rot100,rot150) 195 1257
Population-Level Effects:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept 1.93 0.08 1.78 2.08 1.01 367 617
rot50 0.58 0.05 0.48 0.69 1.00 670 433
rot100 1.17 0.07 1.03 1.31 1.01 811 1578
rot150 1.35 0.08 1.20 1.51 1.01 877 1123
Family Specific Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 0.15 0.05 0.05 0.22 1.14 30 52
Samples were drawn using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
Dieses Modell scheint schlecht spezifiziert zu sein, da wir Warnungen erhalten. Unter anderem, dass einige der \(\hat{R}\)-Werte zu gross sind. Das bedeutet, wir müssten bei dem Versuch, diesen Datensatz zu modellieren, sehr vorsichtig sein.
If you see mistakes or want to suggest changes, please create an issue on the source repository.
Text and figures are licensed under Creative Commons Attribution CC BY 4.0. Source code is available at https://github.com/kogpsy/neuroscicomplab, unless otherwise noted. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".
For attribution, please cite this work as
Ellis (2021, June 22). neuroscicomplab: Übung 3: Lösung. Retrieved from https://kogpsy.github.io/neuroscicomplab/solution-3.html
BibTeX citation
@misc{ellis2021übung, author = {Ellis, Andrew}, title = {neuroscicomplab: Übung 3: Lösung}, url = {https://kogpsy.github.io/neuroscicomplab/solution-3.html}, year = {2021} }