pairwise.chisq.test <- function(A, B, p.adjust.method="bonferroni"){
# wir überprüfen, ob die Objekte vom Typ factor sind.
if (!is.factor(A) | !is.factor(B)) {
stop("Both objects need to be factors.")
}
# erstelle ein Hilfsdatenframe
df <- data.frame(A=A, B=B)
# Erzeuge alle möglichen 2er-Kombinationen von B
kombi <- combn(levels(B), 2)
# Erzeuge ein Datenframe für die Werterückgabe
# die erste Zeile wird später wieder gelöscht.
result <- data.frame(group1 = "A",
group2 = "B",
xsquare = "9",
df = "9999",
pvalue = "9",
padjust = "9",
adjust = p.adjust.method,
sig = "")
# Berechne für jedes Paar
for (i in 1:(length(kombi)/2)) {
# füge die aktuelle Paarung in einem Datenframe zusammen
help <- rbind(df[df$B==kombi[,i][1],],
df[df$B==kombi[,i][2],])
# führe den Chi-Quadrat-Test durch
pups <- chisq.test(help$A, help$B)
# setze das Signifikanz-Signal zurück
sig = ""
# setze einen Punkt wenn p < 0.055
if(p.adjust(as.numeric(pups$p.value), method = p.adjust.method, n=(length(kombi)/2)) < 0.055) {sig="."}
# setze einen Stern wenn p < 0.05
if(p.adjust(as.numeric(pups$p.value), method = p.adjust.method, n=(length(kombi)/2)) < 0.05) {sig="*"}
# setze zwei Sterne wenn p < 0.01
if(p.adjust(as.numeric(pups$p.value), method = p.adjust.method, n=(length(kombi)/2)) < 0.01) {sig="**"}
# setze drei Sterne wenn p < 0.001
if(p.adjust(as.numeric(pups$p.value), method = p.adjust.method, n=(length(kombi)/2)) < 0.001) {sig="***"}
# speichere die Ergebnisse in ein Datenframe
loop <- data.frame(group1 = kombi[,i][1],
group2 = kombi[,i][2],
xsquare = as.numeric(pups$statistic),
df = as.numeric(pups$parameter),
pvalue = as.numeric(pups$p.value),
padjust = p.adjust(as.numeric(pups$p.value), method = p.adjust.method, n=(length(kombi)/2)),
adjust = p.adjust.method,
sig = sig
)
# füge die aktuellen Ergebnisse dem "result"-Objekt hinzu
result <- rbind(result, loop)
}
# Fertig mit allen Paarungen
# Entferne den Dummy-Eintrag
result <- result[-1,]
# Korrigiere die Zeilennummerierung
row.names(result) <- NULL
# Gebe die Ergebnisse zurück
return(result)
}
#----------------------------------------------------------------------#
In R ist keine Funktion implementiert, die einen paarweisen Chiquadrat-Test durchführt. Das mag daran liegen, dass der Chiquadrat-Test als (weniger genaue) Alternative zu dem rechenaufwändigen exakten Fisher-Test entwickelt wurde. Mit der heutigen Computertechnologie ist es jedoch in den meisten Fällen kein Problem, den exakten Test nach Fisher auch auf älterer Hardware anzuwenden, siehe hierzu https://www.produnis.de/R/schliessende-statistik.html#sec-posthocsig.
Paarweise Chiquadrat-Tests
Ich habe aus edukativen Gründen eine Funktion programmiert, die dennoch paarweise Chiquadrat-Tests durchführt.
Die Funktion ist auch in meinem Zusatzpaket jgsbook
enthalten und kann mittels jgsbook::pairwise.chisq.test(x, y)
aufgerufen werden.
Durch die Mehrfachvergleiche (multiples Testen) entsteht das erhöhte Risiko von falsch-positiven Ergebnissen (Typ-I-Fehler). Bei unserer Funktion müssen wir das Problem der Mehrfachtestung berücksichtigen. In Zeile 50 übergeben wir die berechneten p-Werte der p.adjust()
-Funktion, und korrigieren die Werte mit dem gewünschten Verfahren (standardmäßig bonferroni
).
Die Funktion nimmt folgende Parameter entgegen:
A
: die erste Variable, ein Faktor, welche die Zieldaten enthält (z.B.Geschlecht
)B
: die zweite Variable, ein Faktor, welcher die Gruppeneinteilung enthält (z.B.Ort
)p.adjust.method
: die Methode zur Alphafehler-Korrekur, standardmäßig “bonferroni
”, es gehen aber auch “alle anderen”, wie zBholm
oderhochberg
.
Als Ergebnis liefert uns die Funktion ein Datenframe zurück, mit den Einträgen:
- Name von Gruppe 1
- Name von Gruppe 2
- Teststatistik (xsquare)
- Freiheitsgrade (df)
- p-Wert des Chi-Quadrat-Test
- adjustierter p-Wert (nach gewählter Methode)
- die gewählte Methode zur p-Wert-Adjustierung
- Signifikanz-Signal für die adjustierten p-Werte (
.
,*
,**
,***
)
Beispiel
Als Beispiel untersuchen wir im Datensatz pf8
, ob sich die Geschlechter je nach Standort unterscheiden.
# verwende pf8 Datensatz
load(url("https://www.produnis.de/R/data/pf8.RData"))
# führe exakten Test durch
pairwise.chisq.test(pf8$Geschlecht, pf8$Standort)
group1 group2 xsquare df pvalue padjust adjust sig
1 Rheine Münster 1.10471703224467 2 0.57559066836014 1 bonferroni
2 Rheine Bahn 0.484729064039409 2 0.784770051592051 1 bonferroni
3 Rheine Ladbergen 3.82435691920203 2 0.147758150572077 1 bonferroni
4 Rheine Internet 14.0768553245862 2 0.000877505212656076 0.00877505212656076 bonferroni **
5 Münster Bahn 0 1 1 1 bonferroni
6 Münster Ladbergen 3.97980963867552 1 0.0460487671171598 0.460487671171598 bonferroni
7 Münster Internet 8.4878079618588 1 0.00357534323663839 0.0357534323663839 bonferroni *
8 Bahn Ladbergen 2.85957778936492 1 0.0908313368796546 0.908313368796546 bonferroni
9 Bahn Internet 5.76636469124268 1 0.0163357933303218 0.163357933303218 bonferroni
10 Ladbergen Internet 16.0075693112762 1 6.30897415058098e-05 0.000630897415058098 bonferroni ***
Es ist zu erkennen, dass sich die Geschlechterverteilungen zwischen den Standortvergleichen Internet-Rheine
, Internet-Münster
und Internet-Landbergen
unterscheiden.
Vergleich mit Fisher Test
Vergleichen wir die Ergebnisse mit den exakten Fisher-Tests:
Pairwise comparisons using
data: pf8$Geschlecht and pf8$Standort
Rheine Münster Bahn Ladbergen
Münster 1.00000 - - -
Bahn 1.00000 1.00000 - -
Ladbergen 1.00000 0.40161 0.64522 -
Internet 0.00292 0.02852 0.13207 0.00065
P value adjustment method: bonferroni
Es ist erkennbar, dass beide Tests zwar in die selbe Richtung tendieren, und auch die gleichen Signifikanzbewertungen ausgeben. Jedoch sind die p-Werte mit unter deutlich abweichend.
Unsere Funktion ist eine nette Übung. Am Besten ist es aber, immer den exakten Fisher-Test zu rechnen!
Weblinks
- https://www.produnis.de/R/schliessende-statistik.html#sec-posthocsig
- https://cran.r-project.org/web/packages/jgsbook/index.html