Moderator effects or interaction effect are a frequent topic of scientific endeavor. Put bluntly, such effects respond to the question whether the input variable X (predictor or independent variable IV) has an effect on the output variable (dependent variable DV) Y: “it depends”. More precisely, it depends on a second variable, M (Moderator).

More formally, a moderation effect can be summarized as follows:

If the effect of X on Y depends on M, a moderator effect takes place.

There exist some typical procedures to detect such effects. In this post, however we are concerned only with the visualization of moderation.

To do so, we will distinguish three situations: Where X and M are nominal, where X and M are numeric, and situations where one of them is nominal and the other numeric.

We will look at some visualization methods based on ggplot2 (don’t forget to install upfront if not yet installed). We will use the dataset tips from reshape2. dplyr will be used for data mingling.

library(ggplot2) 
library(dplyr) 
tips <- read.csv("https://sebastiansauer.github.io/data/tips.csv")

IV: numeric, M: nominal

Let’s assume we take total_bill as predictor (X), and sex as moderator; tip is the criterion (outcome).

qplot(x = total_bill, y = tip, facets = ~sex, data = tips) +
  geom_smooth(method = "lm")

plot of chunk unnamed-chunk-2

The data set is split in two facets; a regression line indicates the strength of association in each level of the moderator.

However, as the two lines are not aligned, they are of limited use for visual comparison. Better place the lines in one, single diagram.

qplot(x = total_bill, y = tip, data = tips, color = sex) +
  geom_smooth(method = "lm") 

plot of chunk unnamed-chunk-3

Now we see clearly that there are little differences between the groups, if at all.

Some tweaks:

ggplot(tips) +
  aes(x = total_bill, y = tip, color = sex) +
  geom_point(color = "grey") +
  geom_smooth(method = "lm")

plot of chunk unnamed-chunk-4

Or:

tips_f <- filter(tips, sex == "Female")
tips_m <- filter(tips, sex == "Male")


ggplot(tips) +
  aes(x = total_bill, y = tip, color = sex) +
  geom_point(color = "grey") +
  geom_smooth(method = "lm", data = tips_f) +
   geom_smooth(method = "lm", data = tips_m)

plot of chunk unnamed-chunk-5

IV: nominal, M: nominal

ggplot(tips) +
  aes(x = sex, y = tip) +
  geom_boxplot() +
  facet_wrap(~smoker)

plot of chunk unnamed-chunk-6

The moderator effect can be put in this question here “Is the difference between the sexes of equal size in non-smokers the same as in smokers”? It appears that there is little difference in the differences, hence little indication for a moderator effect.

We can also do the statistical summary ourselves:

tips %>% 
  group_by(sex, smoker) %>% 
  summarise(tip_groups = mean(tip)) -> tips2


tips2 %>% 
  ggplot() +
  aes(x = sex, y = tip_groups, color = smoker) +
  geom_line(aes(group = smoker)) +
  geom_point()

plot of chunk unnamed-chunk-7

This plot argues for some interaction of the two predictors, as the lines are not parallel (in fact they are crossed here).

Similarly, in ggplot2:

tips %>% 
  ggplot() +
  aes(x = sex, color = smoker, group = smoker, y = tip) +
  stat_summary(fun.y = mean, geom = "point") +
  stat_summary(fun.y = mean, geom = "line")

plot of chunk unnamed-chunk-8

IV: metric, M: metric

As the effect of the metric moderator is not straight-forward to plot, it is convenient to discretize the metric moderator. For example, in two groups (median split) or in three (1 sd below the mean, mean, 1 sd above the mean, or in terciles…). Then we proceed as above.

tips$tip_2tile <- ntile(tips$tip, 2)
head(tips$tip_2tile)
## [1] 1 1 2 2 2 2
tips$tip_3tile <- ntile(tips$tip, 3)
head(tips$tip_3tile)
## [1] 1 1 3 3 3 3
x <- tips$tip

tips$tip_3group <-
  case_when(x > mean(x)+sd(x) ~ "high",
            x < mean(x)+sd(x) & x > mean(x)-sd(x) ~ "average",
            x < mean(x)-sd(x) ~ "low")

count(tips, tip_3group)
## # A tibble: 3 × 2
##   tip_3group     n
##        <chr> <int>
## 1    average   182
## 2       high    32
## 3        low    30
tips %>% 
  ggplot() +
  aes(x = total_bill, y = size, group = tip_3group, color = tip_3group) +
  geom_point(color = "grey", alpha = .7) +
    geom_smooth(method = "lm")

plot of chunk unnamed-chunk-9

Debrief

In sum, ggplot2 provides some handy functions for visualizing moderator effects. In addition to traditional regression analyses, such plots can help to better grasp what actually is going on.

Dear editorial team,

Thanks for considering me for review. After some thought-meandering I came to the conclusion that traditional publishers - such as the present publisher of this journal - support a business model that I deem unfair and inappropriate for regular science and for the interests of science and scientists alike. That is, the fees are much too high thereby sucking resources out of the science system and out of society which could be used for the better otherwise.

For that reason I will be more likely to conduct unpaid reviews for non- or low-profit organizations but in general less or not for high-margin publishers. Also, I cannot understand why publishers earn heaps of money whereas we are scientists - reviewers, authors, editors alike - are left with empty pockets. Please note that I am in pricinciple prepared to conduct paid reviews.

Please not take my reluctance personally; it is for the improvement of our field that I take this decision.

Regards Sebastian

Hier finden Sie eine Auswahl an wissenschaftlichen Kongressen in 2017 aus der Wirtschaftspsychologie und angrenzender Felder.

Nationale Kongresse 2017 (in Deutschland)

  • GWPS, 2.-4. März in Darmstadt
    Fachtagung der Gesellschaft für angewandte Wirtschaftspsychologie (GWPs)
    Submission Deadline: 30. Nov 2016

  • TeaP, 26.-29. März in Dresden
    Conference of Experimental Psychologists Submission Deadline: 15. Nov. 2016

  • DiffPsy, 4.-6. September in München
    Arbeitstagung der Fachgruppe Differenzielle Psychologie, Persönlichkeitspsychologie und Psychologische Diagnostik
    Submission Deadline: 9. März 2017

  • Sozpsy 4.-6. Sept. in Ulm
    Tagung der Fachgruppe Sozialpsychologie
    Submission Deadline: 31. Januar 2017

  • MediaPsych2017 6.-8. Sept. in Landau i.d. Pfalz
    Conference of the Media Psychology
    Submission Deadline: 1. März 2017

  • AOW, 13.-15. September in Dresden
    Tagung der Fachgruppe Arbeits-, Organisations- und Wirtschaftspsychologie
    Submission Deadline: ~5. März 2017~ verlängert auf 12. März 2017

  • GesPsy, 17.-19. Sept., Graz
    Kongress der Fachgruppe Gesundheitspsychologie
    Submission Deadline: ?

  • FGME, 17.-20. Sept., Tübingen
    Tagung der Fachgruppe Methoden & Evaluation
    Submission Deadline: 13. April 2017

Internationale Kongresse 2017

  • GfA, 15.-17. Februar, Schweiz
    Gesellschaft für Arbeitswissenschaft in der Schweiz
    Submission Deadline: 10. Okt. 2016

  • BSAC, 3.-5. May, Brighton
    British Psychological Society Annual Conference
    Submission Deadline: 26. Okt. 2016

  • SIOP, 27.-29. April in Orlando, Florida
    32nd Annual Conference of the Society for Industrial and Organizational Psychology
    Submission Deadline: 31. Januar 2017 (Pac. time)

  • EAWOP, 17.-20. Mai in Dublin
    European Association of Work and Organizational Psychology
    Submission Deadline: 30. Sept. 2016

  • PsySci, 25.-28. Mai in Boston, Maine
    29th Annual Convention of the Association for Psychological Science
    Submission Deadline: 1. Dezember 2016

  • ECP2017, 11.-14. Juli in Amsterdam
    European Congress of Psychology
    Submission Deadline: 28. Januar 2017

  • IMPS, 17.-21. Juli in Zürich
    International Meeting of the Psychometric Society
    Submission Deadline: 15. Februar 2017

  • ECMH, 4.-6. Oktober, Berlin
    6th European Conference on Mental Health
    Submission Deadline: 14. Mai 2017

https://www.dgps.de/index.php?id=2001024

https://www.zpid.de/index.php?wahl=events

https://www.zpid.de/redact/category.php?cat=16

http://www.report-psychologie.de/service/termine/kongresse-tagungen/

https://www.hogrefe.de/veranstaltungen/tagungen-und-kongresse

A typical task in data analysis is to import CSV-formatted data. CSV is nothing more than a text file with data in rectangular form; rows stand for observations (eg., persons), and columns represent variables (such as age). Columns are separed by a “separator”, often a comma. Hence the name “CSV” - “comma separeted values”. Note however that the separator can in principle anything you like (eg., “;” or tabulator or “ “).

An easy example for importing a CSV is this

d <- read.csv("https://vincentarelbundock.github.io/Rdatasets/csv/MASS/birthwt.csv")
head(d)
##    X low age lwt race smoke ptl ht ui ftv  bwt
## 1 85   0  19 182    2     0   0  0  1   0 2523
## 2 86   0  33 155    3     0   0  0  0   3 2551
## 3 87   0  20 105    1     1   0  0  0   1 2557
## 4 88   0  21 108    1     1   0  0  1   2 2594
## 5 89   0  18 107    1     1   0  0  1   0 2600
## 6 91   0  21 124    3     0   0  0  0   0 2622

It comes in handy that read.csv is able to address websites out of the box!

Now at times it happens that the CSV is somehow strange. Consider this example:

dd <- read.csv("http://www.stat.ufl.edu/~winner/data/slash_survsex.dat")
head(dd)
##         X1.......1.......1
## 1        1       1       1
## 2        1       1       1
## 3        1       1       1
## 4        1       1       1
## 5        1       1       1
## 6        1       1       1

Hm, how many columns do we have?

ncol(dd)
## [1] 1

One only. Something got lost in translation. We would expect three columns. Now what?

To be honest, I am not really sure what the problem exactly consists of. But that does not stop from finding a solution.

The little at X1......1... appear to indicate blanks (spaces). So let’s try to use a blank as the column separator.

dd <- read.csv("http://www.stat.ufl.edu/~winner/data/slash_survsex.dat", 
              sep = " ")
ncol(dd)
## [1] 22

Yosh! 22 columns, that’s too much of something good… Hm, let’s look at the dataframe.

head(dd)
##    X X.1 X.2 X.3 X.4 X.5 X.6 X1 X.7 X.8 X.9 X.10 X.11 X.12 X1.1 X.13 X.14
## 1 NA  NA  NA  NA  NA  NA  NA  1  NA  NA  NA   NA   NA   NA    1   NA   NA
## 2 NA  NA  NA  NA  NA  NA  NA  1  NA  NA  NA   NA   NA   NA    1   NA   NA
## 3 NA  NA  NA  NA  NA  NA  NA  1  NA  NA  NA   NA   NA   NA    1   NA   NA
## 4 NA  NA  NA  NA  NA  NA  NA  1  NA  NA  NA   NA   NA   NA    1   NA   NA
## 5 NA  NA  NA  NA  NA  NA  NA  1  NA  NA  NA   NA   NA   NA    1   NA   NA
## 6 NA  NA  NA  NA  NA  NA  NA  1  NA  NA  NA   NA   NA   NA    1   NA   NA
##   X.15 X.16 X.17 X.18 X1.2
## 1   NA   NA   NA   NA    1
## 2   NA   NA   NA   NA    1
## 3   NA   NA   NA   NA    1
## 4   NA   NA   NA   NA    1
## 5   NA   NA   NA   NA    1
## 6   NA   NA   NA   NA    1

It appears that X1, X1.1, and X1.2 are the only columns which are of interest (all others only consist of NAs). So let’s select those columns and discard the rest.

library(dplyr)
dd <- select(dd, X1, X1.1, X1.2)

Worked!

Finally, let’s change the column names to our desire.

dd <- rename(dd, V1 = X1,V2 = X1.1, V3 = X1.2)

Hilfe! Mein R startet nicht! Mein R startet zwar, tut aber nicht so, wie ich will. Sicherlich hat es sich (wieder einmal) gegen mich verschworen. Wahrscheinlich hilft nur noch Verschrotten… Bevor Sie zum äußersten schreiten, hier einige Tipps, die sich bewährt haben.

Lösungen, wenn R nicht (richtig) läuft

  • AEG: Aus. Ein. Gut. Starten Sie den Rechner neu. Gerade nach Installation neuer Software zu empfehlen.

  • Sehen Sie eine Fehlermeldung, die von einem fehlenden Paket spricht (z.B. “Package ‘Rcpp’ not available”) oder davon spricht, dass ein Paket nicht installiert werden konnte (z.B. “Package ‘Rcpp’ could not be installed” oder “es gibt kein Paket namens ‘Rcpp’” oder “unable to move temporary installation XXX to YYY”), dann tun Sie folgendes:

    • Schließen Sie R und starten Sie es neu.
    • Installieren Sie das oder die angesprochenen Pakete mit install.packages("name_des_pakets", dependencies = TRUE) oder mit dem entsprechenden Klick in RStudio.
    • Starten Sie das entsprechende Paket mit library(paket_name).
  • Gerade bei Windows 10 scheinen die Schreibrechte für R (und damit RStudio oder RComannder) eingeschränkt zu sein. Ohne Schreibrechte kann R aber nicht die Pakete (“packages”) installieren, die Sie für bestimmte R-Funktionen benötigen. Daher schließen Sie R bzw. RStudio und suchen Sie das Icon von R oder wenn Sie RStudio verwenden von RStudio. Rechtsklicken Sie das Icon und wählen Sie “als Administrator ausführen”. Damit geben Sie dem Programm Schreibrechte. Jetzt können Sie etwaige fehlende Pakete installieren.

  • Ein weiterer Grund, warum R bzw. RStudio die Schreibrechte verwehrt werden könnnten (und damit die Installation von Paketen), ist ein Virenscanner. Der Virenscanner sagt, nicht ganz zu Unrecht, “Moment, einfach hier Software zu installieren, das geht nicht, zu gefährlich”. Grundsätzlich gut, in diesem Fall unnötig. Schließen Sie R/RStudio und schalten Sie dann den Virenscanner komplett (!) aus. Öffnen Sie dann R/RStudio wieder und versuchen Sie fehlende Pakete zu installieren.

  • Läuft der RCommander unter Mac nicht, dann prüfen Sie, ob Sie X11 installiert haben. X11 muss installiert sein, damit der RCommander unter Mac läuft.

  • Die “app nap” Funktion beim Mac kann den RCommander empfindlich ausbremsen. Schalten Sie diese Funktion aus z.B. im RCommander über Tools - Manage Mac OS X app nap for R.app.

Allgemeine Hinweise zur Denk- und Gefühlswelt von R

  • Wenn Sie RStudio starten, startet R automatisch auch. Starten Sie daher, wenn Sie RStudio gestartet haben, nicht noch extra R. Damit hätten Sie sonst zwei Instanzen von R laufen, was zu Verwirrungen (bei R und beim Nutzer) führen kann.

  • Ein neues R-Skript im RStudio können Sie z.B. öffnen mit File-New File-R Script.

  • R-Skripte können Sie speichern (File-Save) und öffnen.

  • R-Skripte sind einfache Textdateien, die jeder Texteditor verarbeiten kann. Nur statt der Endung txt, sind R-Skripte stolzer Träger der Endung R. Es bleibt aber eine schnöde Textdatei.

  • Bei der Installation von Paketen mit install.packages("name_des_pakets") sollte stets der Parameter dependencies = TRUE angefügt werden. Also install.packages("name_des_pakets", dependencies = TRUE). Hintergrund ist: Falls das zu installierende Paket seinerseits Pakete benötigt, die noch nicht installiert sind (gut möglich), dann werden diese sog. “dependencies” gleich mitinstalliert (wenn Sie dependencies = TRUE setzen).

  • Hier finden Sie weitere Hinweise zur Installation des RCommanders: http://socserv.socsci.mcmaster.ca/jfox/Misc/Rcmdr/installation-notes.html.

  • Sie müssen online sein, um Packages zu installieren.

  • Die “app nap” Funktion beim Mac kann den RCommander empfindlich ausbremsen. Schalten Sie diese Funktion aus z.B. im RCommander über Tools - Manage Mac OS X app nap for R.app.

Sei aktuell

  • Verwenden Sie möglichst die neueste Version von R, RStudio und Ihres Betriebssystems. Ältere Versionen führen u.U. zu Problemen; je älter, desto Problem…

  • Updaten Sie Ihre Packages regelmäßig z.B. mit update.packages() oder dem Button “Update” bei RStudio (Reiter Packages).

Sei milde

R zu lernen kann hart sein. Ich weiß, wovon ich spreche. Wahrscheinlich eine spirituelle Prüfung in Geduld und Hartnäckigkeit… Tolle Gelegenheit, sich in diesen Tugenden zu trainieren :-)