Workbook suggested answers
Introduction
This chapter presents the suggested R code to answer the workbook activities and exercises throughout the course labs in Quantitative Methods in the Social Sciences. This covers Lab 3 to Lab 10.
Before looking at the answers, try asking your tutor for help. Also, we strongly recommend web resources, such as https://stackoverflow.com/ or https://forum.posit.co/. By solving the issues, you will learn a lot! ;)
Lab 3. Data wrangling
The two data frames should not look the same. In cleaned_nilt the eight variables we coerced (rsex, highqual, religcat, uninatid and ruhappy as <fct>; rage, rhourswk and persinc2 as <dbl>) show their category labels or plain numbers, whereas in unclean_nilt every variable is still <dbl+lbl>. If they look identical, the coerced version was not saved.
Lab 4. Exploratory data analysis
Preamble code
# Subset
nilt_subset <- nilt |>
select(rsex, rage, highqual, religcat, uninatid, ruhappy, rhourswk, persinc2)Activity 1
In your R Markdown file, do the following activities using the data stored in the nilt_subset object:
- Create a One-Way contingency table for
uninatidin thenilt_subsetdataset using thesumtable()function;
| Variable | N | Percent |
|---|---|---|
| uninatid | 1183 | |
| ... Unionist | 348 | 29% |
| ... Nationalist | 255 | 22% |
| ... Neither | 580 | 49% |
- Using the variables
religcatanduninatid, generate a Two-Way contingency table;
Activity 2
Using the data in the nilt_subset object, complete the following activities.
- Using the
hist()function plot a histogram of personal incomepersinc2. From the NILT documentation this variable refers to annual personal income in £ before taxes and other deductions;
hist(nilt_subset$persinc2)
- Create a summary of the personal income
persinc2variable, using thesumtable()function.
| Variable | N | Mean | Std. Dev. | Min | Pctl. 25 | Pctl. 75 | Max |
|---|---|---|---|---|---|---|---|
| persinc2 | 897 | 16395 | 13466 | 260 | 6760 | 22100 | 75000 |
- Compute the mean and standard deviation of the personal income
persinc2, grouped by happinessruhappy.
Lab 6. Visual exploratory analysis
Preamble code
Activity
Using the nilt_subset object, complete the tasks below in the ‘Lab-6-Visual.Rmd’ file, which you created earlier. Insert a new chunk for each of these activities and include brief comments as text in the Rmd document to introduce the plots and discuss the results (tip —leave an empty line between your text and the next chunk to separate the description and the plots):
- Create a first-level header to start a section called “Categorical analysis”;
# Categorical analysis- Create a simple bar plot using the
geom_bar()geometry to visualize the political affiliation reported by the respondents using the variableuninatid;

- Based on the plot above, create a ‘stacked bar plot’ to visualize the political affiliation by religion, using the
uninatidandreligcatvariables;

- Create a new first-level header to start a section called “Numeric analysis”;
# Numeric analysis- Create a scatter plot about the relationship between personal income
persinc2on the Y axis and number of hours worked a weekrhourswkon the X axis;
ggplot(nilt_subset, aes(x = rhourswk, y = persinc2)) +
geom_point() +
labs(
title = "Income and number of hours worked a week",
x = "Number of hours worked a week", y = "Personal income (£ a year)"
)
- Finally, create a box plot to visualize personal income
persinc2on the Y axis and self-reported level of happinessruhappyon the x axis… Interesting result, Isn’t it? Talk to your neighbour or tutor about your results;
ggplot(nilt_subset, aes(x = ruhappy, y = persinc2)) +
geom_boxplot() +
labs(
title = "Personal income and happiness",
x = "Happiness level", y = "Personal income (£ a year)"
)
- Briefly comment each of the plots as text in your Rmd file;
- Knit the .Rmd document as HTML. The knitted file will be saved automatically in your project. You can come back to the Rmd file to make changes if needed and knit it again as many times as you wish.
Lab 7. Correlation
# Age of respondent’s spouse/partner
nilt$spage <- as.numeric(nilt$spage)
# Migration
nilt <- mutate_at(nilt, vars(mil10yrs, miecono, micultur), as.numeric)Activity 1
Using the nilt data object, visualize the relationship of the following variables by creating a new chunk. Run the chunk individually and comment on what you observe from the result as text in the Rmd file (remember to leave an empty line between your text and the chunk).
- Create a scatter plot to visualize the correlation between the respondent’s overall opinion in relation to migration
mig_perand the respondent’s agerage. Remember that we just created themig_pervariable by summing three variables which were in a 0-10 scale (the higher the value, the better the person’s perception is). Inaes(), specifyrageon the X axis andmig_peron the Y axis. Use theggplot()function andgeom_point(). Also, include a straight line describing the points using thegeom_smooth()function. Within this function, set themethodargument to'lm'.
nilt |> ggplot(aes(x = rage, y = mig_per)) +
geom_point() +
geom_smooth(method = "lm") +
labs(
title = "Perception of migration vs age",
x = "Respondent age", y = "Perception of migration (0-30)"
)
- What type of relationship do you observe? Comment the overall result of the plot and whether this is in line with your previous expectation.
Activity 2
- Create a boxplot to visualize the relationship between the respondent’s overall opinion in relation to migration
mig_per(Y axis) and constitutional viewuninatid(X axis).
nilt |> ggplot(aes(x = uninatid, y = mig_per)) +
geom_boxplot() +
labs(
title = "Perception of migration by constitutional view",
x = "Constitutional view", y = "Perception of migration (0-30)"
)
- Do you think the opinion towards migration differs among the groups? Re: The boxes overlap considerably, but unionist respondents have a slightly lower median (15) than nationalist respondents and those identifying with neither (17 for both). So there is a modest difference in the overall perception of migration between the groups, with a lot of variation within each group.
Activity 3
- Compute a correlation matrix for
rage,persinc2,mil10yrs,mieconoandmicultur, using'pairwise.complete.obs'and rounding to 3 decimals.
rage persinc2 mil10yrs miecono micultur
rage 1.000 -0.036 -0.024 -0.029 -0.099
persinc2 -0.036 1.000 0.165 0.199 0.195
mil10yrs -0.024 0.165 1.000 0.716 0.627
miecono -0.029 0.199 0.716 1.000 0.665
micultur -0.099 0.195 0.627 0.665 1.000
- Is personal income or age correlated with the perception of migrants? Re: Age is not correlated with any of the three migration variables (all coefficients are close to 0, between -0.02 and -0.10). Personal income shows a weak positive correlation with all three (around 0.17 to 0.20): respondents with higher incomes tend to report slightly more positive views of the effects of migration on the economy (
miecono), on culture (micultur) and on Northern Ireland over the last ten years (mil10yrs). The three migration variables are strongly correlated with each other (0.63 to 0.72), which is why combining them intomig_peris reasonable.
Lab 8. Linear model. Simple linear regression
m3 <- lm(persinc2 ~ rhourswk, data = nilt)Lab activities
Use the nilt data set object in your ‘Lab-8-Simple-Linear-Regression.Rmd’ file to:
- Plot a scatter plot using
ggplot. In the aesthetics, locaterhourswkin the X axis, andpersinc2in the Y axis. In thegeom_point(), jitter the points by specifying theposition = 'jitter'. Also, include the best fit line using thegeom_smooth()function, and specify themethod = 'lm'inside.
ggplot(nilt, aes(x = rhourswk, y = persinc2)) +
geom_point(position = "jitter") +
geom_smooth(method = "lm")
- Print the summary of
m3using thesummary()function.
summary(m3)
Call:
lm(formula = persinc2 ~ rhourswk, data = nilt)
Residuals:
Min 1Q Median 3Q Max
-43694 -8148 -3070 4990 58249
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5170.4 1966.2 2.63 0.00884 **
rhourswk 463.2 52.4 8.84 < 2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 13860 on 455 degrees of freedom
(747 observations deleted due to missingness)
Multiple R-squared: 0.1466, Adjusted R-squared: 0.1447
F-statistic: 78.15 on 1 and 455 DF, p-value: < 2.2e-16
- Is the relationship of hours worked a week significant? Re: Yes. The p-value (fourth column of the ‘Coefficients’ table) is lower than 0.05.
- What is the adjusted r-squared? How would you interpret it? Re: the adjusted R-squared is 0.14. This can be interpreted in terms of percentage, e.g. 14% of the variance in personal income can be explained by the number of hours worked a week.
- What is the sample size to fit the model? Re: The total number of observations in the data set is 1,204 and the model summary says that 747 observations were deleted due to missingness. Therefore, the sample size is 457 (1204-747).
- What is the expected income in pounds a year for a respondent who works 30 hours a week according to coefficients of this model?
5170.4 + 463.2 * 30[1] 19066.4
- Plot a histogram of the residuals of
m3using theresiduals()function insidehist(). Do the residuals look normally distributed (as in a bell-shaped curve)?
Overall, the residuals look normally distributed with the exception of the values to the right-hand side of the plot (between 40000 and 60000).
Lab 9. Multiple linear regression (part 1)
Lab activities
Load the packages and the data that you will need in your file using the code below:
## Load the packages
library(moderndive)
library(tidyverse)
# Read the data from the .rds file
nilt <- readRDS("data/nilt_r_object.rds")- Print a table for the highest level of qualification
highqualusing thetable()function.
table(nilt$highqual)
Degree level or higher Higher education GCE A level or equiv
230 102 243
GCSE A-C or equiv GCSE D-G or equiv No qualifications
185 82 281
Other, level unknown Unclassified
27 54
- Generate a scatter plot using
ggplot. Withinaes(), locate the number of hours worked a weekrhourswkon the X axis and the personal incomepersinc2on the Y axis, and specify thecolorof the dots by the highest level of qualificationhighqual. Use thegeom_point()function and ‘jitter’ the points using the argumentposition. Add the parallel slopes using thegeom_parallel_slopes()function and set the standard errorsetoFALSE. What is your interpretation of the plot? Write down your comments to introduce the plot.
ggplot(nilt, aes(x = rhourswk, y = persinc2, color = highqual)) +
geom_point(position = "jitter") +
moderndive::geom_parallel_slopes(se = FALSE) +
labs(
title = "Personal income",
subtitle = "Personal income and number of hours worked a week by education level",
x = "Number of hours worked a week", y = "Personal income (£ a year)",
color = "Highest education level"
)
- Fit a linear model using the
lm()function to analyse the personal incomepersinc2using the number of hours worked a weekrhourswk, the highest level of qualificationhighqual, and the age of the respondentrageas independent variables. Store the model in an object calledm4and print the summary.
Call:
lm(formula = persinc2 ~ rhourswk + rage + highqual, data = nilt)
Residuals:
Min 1Q Median 3Q Max
-36228 -6425 -1411 4635 54749
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3904.64 2714.25 1.439 0.151
rhourswk 444.79 45.32 9.814 < 2e-16 ***
rage 236.59 48.47 4.881 1.47e-06 ***
highqualHigher education -8164.91 1939.50 -4.210 3.09e-05 ***
highqualGCE A level or equiv -12439.26 1563.42 -7.956 1.47e-14 ***
highqualGCSE A-C or equiv -13037.47 1703.07 -7.655 1.20e-13 ***
highqualGCSE D-G or equiv -11622.07 2665.38 -4.360 1.61e-05 ***
highqualNo qualifications -12968.10 2339.78 -5.542 5.11e-08 ***
highqualOther, level unknown 15445.70 3334.75 4.632 4.76e-06 ***
highqualUnclassified -12399.58 2786.16 -4.450 1.08e-05 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 11830 on 447 degrees of freedom
(747 observations deleted due to missingness)
Multiple R-squared: 0.3887, Adjusted R-squared: 0.3764
F-statistic: 31.58 on 9 and 447 DF, p-value: < 2.2e-16
- Comment on the results of the model by mentioning which of the variables is significant and their respective p-value, the adjusted r-squared of the model, and the number of observations used to fit the model. Re: All the independent variables including the number of hours worked a week, age, and all the categories of highest qualification level compared to ‘Degree level or higher’ are significant to predict personal income in the model ‘m4’, considering that the p-value is lower than 0.05. We can confirm this from the fourth column of the ‘Coefficients’ table. The adjusted R-squared of the model is 0.38 (0.3764 in the output). This means that 37.6% of the variance in personal income can be explained by these variables. The size of the sample used to fit this model is 457, considering that the ‘nilt’ data set contains 1204 observations but 747 were deleted due to missingness (1204 - 747).
Lab 10. Multiple linear regression (part 2)
Returning to the model m4 from the Lab 9 activities:
- Comment on the R2, adjusted R2, and F-statistic to interpret the model fit. Re: The R2 is 0.389 and the adjusted R2 is 0.376, so the number of hours worked, age and highest qualification together explain around 38% of the variation in personal income among the respondents in the model. The F-statistic is 31.6 on 9 and 447 degrees of freedom with a p-value far below 0.05, so the model as a whole is statistically significant - the independent variables explain more variation than would be expected by chance.
- How many observations were removed due to missing values? Which variables are contributing to this? Re: 747 observations were deleted due to missingness, leaving 457 of the 1,204 respondents. We can check where the missing values come from by counting the NAs in each variable:
persinc2 rhourswk rage highqual
Min. : 260 Min. : 4.00 Min. :18.00 No qualifications :281
1st Qu.: 6760 1st Qu.: 25.25 1st Qu.:35.00 GCE A level or equiv :243
Median :11960 Median : 37.00 Median :48.00 Degree level or higher:230
Mean :16395 Mean : 35.09 Mean :49.62 GCSE A-C or equiv :185
3rd Qu.:22100 3rd Qu.: 40.00 3rd Qu.:64.00 Higher education :102
Max. :75000 Max. :100.00 Max. :97.00 GCSE D-G or equiv : 82
NAs :307 NAs :638 NAs :3 (Other) : 81
Most of the missing values come from rhourswk (638 NAs, as the question was only asked of respondents in work) and persinc2 (307 NAs), with only 3 missing ages and no missing qualifications. As discussed in Lab 10, this means the model only describes respondents who are in work and reported their income, which needs to be borne in mind when interpreting the results.
- Plot a histogram of the residuals for model
m4. Do they look normally distributed? Can we trust our estimates or would you advise to carry out further actions to verify the adequate interpretation of this model?
The distribution of the residuals in ‘m4’ looks overall normally distributed. However, the distribution is not perfectly symmetric. Therefore, we would advise conducting further checks to test the linear model assumptions.

