Interactions I: Num x Cat

Learning Objectives

At the end of this lab, you will:

  1. Understand the concept of an interaction
  2. Be able to interpret the meaning of a numeric \(\times\) categorical interaction
  3. Be able to visualize and probe interactions

What You Need

  1. Be up to date with lectures
  2. Have completed all labs from Semester 1

Required R Packages

Remember to load all packages within a code chunk at the start of your RMarkdown file using library(). If you do not have a package and need to install, do so within the console using install.packages(" "). For further guidance on installing/updating packages, see Section C here.

For this lab, you will need to load the following package(s):

  • tidyverse
  • kableExtra
  • psych
  • sjPlot
  • patchwork
  • sandwich
  • interactions

Presenting Results

All results should be presented following APA guidelines.If you need a reminder on how to hide code, format tables/plots, etc., make sure to review the rmd bootcamp.

The example write-up sections included as part of the solutions are not perfect - they instead should give you a good example of what information you should include and how to structure this. Note that you must not copy any of the write-ups included below for future reports - if you do, you will be committing plagiarism, and this type of academic misconduct is taken very seriously by the University. You can find out more here.

Lab Data

You can download the data required for this lab here or read it in via this link https://uoepsy.github.io/data/wellbeing_location_rural.csv

Study Overview

Research Question

Does the association between wellbeing and the number of social interactions differ between rural and non-rural residents?

Wellbeing/Rurality data codebook.

Setup

Setup
  1. Create a new RMarkdown file
  2. Load the required package(s)
  3. Read the wellbeing_location_rural dataset into R, assigning it to an object named ruraldata

#Loading the required package(s)
library(tidyverse)
library(kableExtra)
library(psych)
library(sjPlot)
library(patchwork)
library(sandwich)
library(interactions)

#Reading in data and storing in object named 'ruraldata'
ruraldata <- read_csv("https://uoepsy.github.io/data/wellbeing_location_rural.csv")

Exercises

Study & Analysis Plan Overview

Question 1

Provide a brief overview of the study design and data, before detailing your analysis plan to address the research question.

  • Give the reader some background on the context of the study
  • State what type of analysis you will conduct in order to address the research question
  • Specify the model to be fitted to address the research question (note that you will need to specify the reference level of your categorical variable)
  • Specify your chosen significance (\(\alpha\)) level
  • State your hypotheses

Much of the information required can be found in the Study Overview codebook.

The statistical models flashcards may also be useful to refer to. Specifically the interaction models flashcards and numeric x categorical example flashcards might be of most use.

The ruraldata dataset contained information on 200 hypothetical participants who lived in Edinburgh & Lothians area. Using a between-subjects design, the researchers collected information on participants’ wellbeing (measured via WEMWBS), outdoor time (hours per week), social interactions (number per week), routine (whether or not one was followed), location of residence (City, Suburb, or Rural), average weekly steps (in thousands), and age (in years).

Density plots and histograms will be used to visualise the marginal distributions of wellbeing and social interactions, and the strength of association between the two variables estimated via the correlation coefficient. To understand how these associations differ between rural and non-rural locations, scatterplots will be used.

To address the research question of whether the association between the number of social interactions and wellbeing differs between rural and non-rural residents, we are going to fit the following interaction model (where not rural will be specified as the reference group for location).

\[ \begin{align} \text{Wellbeing} ~=~ & \beta_0 + \beta_1 \cdot \text{Social Interactions} + \beta_2 \cdot \text{Location}_\text{Rural} \\ & + \beta_3 \cdot (\text{Social Interactions} \cdot \text{Location}_\text{Rural}) + \epsilon \\ \end{align} \quad \]

Effects will be considered statistically significant at \(\alpha=.05\)

Our hypotheses are:

\(H_0: \beta_3 = 0\)

The association between wellbeing and social interactions is not moderated by whether or not a person lives in a rural area.

\(H_1: \beta_3 \neq 0\)

The association between wellbeing and social interactions is moderated by whether or not a person lives in a rural area.


Question 2

Check coding of variables (e.g., that categorical variables are coded as factors).

As specified in Q1, we want ‘not rural’ as the reference group, so make sure to specify this.

Check coding of variables within ruraldata and ensure isRural is a factor with two levels, ‘rural’ and ‘not rural’:

# check structure of dataset 
str(ruraldata) 
spc_tbl_ [200 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ age         : num [1:200] 28 56 25 60 19 34 41 41 35 53 ...
 $ outdoor_time: num [1:200] 12 5 19 25 9 18 17 11 12 13 ...
 $ social_int  : num [1:200] 13 15 11 15 18 13 19 12 13 15 ...
 $ routine     : num [1:200] 1 1 1 0 1 1 1 1 0 1 ...
 $ wellbeing   : num [1:200] 36 41 35 35 32 34 39 43 35 37 ...
 $ isRural     : chr [1:200] "rural" "rural" "rural" "rural" ...
 - attr(*, "spec")=
  .. cols(
  ..   age = col_double(),
  ..   outdoor_time = col_double(),
  ..   social_int = col_double(),
  ..   routine = col_double(),
  ..   wellbeing = col_double(),
  ..   isRural = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 
#alternatively run is.factor() for specific variable
is.factor(ruraldata$isRural) 
[1] FALSE
#set isRural as a factor
ruraldata <- ruraldata %>%
    mutate(
        isRural = factor(isRural, 
                           labels = c('not rural', 'rural')))
#specify 'not rural' as reference group
ruraldata$isRural <- relevel(ruraldata$isRural, 'not rural')

Descriptive Statistics & Visualisations

Question 3

Provide a table of descriptive statistics and visualise your data.

Remember to interpret your findings in the context of the study (i.e., comment on any observed differences among groups).

In particular:

  1. Explore the associations among the variables included in your analysis
  2. Produce a visualisation of the association between weekly number of social interactions and well-being, with separate facets for rural vs non-rural respondents OR with different colours for each level of the isRural variable.

Review the many ways to numerically and visually explore your data by reading over the data exploration flashcards.

For examples, see flashcards on descriptives statistics tables - categorical and numeric values examples and numeric x categorical example - visualise data, paying particular attention to the type of data that you’re working with.

More specifically:
1. For your table of descriptive statistics, both the group_by() and summarise() functions will come in handy here.

  1. If you use the select() function and get an error along the lines of Error in select...unused arguments..., you will need to specify dplyr::select() (this just tells R which package to use the select function from).

  2. The pairs.panels() function from the psych package can plot all variables in a dataset against one another. This will save you the time you would have spent creating individual plots, but is only useful for continuous variables.

We can present our summary statistics for wellbeing and social interactions grouped by location in a well formatted table using kable():

ruraldata %>%
    select(isRural, wellbeing, social_int) %>%
    group_by(isRural) %>%
    summarise(Wellbeing_M = mean(wellbeing),
              Wellbeing_SD = sd(wellbeing),
              SocialInt_M = mean(social_int),
              SocialInt_SD = sd(social_int)) %>%
    kable(caption = "Wellbeing, Social Interactions, and Location Descriptive Statistics", align = "c", digits = 2, booktabs = TRUE) %>%
    kable_styling(full_width = FALSE)
Table 1: Wellbeing, Social Interactions, and Location Descriptive Statistics
isRural Wellbeing_M Wellbeing_SD SocialInt_M SocialInt_SD
not rural 38.57 5.66 11.67 3.95
rural 34.02 3.99 12.46 4.08

For a quick overview of several summary statistics at once for all columns of our dataframe, but separately for each level of location (i.e.,rural and non-rural), you can use describeBy() from the psych package:

describeBy(ruraldata, ruraldata$isRural)

 Descriptive statistics by group 
group: not rural
             vars   n  mean    sd median trimmed   mad min max range  skew
age             1 100 43.01 14.95   41.5   42.74 18.53  18  70    52  0.13
outdoor_time    2 100 18.72  6.91   18.0   18.61  7.41   6  34    28  0.08
social_int      3 100 11.67  3.95   12.0   11.51  3.71   3  24    21  0.37
routine         4 100  0.54  0.50    1.0    0.55  0.00   0   1     1 -0.16
wellbeing       5 100 38.57  5.66   39.0   38.36  5.93  26  59    33  0.44
isRural*        6 100  1.00  0.00    1.0    1.00  0.00   1   1     0   NaN
             kurtosis   se
age             -1.13 1.49
outdoor_time    -0.87 0.69
social_int       0.13 0.39
routine         -1.99 0.05
wellbeing        0.38 0.57
isRural*          NaN 0.00
------------------------------------------------------------ 
group: rural
             vars   n  mean    sd median trimmed   mad min max range  skew
age             1 100 41.59 14.85     42   41.38 17.79  18  69    51  0.09
outdoor_time    2 100 17.79  7.29     18   17.68  7.41   1  35    34  0.06
social_int      3 100 12.46  4.08     12   12.41  4.45   4  21    17  0.05
routine         4 100  0.59  0.49      1    0.61  0.00   0   1     1 -0.36
wellbeing       5 100 34.02  3.99     34   34.08  2.97  22  45    23 -0.07
isRural*        6 100  2.00  0.00      2    2.00  0.00   2   2     0   NaN
             kurtosis   se
age             -1.18 1.49
outdoor_time    -0.49 0.73
social_int      -0.82 0.41
routine         -1.89 0.05
wellbeing        0.40 0.40
isRural*          NaN 0.00

Let’s first plot the continuous variables included within our model (note that we could use this for the whole dataset, but we don’t want to include irrelevant / non-continuous variables):

ruraldata %>% 
  select(wellbeing, social_int) %>%
  pairs.panels()

Wellbeing and social interactions appear to follow unimodal distributions. There was a weak, positive association between wellbeing and social interactions \((r = .24)\).

Now lets look at wellbeing scores by location:

ggplot(data = ruraldata, aes(x = isRural, y = wellbeing)) +
  geom_boxplot() + 
  labs(x = "Location", y = "Wellbeing (WEMWBS Scores)")

Those in rural locations appear to have lower wellbeing scores in comparison to those in non-rural locations.

Next, lets produce our plots with a facet for rural vs non-rural residents:

ggplot(data = ruraldata, aes(x = social_int, y = wellbeing)) +
  geom_point() +
  geom_smooth(method="lm", se=FALSE) +
  facet_wrap(~isRural, labeller = "label_both") + 
  labs(x = "Social Interactions (number per week)", y = "Wellbeing (WEMWBS Scores)")

Or instead of facets, we could use different colors for each location (rural vs non-rural):

ggplot(data = ruraldata, aes(x = social_int, y = wellbeing, colour = isRural)) +
  geom_point() + 
  geom_smooth(method="lm", se=FALSE) +
    scale_colour_discrete(
    name ="Location",
    labels=c("Not Rural", "Rural")) + 
    labs(x = "Social Interactions (number per week)", y = "Wellbeing (WEMWBS Scores)")

Those in non-rural locations appear to have higher wellbeing scores across almost all levels of social interactions. The slopes appear to be different for each location, where the greatest difference in wellbeing scores by location is most visible the highest number of social interactions. This suggests that there may be an interaction.

The lines in the two plots above do not run in parallel - this suggested the presence of an interaction. Specifically in our example, the non-parallel lines suggested an interaction effect based on location, as the number of social interactions did not appear to have the same influence on rural and non-rural residents’ wellbeing scores.

However, the only way we can determine whether there is actually an interaction is by including an interaction term in our model, and testing this.

Model Fitting & Interpretation

Question 4

Fit the specified model, and assign it the name “rural_mod”.

We can fit interaction models using the lm() function.

For an overview, see the interaction models flashcards.

For an example, review the interaction models > numeric x categorical example > model building flashcards.

#fit model including interaction between social_int and isRural
rural_mod <- lm(wellbeing ~  social_int * isRural, data = ruraldata)

#check model output
summary(rural_mod)

Call:
lm(formula = wellbeing ~ social_int * isRural, data = ruraldata)

Residuals:
     Min       1Q   Median       3Q      Max 
-12.4845  -2.7975   0.0155   2.4539  15.6743 

Coefficients:
                        Estimate Std. Error t value Pr(>|t|)    
(Intercept)              30.9986     1.4284  21.702  < 2e-16 ***
social_int                0.6488     0.1160   5.593 7.42e-08 ***
isRuralrural              1.3866     2.0510   0.676  0.49981    
social_int:isRuralrural  -0.5176     0.1615  -3.206  0.00157 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 4.558 on 196 degrees of freedom
Multiple R-squared:  0.2962,    Adjusted R-squared:  0.2854 
F-statistic: 27.49 on 3 and 196 DF,  p-value: 6.97e-15


Question 5

Look at the parameter estimates from your model, and write a description of what each one corresponds to on the plot shown in Figure 1 (it may help to sketch out the plot yourself and annotate it, and refer to the drop down options below).

Figure 1: Multiple regression model: Wellbeing ~ Social Interactions * is Rural
Options for Mapping Parameter Estimates to Plot

Recall that we can obtain our parameter estimates using various functions such as summary(),coef(), coefficients(), etc.

For an overview of how to interpret coefficients, review the interaction models > interpreting coefficients flashcard.

For a specific example of coefficient interpretation, review the interaction models > numeric x categorical example > results interpretation flashcards.

Obtain parameter estimates:

coefficients(rural_mod)
            (Intercept)              social_int            isRuralrural 
             30.9985688               0.6487945               1.3865688 
social_int:isRuralrural 
             -0.5175856 

\(\beta_0\) = (Intercept) = 31

  • On plot: The point at which the red line cuts the y-axis.
  • Interpretation: The intercept, or predicted wellbeing score when the number of social interactions per week was 0, and when location was not rural.
    • A non-rural resident who had zero social interactions per week was expected to have a wellbeing score of 31.

\(\beta_1\) = social_int = 0.65

  • On plot: The slope (vertical increase on the y-axis associated with a 1 unit increase on the x-axis) of the red line.
  • Interpretation: The simple slope of social interactions (number per week) for location reference group (not rural).
    • For someone who lives in a non-rural location, every 1 additional social interaction per week was associated with a 0.65 point change in their wellbeing score.

\(\beta_2\) = isRuralrural = 1.39

  • On plot: The vertical distance from the red to the blue line at the y-axis (where social_int = 0).
  • Interpretation: The simple effect of location (or the difference in wellbeing scores between rural and non rural residents) when number of social interactions was 0.
    • For residents who had zero social interactions per week, living in a rural location was associated with wellbeing scores 1.39 points higher than living in non-rural locations (note that this difference was not significantly different from zero).

\(\beta_3\) = social_int:isRuralrural = -0.52

  • On plot: How the slope of the line differs when you move from the red to the blue line.
  • Interpretation: The interaction between social interactions (number per week) and location (rural/not rural). This is the estimated difference in simple slopes of social interactions for rural vs non-rural residents.
    • Compared to living in non-rural locations, living in a rural location was associated with a 0.52 lesser increase in wellbeing scores for every every 1 additional social interaction per week.


Question 6

No participants in our dataset had zero hours of social interactions per week (the lowest was 3), and we’re likely not interested in differences between rural and non-rural residents who have never interacted with others.

Mean center the continuous IV(s), and re-run your model with mean centered variable(s).

There are a couple of different ways that we can re-centre. See the data transformations > centering flashcards for a recap. Note, it would be best to create a new mean-centered variable to then use within the model in this instance.

Create mean centered variable for ‘social_int’, named ‘mc_social_int’:

ruraldata <-
 ruraldata %>%
  mutate(
   mc_social_int = social_int - mean(social_int)
    )

Re-run model with ‘mc_social_int’:

#fit model including interaction between social_int and isRural
rural_mod1 <- lm(wellbeing ~  mc_social_int * isRural, data = ruraldata)

#check model output
summary(rural_mod1)

Call:
lm(formula = wellbeing ~ mc_social_int * isRural, data = ruraldata)

Residuals:
     Min       1Q   Median       3Q      Max 
-12.4845  -2.7975   0.0155   2.4539  15.6743 

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 38.8263     0.4581  84.754  < 2e-16 ***
mc_social_int                0.6488     0.1160   5.593 7.42e-08 ***
isRuralrural                -4.8581     0.6478  -7.500 2.17e-12 ***
mc_social_int:isRuralrural  -0.5176     0.1615  -3.206  0.00157 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 4.558 on 196 degrees of freedom
Multiple R-squared:  0.2962,    Adjusted R-squared:  0.2854 
F-statistic: 27.49 on 3 and 196 DF,  p-value: 6.97e-15


Question 7

Note any differences between the summary() output between the original (“rural_mod”) and mean centred (“rural_mod1”) models. Pay particular attention to your coefficients and their significance values. How have your coefficients changed? Why do you think these differences have been observed?

These plots illustrate the difference between the “rural_mod” and “rural_mod1” models.

Figure 2: Difference when social interactions is not vs is mean centered.
Note that the lines without SE intervals on the left plot represent predicted values below the minimum observed number of social interactions, to ensure that zero on the x-axis is visible

By comparing the summary() outputs, you should see that the coefficients for (Intercept) and isRuralrural differed between the two models, whilst the coefficients for social_int and mc_social_int were the same, as were the interaction estimates social_int:isRuralrural.

Original Model - rural_mod:

Call:
lm(formula = wellbeing ~ social_int * isRural, data = ruraldata)

Coefficients:
                        Estimate Std. Error t value Pr(>|t|)    
(Intercept)              30.9986     1.4284  21.702  < 2e-16 ***
social_int                0.6488     0.1160   5.593 7.42e-08 ***
isRuralrural              1.3866     2.0510   0.676  0.49981    
social_int:isRuralrural  -0.5176     0.1615  -3.206  0.00157 ** 

Model with mean centered social interactions - rural_mod1:

Call:
lm(formula = wellbeing ~ mc_social_int * isRural, data = ruraldata)

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 38.8263     0.4581  84.754  < 2e-16 ***
mc_social_int                0.6488     0.1160   5.593 7.42e-08 ***
isRuralrural                -4.8581     0.6478  -7.500 2.17e-12 ***
mc_social_int:isRuralrural  -0.5176     0.1615  -3.206  0.00157 ** 

Recall that when there is an interaction A\(\times\)B, the coefficients A and B are no longer main effects. Instead, they are conditional effects upon the other being zero.

If we have the interaction y ~ x1 + x2 + x1:x2 (where \(y\) = wellbeing; \(x_1\) = social interactions; and \(x_2\) = whether or not the respondent lives in a rural location.), then:

  • In our “rural_mod”, the coefficient for x2 represents the the association between \(y\) and \(x_2\) for someone with a score of 0 on \(x_1\)
  • In our “rural_mod1”, where x1 is mean centered, this will now make the coefficient for x2 represent the the association between \(y\) and \(x_2\) for someone at the average of \(x_1\) (i.e., in our current example, 12.06)

Whilst the difference in rural vs non-rural may not have been significantly different when the number of weekly social interactions is zero, there did appear to be a significant difference at the average number of social interactions (as you can see from the plot below - note that this is the same plot as in the hint). Note that we can see that the model doesn’t change, it is just extracting different information (the distance to move from the blue dot to the red dot is different):

Figure 3: Difference when social interactions is not vs is mean centered

Visualise Interaction Model

Question 8

Using the probe_interaction() function from the interactions package, visualise the interaction effects from your model.

Try to summarise the interaction effects in a short and concise sentence.

plt_rural_mod <- probe_interaction(model = rural_mod1, 
                  pred = mc_social_int, 
                  modx = isRural, 
                  interval = T,
                  main.title = "Predicted Wellbeing Scores across \n Social Interactions by Location",
                  x.label = "Number of Social Interactions per Week (Mean Centred)",
                  y.label = "Wellbeing (WEMWBS Scores)",
                  legend.main = "Location")

Let’s look at our plot:

plt_rural_mod$interactplot

Figure 4: Predicted Wellbeing Scores across Social Interactions by Location

This suggested that for individuals living in rural locations, wellbeing scores increased at a slower rate across the number of weekly social interactions in comparison to those in rural locations. In other words, for each additional social interaction per week, wellbeing scores of those living in non-rural locations increased at a greater rate than those in rural.

Writing Up & Presenting Results

Question 9

Provide key model results in a formatted table.

Use tab_model() from the sjPlot package. For a quick guide, review the tables flashcard.

#create table for results
tab_model(rural_mod1,
          dv.labels = "Wellbeing (WEMWBS Scores)",
          pred.labels = c("mc_social_int" = "Social Interactions (number per week; mean centred)",
                          "isRuralrural" = "Location - Rural",
                          "mc_social_int:isRuralrural" = "Social Interactions (number per week; mean centred) * Location - Rural"),
          title = "Regression Table for Wellbeing Model")
Table 2: Regression Table for Wellbeing Model
  Wellbeing (WEMWBS Scores)
Predictors Estimates CI p
(Intercept) 38.83 37.92 – 39.73 <0.001
Social Interactions
(number per week; mean
centred)
0.65 0.42 – 0.88 <0.001
Location - Rural -4.86 -6.14 – -3.58 <0.001
Social Interactions
(number per week; mean
centred) * Location -
Rural
-0.52 -0.84 – -0.20 0.002
Observations 200
R2 / R2 adjusted 0.296 / 0.285


Question 10

Interpret your results in the context of the research question and report your model in full.

Make reference to the interaction plot and regression table.

For an example of coefficient interpretation, review the interaction models > numeric x categorical example > results interpretation flashcards.

Full regression results including 95% Confidence Intervals are shown in Table 2. The \(F\)-test for model utility was significant \((F(3,196) = 27.49, p < .001)\), and the model explained approximately 28.54% of the variability in wellbeing (WEMWBS Scores).

There was a significant conditional association between wellbeing (WEMWBS Scores) and the number of weekly social interactions \((\beta = 0.65,~ SE = 0.12,~ t(196) = 5.59,~ p < .001)\), which suggested that for those living in non-rural locations, wellbeing scores increased by 0.65 for every additional social interaction per week. A significant conditional association was also evident between wellbeing and location \((\beta = -4.86,~ SE = 0.65,~ t(196) = -7.50,~ p < .001)\), which suggested that for those who have the average number of social interactions per week \((M = 12.06)\), wellbeing scores were 4.86 points lower for those in rural areas in comparison to those in non-rural.

The association between wellbeing (WEMWBS Scores) and social interactions was found to be dependent upon location (rural/non-rural), and this was significant \((\beta = -0.52,~ SE = 0.16,~ t(196) = -3.21, ~p = .002)\). The expected increase in wellbeing (WEMWBS Scores) for every additional social interaction per week was 0.52 points less for those living in rural locations in comparison to those in non-rural. This interaction is visually presented in Figure 4. Therefore, we have evidence to reject the null hypothesis (that the association between wellbeing and social interactions is not moderated by whether or not a person lives in a rural area).

Compile Report

Compile Report

Knit your report to PDF, and check over your work. To do so, you should make sure:

  • Only the output you want your reader to see is visible (e.g., do you want to hide your code?)
  • Check that the tinytex package is installed
  • Ensure that the ‘yaml’ (bit at the very top of your document) looks something like this:
---
title: "this is my report title"
author: "B1234506"
date: "07/09/2024"
output: bookdown::pdf_document2
---

If you are having issues knitting directly to PDF, try the following:

  • Knit to HTML file
  • Open your HTML in a web-browser (e.g. Chrome, Firefox)
  • Print to PDF (Ctrl+P, then choose to save to PDF)
  • Open file to check formatting

To not show the code of an R code chunk, and only show the output, write:

```{r, echo=FALSE}
# code goes here
```

To show the code of an R code chunk, but hide the output, write:

```{r, results='hide'}
# code goes here
```

To hide both code and output of an R code chunk, write:

```{r, include=FALSE}
# code goes here
```

You must make sure you have tinytex installed in R so that you can “Knit” your Rmd document to a PDF file:

install.packages("tinytex")
tinytex::install_tinytex()

You should end up with a PDF file. If you have followed the above instructions and still have issues with knitting, speak with a Tutor.