# Loading necessary data R packages
library(tidyverse)
library(lubridate)

Note that for the select all that apply style questions, there was a percentage correction on incorrect answers for some cases. There might be some changes on your mark compared to what is announced on 21st of October.

Question 1 (1 mark)

Correct answer is arrange().

The arrange() function from tidyverse is used to sort a dataset. The base R equivalent is order(). One example is given below

# Example:
data <- tibble(name = c("Alice", "Bob", "Charlie"), value = c(3, 1, 2))
data %>% arrange(value)
## # A tibble: 3 × 2
##   name    value
##   <chr>   <dbl>
## 1 Bob         1
## 2 Charlie     2
## 3 Alice       3
# Returns rows sorted by value in ascending order

order() does something similar but not from tidyverse, it is a base R function and needs a different syntax indeed!

data[order(data$value),]
## # A tibble: 3 × 2
##   name    value
##   <chr>   <dbl>
## 1 Bob         1
## 2 Charlie     2
## 3 Alice       3

Question 2 (1 mark)

Assume that the dataset visitors_with_nations (from our workshop) has been imported into your environment. What is wrong with the following code? Select one.

filter(visitors_with_nations, n_2021 > 100000, "England")

Problem here is that we have a missing condition variable. In general, the filter() function requires logical conditions. The string “England” evaluates to TRUE in a logical context, but this is not the intended comparison. The correct syntax uses == for equality comparison. The correct format given as below

filter(visitors_with_nations, n_2021 > 100000, nation == "England")

Question 3 (1 mark)

Assume that the dataset visitors_with_nations contains several columns, including n_2021 and n_2022, and others.

What will the following code return? Select one.

visitors <- read_csv("data/UK-visitor-numbers.csv")
## Rows: 348 Columns: 6
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): attraction, admission, setting, region
## dbl (2): n_2021, n_2022
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
visitors_with_nations <- visitors %>%
  mutate(
    nation = case_when(
      region == "Northern Ireland" ~ "Northern Ireland",
      region == "Scotland" ~ "Scotland",
      region == "Wales" ~ "Wales",
      TRUE ~ "England"
    )
  )
visitors_with_nations %>% select(-n_2021, -n_2022)
## # A tibble: 348 × 5
##    attraction                                    admission setting region nation
##    <chr>                                         <chr>     <chr>   <chr>  <chr> 
##  1 1066 Battle of Hastings, Abbey and Battlefie… Members   O       South… Engla…
##  2 Abbey House Museum                            Charged   I       Yorks… Engla…
##  3 Abertarff House                               Free      I       Scotl… Scotl…
##  4 Alice Holt Forest                             Free      O       South… Engla…
##  5 Alloa Tower                                   Members   I       Scotl… Scotl…
##  6 Anglesey Abbey                                Members   M       East … Engla…
##  7 Anne Hathaway's Cottage                       Charged   M       West … Engla…
##  8 Arbeia South Sheilds Roman Fort               Free      M       North… Engla…
##  9 Ashmolean Museum Oxford                       Free      I       South… Engla…
## 10 Aston Hall                                    Members   M       West … Engla…
## # ℹ 338 more rows

With this minus sign in front of the variables, the code removes n_2021 and n_2022 from the dataset.


Question 4 (1 mark)

Suppose you run the following code in your R session:

visitors_with_nations <- visitors_with_nations %>%
  mutate(growth = n_2022 - n_2021)

followed by a second incomplete snippet of code:

#visitors_with_nations %>%
#  ___________(nation) %>%
#  summarise(avg_growth = mean(growth, na.rm = TRUE))

To get the average growth for each nation, which function goes in the blank? (Select one)

To calculate statistics by groups (fore each nation), you need to use group_by() before summarise(). The correct form should be

visitors_with_nations %>%
  group_by(nation) %>%
  summarise(avg_growth = mean(growth, na.rm = TRUE))
## # A tibble: 4 × 2
##   nation           avg_growth
##   <chr>                 <dbl>
## 1 England             209979.
## 2 Northern Ireland    123486.
## 3 Scotland             95449.
## 4 Wales                24752

In this question the first code snippet should be given like this but not affecting the overall selection from the multiple choices. Please note that this difference.


Question 5 (2 marks)

Suppose visitors_with_nations contains the following columns, among the others: attraction, admission, nation.

Which of the following gives an error message? Select one.


summarise() collapses the dataset to one row per group (or one row total without grouping), losing the admission column. Therefore, filtering on admission after summarise() causes an error.

# Working examples:
visitors_with_nations %>%
  filter(admission == "Free") %>%
  summarise(n = n())
## # A tibble: 1 × 1
##       n
##   <int>
## 1   128
visitors_with_nations %>%
  group_by(nation) %>%
  filter(admission == "Free") %>%
  summarise(n = n())
## # A tibble: 3 × 2
##   nation               n
##   <chr>            <int>
## 1 England             93
## 2 Northern Ireland     1
## 3 Scotland            34
visitors_with_nations %>%
  group_by(nation) %>%
  summarise(n = n())
## # A tibble: 4 × 2
##   nation               n
##   <chr>            <int>
## 1 England            240
## 2 Northern Ireland     9
## 3 Scotland            97
## 4 Wales                2

But the we got the failure in that format

visitors_with_nations %>%
  summarise(n = n()) %>%
  filter(admission == "Free")  
# Error: object 'admission' not found

Question 6 (3 marks)

The following code calculates, for each nation, the percentage of tourist attractions that offer free admission to all visitors:

visitors_with_nations %>%
 group_by(nation) %>%
 count(admission) %>%
 mutate(pct = 100 * n / sum(n)) %>%
 filter(admission == "Free")
## # A tibble: 3 × 4
## # Groups:   nation [3]
##   nation           admission     n   pct
##   <chr>            <chr>     <int> <dbl>
## 1 England          Free         93  38.8
## 2 Northern Ireland Free          1  11.1
## 3 Scotland         Free         34  35.1

Consider this alternative code:

visitors_with_nations %>%
  group_by(admission, nation) %>%
  summarise(n = n()) %>%
  mutate(pct = 100 * n / sum(n)) %>%
  filter(admission == "Free")
## `summarise()` has grouped output by 'admission'. You can override using the
## `.groups` argument.
## # A tibble: 3 × 4
## # Groups:   admission [1]
##   admission nation               n    pct
##   <chr>     <chr>            <int>  <dbl>
## 1 Free      England             93 72.7  
## 2 Free      Northern Ireland     1  0.781
## 3 Free      Scotland            34 26.6

Does this alternative code produce the same output as the reference code? If not, why? Select one.

Correct answer is “No, because the percentage is computed within each admission group (across nations), not within each nation.”

The reference code groups by nation before counting, so the percentage is calculated within each nation. The alternative groups by admission first, so the percentage is calculated across nations for each admission type.


Question 7 (1 mark)

You have this data set when you run this in R (explored in Quiz 2 as well)

library(tidyverse)
us_rent_income
## # A tibble: 104 × 5
##    GEOID NAME       variable estimate   moe
##    <chr> <chr>      <chr>       <dbl> <dbl>
##  1 01    Alabama    income      24476   136
##  2 01    Alabama    rent          747     3
##  3 02    Alaska     income      32940   508
##  4 02    Alaska     rent         1200    13
##  5 04    Arizona    income      27517   148
##  6 04    Arizona    rent          972     4
##  7 05    Arkansas   income      23789   165
##  8 05    Arkansas   rent          709     5
##  9 06    California income      29454   109
## 10 06    California rent         1358     3
## # ℹ 94 more rows

The us_rent_income dataset contains such columns:

What happens if you run

us_rent_income %>%
  summarise(avg_variable = mean(variable, na.rm = TRUE))

Select one.

Correct answer is “The code prints a warning:”argument is not numeric or logical: returning NA”

The variable column contains character values (“income” or “rent”), not numeric values. The mean() function cannot compute the mean of character strings.

us_rent_income %>%
  summarise(avg_variable = mean(variable, na.rm = TRUE))
# Produces 

Question 8 (2 marks - Allow partial and negative credit)

Which of the following expressions will select only the rows in us_rent_income where the variable is income and the estimate value is more than 20000? Select all that apply.

The correct answers are

us_rent_income %>%
 filter(variable == "income" & estimate > 20000)

# And 

us_rent_income %>%
 filter(estimate > 20000) %>%
 filter(variable == "income")
us_rent_income %>%
  filter(variable == "income" | estimate > 20000)

uses OR (|) which would include rows where either condition is true.

us_rent_income %>%
  filter(estimate > 20000) %>%
  select(variable == "income")

incorrectly tries to use select() with a logical condition.


Question 9 (2 marks - Allow partial and negative credit)

In which of the following objects is the first element a number (double)? Select all that apply.

Correct answers are;

list(1, "b", TRUE)
## [[1]]
## [1] 1
## 
## [[2]]
## [1] "b"
## 
## [[3]]
## [1] TRUE
typeof(list(1, "b", TRUE)[[1]])
## [1] "double"
# and 
c(1, 2, 5)
## [1] 1 2 5
typeof(c(1,2,5)[1])
## [1] "double"
# Counterexamples:
c(1, "a", TRUE)[1]  # Returns 1 (but the vector becomes character because of "a" item)
## [1] "1"
list("1", 3.5, TRUE)[[1]]  # Returns "1" as character
## [1] "1"

Question 10 (2 marks)

Match each expression to its result (each answer matches to exactly one expression).

Correct matches are;

FALSE & TRUE
## [1] FALSE
TRUE & NA
## [1] NA

Question 11 (2 marks - Allow partial and negative credit)

You have the following two tibbles:

vis <- tibble(
 attraction = c("Castle", "Museum", "Park", "Zoo"),
 n_2022 = c(150000, 125000, 70000, 90000)
)
 
info <- tibble(
 attraction = c("Castle", "Museum", "Park", "Aquarium"),
 admission = c("Charged", "Free", "Free", "Charged")
)

You join them using

vis %>% inner_join(info, by = "attraction")
## # A tibble: 3 × 3
##   attraction n_2022 admission
##   <chr>       <dbl> <chr>    
## 1 Castle     150000 Charged  
## 2 Museum     125000 Free     
## 3 Park        70000 Free

Which ones of the following code snippets produce the same output? Select all that apply.

Correct options are;

vis %>% full_join(info, by = "attraction") %>%
  drop_na()
## # A tibble: 3 × 3
##   attraction n_2022 admission
##   <chr>       <dbl> <chr>    
## 1 Castle     150000 Charged  
## 2 Museum     125000 Free     
## 3 Park        70000 Free
vis %>% left_join(info, by = "attraction") %>%
  drop_na()
## # A tibble: 3 × 3
##   attraction n_2022 admission
##   <chr>       <dbl> <chr>    
## 1 Castle     150000 Charged  
## 2 Museum     125000 Free     
## 3 Park        70000 Free

The others are returning different format outputs

vis %>% full_join(info, by = "attraction")
## # A tibble: 5 × 3
##   attraction n_2022 admission
##   <chr>       <dbl> <chr>    
## 1 Castle     150000 Charged  
## 2 Museum     125000 Free     
## 3 Park        70000 Free     
## 4 Zoo         90000 <NA>     
## 5 Aquarium       NA Charged
vis %>% semi_join(info, by = "attraction")
## # A tibble: 3 × 2
##   attraction n_2022
##   <chr>       <dbl>
## 1 Castle     150000
## 2 Museum     125000
## 3 Park        70000

Question 12 (4 marks - Allow partial and negative credit)

For this given data,

vis <- tibble(attraction = c("Castle", "Museum", "Park", "Zoo"), 
              n_2021 = c(130000, 110000, 75000, 80000), 
              n_2022 = c(150000, 125000, 70000, 90000) )

You use the following code snippet to transform the data:

vis %>% 
  pivot_longer( cols = c(n_2021, n_2022), 
                names_to = "year", 
                values_to = "visitors" )
## # A tibble: 8 × 3
##   attraction year   visitors
##   <chr>      <chr>     <dbl>
## 1 Castle     n_2021   130000
## 2 Castle     n_2022   150000
## 3 Museum     n_2021   110000
## 4 Museum     n_2022   125000
## 5 Park       n_2021    75000
## 6 Park       n_2022    70000
## 7 Zoo        n_2021    80000
## 8 Zoo        n_2022    90000

Correct answer are “The output has 3 columns: attraction, year and visitors” and “Each attraction appears twice in the output”

The “year” column will be of type numeric. is not correct since the year column type is character, also it does not include solely 2021 and 2022 by the nature of data. It contains \(n_2021\) and \(n_2022\) here


Question 13 (2 marks)

This is the subset of billboard data from tidyverse if we look at it briefly as below

library(tidyverse)
df_bilboard <- billboard %>% select(artist, track, date.entered, wk1, wk2, wk3)
df_bilboard
## # A tibble: 317 × 6
##    artist         track                   date.entered   wk1   wk2   wk3
##    <chr>          <chr>                   <date>       <dbl> <dbl> <dbl>
##  1 2 Pac          Baby Don't Cry (Keep... 2000-02-26      87    82    72
##  2 2Ge+her        The Hardest Part Of ... 2000-09-02      91    87    92
##  3 3 Doors Down   Kryptonite              2000-04-08      81    70    68
##  4 3 Doors Down   Loser                   2000-10-21      76    76    72
##  5 504 Boyz       Wobble Wobble           2000-04-15      57    34    25
##  6 98^0           Give Me Just One Nig... 2000-08-19      51    39    34
##  7 A*Teens        Dancing Queen           2000-07-08      97    97    96
##  8 Aaliyah        I Don't Wanna           2000-01-29      84    62    51
##  9 Aaliyah        Try Again               2000-03-18      59    53    38
## 10 Adams, Yolanda Open My Heart           2000-08-26      76    76    74
## # ℹ 307 more rows

Which of the following describes the output given by this code snippet? Select one.

df_bilboard %>% 
  drop_na(wk1) %>% 
  drop_na(wk2) %>% 
  mutate(change = wk1 - wk2) %>% 
  filter(change > 10)
## # A tibble: 108 × 7
##    artist              track               date.entered   wk1   wk2   wk3 change
##    <chr>               <chr>               <date>       <dbl> <dbl> <dbl>  <dbl>
##  1 3 Doors Down        Kryptonite          2000-04-08      81    70    68     11
##  2 504 Boyz            Wobble Wobble       2000-04-15      57    34    25     23
##  3 98^0                Give Me Just One N… 2000-08-19      51    39    34     12
##  4 Aaliyah             I Don't Wanna       2000-01-29      84    62    51     22
##  5 Aguilera, Christina I Turn To You       2000-04-15      50    39    30     11
##  6 Aguilera, Christina What A Girl Wants   1999-11-27      71    51    28     20
##  7 Alice Deejay        Better Off Alone    2000-04-08      79    65    53     14
##  8 Anthony, Marc       You Sang To Me      2000-02-26      77    54    50     23
##  9 Avant               Separated           2000-04-29      62    32    30     30
## 10 BBMak               Back Here           2000-04-29      99    86    60     13
## # ℹ 98 more rows

The correct answer is

“A data frame containing only the songs that moved up the chart by more than 10 places between week 1 and week 2”

The code:

By definition, the difference can be positive or negative but we are only interested in positive values that are greater than 10.


Question 14 (1 mark)

You want to calculate the mean chart position in week 2 for each artist, but the following code does not produce the desired output. Why? Select one.

df_bilboard %>% 
  summarise(mean_wk2 = mean(wk2, na.rm = TRUE))
## # A tibble: 1 × 1
##   mean_wk2
##      <dbl>
## 1     71.2

Correct answer is “Because group_by(artist) is missing”

The summarise() function without group_by() calculates a single summary statistic for the entire dataset. To calculate statistics by each artist, you need to group_by(artist) first.