Erstellung von APA-konformen Tabellen

Export mit apaTables

Wie am Anfang gesagt wollen wir möglichst vermeiden, Tabellen und Daten a) händisch zu übertragen und b) zu formatieren.

Im besten Fall exportieren wir also alles was an Zahlen und Tabellen für den Text anfällt direkt in files, die wir einfach einbinden können.

Für die ANOVAs, Regressionen, t-Tests und Korrelationsanalysen gibt es im apaTables-Paket fertige Wrapper, die einen direkten Export der Tabellen ins RTF-Format umsetzen.

Unter den folgenden Links findet Ihr die Dokumentation der einzelnen Funktionen aufgelistet:

Außerdem sind im Tutorial Beispiele für alle implementierten Verfahren und Tabellen zu finden.

Wir können zum Beispiel mit ezAnova eine Varianzanalyse für unsere Pinguine durchführen, bei der wir Pinguin-Geschlecht und Spezies als UVs und die Flossenlänge als AV nutzen:

library(ez)

palmerpenguins::penguins %>% 
  mutate(id = seq_along(species)) %>% 
  filter(!is.na(flipper_length_mm)) %>% 
  ezANOVA(wid = id,
          between = .(species, sex),
          dv = flipper_length_mm,
          detailed = T,
          type = 2)
Warning: Converting "id" to factor for ANOVA.
Warning: You have removed one or more levels from variable "sex". Refactoring
for ANOVA.
Warning: Data is unbalanced (unequal N per group). Make sure you specified a
well-considered value for the type argument to ezANOVA().
Coefficient covariances computed by hccm()
$ANOVA
       Effect DFn DFd        SSn      SSd          F             p p<.05
1     species   2 327 50185.0266 10458.11 784.582911 1.569568e-125     *
2         sex   1 327  3905.6038 10458.11 122.118894  2.461150e-24     *
3 species:sex   2 327   329.0425 10458.11   5.144186  6.314424e-03     *
         ges
1 0.82754673
2 0.27190772
3 0.03050319

$`Levene's Test for Homogeneity of Variance`
  DFn DFd      SSn    SSd        F          p p<.05
1   5 327 141.5881 3845.9 2.407723 0.03652301     *

Mit apa.ezANOVA.table können gewünschte Tabellen dann exportiert werden. Laut der Doku ist dabei noch wichtig, mit options eine Anzahl an Dezimalstellen vor Umwandlung in 10-er-Potenz-Notation zu setzen, die mindestens 10 ist.

library(apaTables)
options(digits = 10)

palmerpenguins::penguins %>%
  mutate(id = seq_along(species)) %>%
  filter(!is.na(flipper_length_mm)) %>%
  ezANOVA(
    wid = id,
    between = .(species, sex),
    dv = flipper_length_mm,
    detailed = T,
    type = 2
  ) %>%
  apa.ezANOVA.table(drink_attitude_results,
                    table.number = 1,
                    filename = "Table1_APA.doc")
Warning: Converting "id" to factor for ANOVA.
Warning: You have removed one or more levels from variable "sex". Refactoring
for ANOVA.
Warning: Data is unbalanced (unequal N per group). Make sure you specified a
well-considered value for the type argument to ezANOVA().
Coefficient covariances computed by hccm()


Table 1 

ANOVA results
 

     Predictor df_num df_den   SS_num   SS_den      F    p ges
       species      2    327 50185.03 10458.11 784.58 .000 .83
           sex      1    327  3905.60 10458.11 122.12 .000 .27
 species x sex      2    327   329.04 10458.11   5.14 .006 .03

Note. df_num indicates degrees of freedom numerator. df_den indicates degrees of freedom denominator. 
SS_num indicates sum of squares numerator. SS_den indicates sum of squares denominator. 
ges indicates generalized eta-squared.
 

Das table_number-Argument setzt dabei nur die Zahl in der Tabellen-Überschrift.

Das Ergebnis ist in Abbildung 9.1 zu sehen.

Abb 9.1: Output von apa.ezANOVA.table

Das ist natürlich schon schön und praktisch wenn wir uns englische Tabellen für die implementierten Analysen ausgeben lassen wollen. Aber wie können wir eigene Tabellen nach APA-Richtlinien-konform exportieren?

Tabellen mit flextable

Was sind die Anforderungen an Tabellen laut APA? Konsultieren wir nochmal Julias1 Folien (Abbildung 9.2).

Abb 9.2: Checklist für Tabellen aus Julias Folien

Unsere Tabellen müssen also die folgenden Anforderungen erfüllen:

  1. Jede Spalte muss eine Überschrift haben
  2. die Überschriften müssen zentriert sein

Außerdem kommen noch die folgenden Anforderungen an die Formatierung statistischer Ergebnisse2 hinzu:

  1. Namen statistischer Kennwerte sollen kursiv sein
  2. Zahlen sollen auf den Wert gerundet werden, bei dem Präzision erhalten wird
  3. Werte die nicht größer als 1 werden können sollen keine Null vor dem Komma haben

Fangen wir mit der Formatierung der Nummern an. Als Beispiel haben wir die folgende Tabelle, in der die mittlere Schnabellänge und Standardabweichung pro Pinguin-Spezies und Beobachtungsort abgetragen sind:

summary_table <- palmerpenguins::penguins %>%
  group_by(species, island) %>%
  summarise(across(matches("bill_length_mm"),
                   .fns = list(
                     M = \(x) mean(x, na.rm = T),
                     SD = \(x) sd(x, na.rm = T)
                   ),
                   .names = "{.col}_{.fn}")) %>% # Damit Funktion hinten steht
  pivot_wider(names_from = 'island',
              values_from = 3:4,
              names_glue = "{island}_{.value}") # Damit Insel vorne steht
`summarise()` has grouped output by 'species'. You can override using the
`.groups` argument.
summary_table
# A tibble: 3 × 7
# Groups:   species [3]
  species   Biscoe_bill_length_m…¹ Dream_bill_length_mm_M Torgersen_bill_lengt…²
  <fct>                      <dbl>                  <dbl>                  <dbl>
1 Adelie                      39.0                   38.5                   39.0
2 Chinstrap                   NA                     48.8                   NA  
3 Gentoo                      47.5                   NA                     NA  
# ℹ abbreviated names: ¹​Biscoe_bill_length_mm_M, ²​Torgersen_bill_length_mm_M
# ℹ 3 more variables: Biscoe_bill_length_mm_SD <dbl>,
#   Dream_bill_length_mm_SD <dbl>, Torgersen_bill_length_mm_SD <dbl>

Zuerstmal sortieren wir die Spalten so, dass Pro Ort erst Mittelwert, dann SD steht:

summary_table %>% 
  select(species, matches('Bis'), matches('Dre'), matches('Tor'))
# A tibble: 3 × 7
# Groups:   species [3]
  species   Biscoe_bill_length_m…¹ Biscoe_bill_length_m…² Dream_bill_length_mm_M
  <fct>                      <dbl>                  <dbl>                  <dbl>
1 Adelie                      39.0                   2.48                   38.5
2 Chinstrap                   NA                    NA                      48.8
3 Gentoo                      47.5                   3.08                   NA  
# ℹ abbreviated names: ¹​Biscoe_bill_length_mm_M, ²​Biscoe_bill_length_mm_SD
# ℹ 3 more variables: Dream_bill_length_mm_SD <dbl>,
#   Torgersen_bill_length_mm_M <dbl>, Torgersen_bill_length_mm_SD <dbl>

Dann legen wir die Dezimalstellen auf eine Nachkommastelle fest:

summary_table %>% 
  select(species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1)))
# A tibble: 3 × 7
# Groups:   species [3]
  species   Biscoe_bill_length_m…¹ Biscoe_bill_length_m…² Dream_bill_length_mm_M
  <fct>                      <dbl>                  <dbl>                  <dbl>
1 Adelie                      39                      2.5                   38.5
2 Chinstrap                   NA                     NA                     48.8
3 Gentoo                      47.5                    3.1                   NA  
# ℹ abbreviated names: ¹​Biscoe_bill_length_mm_M, ²​Biscoe_bill_length_mm_SD
# ℹ 3 more variables: Dream_bill_length_mm_SD <dbl>,
#   Torgersen_bill_length_mm_M <dbl>, Torgersen_bill_length_mm_SD <dbl>

Und schon können wir an die eigentliche Formatierung in einer Tabelle gehen. Dazu nutzen wir das Paket flextable.

Wir können unsere Tabelle direkt in flextable pipen:

library(flextable)

Attaching package: 'flextable'
The following object is masked from 'package:purrr':

    compose
summary_table %>% 
  select(species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1))) %>% 
  flextable()

species

Biscoe_bill_length_mm_M

Biscoe_bill_length_mm_SD

Dream_bill_length_mm_M

Dream_bill_length_mm_SD

Torgersen_bill_length_mm_M

Torgersen_bill_length_mm_SD

Adelie

39.0

2.5

38.5

2.5

39

3

Chinstrap

48.8

3.3

Gentoo

47.5

3.1

Als erstes können wir den Header trennen:

summary_table %>% 
  select(species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1))) %>% 
  flextable() %>% 
  separate_header()

species

Biscoe

Dream

Torgersen

bill

length

mm

M

SD

M

SD

M

SD

Adelie

39.0

2.5

38.5

2.5

39

3

Chinstrap

48.8

3.3

Gentoo

47.5

3.1

Die bill-length kann weg, am besten entfernen wir die schon vor der Umwandlung in eine flextable:

summary_table %>% 
  select(species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1))) %>% 
  rename_with(.fn = ~str_remove(.,'bill_length_mm')) %>% 
  flextable() %>% 
  separate_header()

species

Biscoe

Dream

Torgersen

M

SD

M

SD

M

SD

Adelie

39.0

2.5

38.5

2.5

39

3

Chinstrap

48.8

3.3

Gentoo

47.5

3.1

“Spezies” können wir auch in deutsch übertiteln:

summary_table %>% 
  select(Spezies = species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1))) %>% 
  rename_with(.fn = ~str_remove(.,'_bill_length_mm')) %>% 
  flextable() %>% 
  separate_header()

Spezies

Biscoe

Dream

Torgersen

M

SD

M

SD

M

SD

Adelie

39.0

2.5

38.5

2.5

39

3

Chinstrap

48.8

3.3

Gentoo

47.5

3.1

Und die statistischen Kennwerte kursiv setzen:

summary_table %>% 
  select(Spezies = species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1))) %>% 
  rename_with(.fn = ~str_remove(.,'_bill_length_mm')) %>% 
  flextable() %>% 
  separate_header() %>% 
  style(i = 2,part = 'header',
        pr_t = fp_text_default(italic = T))

Spezies

Biscoe

Dream

Torgersen

M

SD

M

SD

M

SD

Adelie

39.0

2.5

38.5

2.5

39

3

Chinstrap

48.8

3.3

Gentoo

47.5

3.1

Im flextable-Paket gibt es außerdem die theme_apa-Funktion, die den Text und die Abstände nach APA formatiert:

summary_table %>% 
  select(Spezies = species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1))) %>% 
  rename_with(.fn = ~str_remove(.,'_bill_length_mm')) %>% 
  flextable() %>% 
  separate_header() %>%
  italic(part ="header", i= 2) %>% 
  theme_apa()

Spezies

Biscoe

Dream

Torgersen

M

SD

M

SD

M

SD

Adelie

39.00

2.50

38.50

2.50

39.00

3.00

Chinstrap

48.80

3.30

Gentoo

47.50

3.10

Außerdem können wir Linien unter den Inseln einfügen:

summary_table %>% 
  select(Spezies = species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1))) %>% 
  rename_with(.fn = ~str_remove(.,'_bill_length_mm')) %>% 
  flextable() %>% 
  separate_header() %>%
  italic(part ="header", i= 2) %>% 
  theme_apa() %>% 
  hline(i = 1, j = -1,part = 'header', 
        border= list(width = 0.1, color = "black", style = "solid"))

Spezies

Biscoe

Dream

Torgersen

M

SD

M

SD

M

SD

Adelie

39.00

2.50

38.50

2.50

39.00

3.00

Chinstrap

48.80

3.30

Gentoo

47.50

3.10

Und abschließend exportieren:

summary_table %>% 
  select(Spezies = species, matches('Bis'), matches('Dre'), matches('Tor')) %>% 
  mutate(across(where(is.numeric), ~round(., 1))) %>% 
  rename_with(.fn = ~str_remove(.,'_bill_length_mm')) %>% 
  flextable() %>% 
  separate_header() %>%
  italic(part ="header", i= 2) %>% 
  theme_apa() %>% 
  hline(i = 1, j = -1,part = 'header', 
        border= list(width = 0.1, color = "black", style = "solid")) %>% 
  save_as_docx(path = 'flextable_out.docx')

Der Export ist in Abbildung 9.3 zu sehen.

Abb 9.3: Export des flextable-calls.

Aber da muss ich ja die Beschreibung doch noch nachträglich einfügen! Und was soll ich mit 15 einzelnen docs, dann muss ich ja doch alles rüberkopieren!

Natürlich gibt es da auch eine Lösung! Auftritt quarto.

Aufgabe

  1. Erstelle eine Tabelle mit den deskriptiven Kennwerten der Blütenblatt-Länge im iris-Datensatz mit apaTables. Guck Dir dafür die Dokumentation der apa.1way.table-Funktion an.

  2. Baue dieselbe Tabelle mit flextable nach.


  1. Nochmal Danke, Julia!↩︎

  2. laut https://apastyle.apa.org/style-grammar-guidelines/tables-figures/tables und https://www.scribbr.com/apa-style/numbers-and-statistics/↩︎