Source: Abe Hofman / R for Data Science; Wickham & Grolemund Work on these assignments individually. You can find the deadline in the course syllabus. After the deadline my own solutions will be posted on Canvas. This assignment won’t be graded, because a lot of materials are available online. But you do need to hand it in through Canvas. Use these online materials wisely! Make the assignment in a .R or Rmarkdown file. If you work in a .R file, use comments (for us or yourself) to keep thing readable and make sure you document runs without errors. If you use an Markdown file, Knit it to an HTML file and hand that in through Canvas. Make sure that it includes your code. We will give a short Markdown demo during one of the tutorials (see course syllabus). Read Ch 1, 2 and 18 before you start, and work on the exercises of Ch 19 the activate your Base-R knowledge. We assume that everything in Ch 4 and 6 is already known. Make sure to double check this. Ch 8 might be helpful if you have not worked in an R-project before. Not required, but might be very handy, feel free to ask questions about this during the tutorials. The assignment will cover Ch 3, 5 and 19, and includes a subset of the questions of these chapters.
1. Run ggplot(data = mpg). What do you see?
We see a blank canvas, because we have not specified any variables to plot yet.
2. How many rows are in mpg? How many columns?
cat("Number of rows:", nrow(mpg))
#> Number of rows: 234
cat("Number of columns:", ncol(mpg))
#> Number of columns: 11
glimpse(mpg)
#> Rows: 234
#> Columns: 11
#> $ manufacturer <chr> "audi", "audi", "audi", "audi", "audi", "audi", "audi", "…
#> $ model <chr> "a4", "a4", "a4", "a4", "a4", "a4", "a4", "a4 quattro", "…
#> $ displ <dbl> 1.8, 1.8, 2.0, 2.0, 2.8, 2.8, 3.1, 1.8, 1.8, 2.0, 2.0, 2.…
#> $ year <int> 1999, 1999, 2008, 2008, 1999, 1999, 2008, 1999, 1999, 200…
#> $ cyl <int> 4, 4, 4, 4, 6, 6, 6, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, …
#> $ trans <chr> "auto(l5)", "manual(m5)", "manual(m6)", "auto(av)", "auto…
#> $ drv <chr> "f", "f", "f", "f", "f", "f", "f", "4", "4", "4", "4", "4…
#> $ cty <int> 18, 21, 20, 21, 16, 18, 18, 18, 16, 20, 19, 15, 17, 17, 1…
#> $ hwy <int> 29, 29, 31, 30, 26, 26, 27, 26, 25, 28, 27, 25, 25, 25, 2…
#> $ fl <chr> "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p…
#> $ class <chr> "compact", "compact", "compact", "compact", "compact", "c…3. What does the drv variable describe? Read the help for ?mpg to find out.
From the docs:
drv - the type of drive train, where f = front-wheel drive, r = rear wheel drive, 4 = 4wd
4. Make a scatterplot of hwy vs cyl.
5. What happens if you make a scatterplot of class vs drv? Why is the plot not useful?
Both data are categorical, and the points on the scatterplot overlap.
1. What’s gone wrong with this code? Why are the points not blue?
Everything inside aes() is treated as a variable name,
so color evaluates to a new variable “blue”, visuzlized
with a default color.
Actually blue output:
2. Map a continuous variable to color, size, and shape. How do these aesthetics behave differently for categorical vs. continuous variables?
# continuous to color
ggplot(mpg, aes(displ, hwy, colour = cty)) + geom_point()
# categorical to color
ggplot(mpg, aes(displ, hwy, colour = class)) + geom_point()
# continuous to size
ggplot(mpg, aes(displ, hwy, size = cty)) + geom_point()
# categorical to size
ggplot(mpg, aes(displ, hwy, size = class)) + geom_point()
# continuous to shape
ggplot(mpg, aes(displ, hwy, shape = cty)) + geom_point()
#> Error in `geom_point()`:
#> ! Problem while computing aesthetics.
#> ℹ Error occurred in the 1st layer.
#> Caused by error in `scale_f()`:
#> ! A continuous variable cannot be mapped to the shape aesthetic.
#> ℹ Choose a different aesthetic or use `scale_shape_binned()`.
# categorical to shape
ggplot(mpg, aes(displ, hwy, shape = class)) + geom_point()Mapping both continuous and categorical variables to color works well. Mapping continuous variables to size works well, but while mapping categorical variables to size works, it doesn’t make a lot of sense. Mapping continuous to shape produces an error, and rightfully so because shapes are inherently discrete. Mapping categorical variables to shape works well.
3. What happens if you map the same variable to multiple aesthetics?
It works fine but it’s unncessarily convoluted and confusing.
4. What happens if you map an aesthetic to something other than a variable name, like aes(colour = displ < 5)? Note, you’ll also need to specify x and y.
The points are colored based on whether their displ
value is less than 5 or not: a result of a logical evaluation yielding
TRUE or FALSE.
Recreate the R code necessary to generate the following graphs.
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth(se = FALSE)
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth(aes(group = drv), se = FALSE)
ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_point() +
geom_smooth(se = FALSE)
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(color = drv)) +
geom_smooth(se = FALSE)
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(color = drv)) +
geom_smooth(aes(linetype = drv), se = FALSE)
ggplot(mpg, aes(displ, hwy)) +
geom_point(color = "white", size = 4) +
geom_point(aes(color = drv))What does geom_col() do? How is it different to geom_bar()?
From docs:
geom_bar()makes the height of the bar proportional to the number of cases in each group (or if the weight aesthetic is supplied, the sum of the weights). If you want the heights of the bars to represent values in the data, usegeom_col()instead.geom_bar()usesstat_count()by default: it counts the number of cases at each x position.geom_col()usesstat_identity(): it leaves the data as is.
What is the problem with this plot? How could you improve it?
The points overlap, which makes it difficult to see the density of
points. As a basic remedy, we could reduce the alpha of the points to
make them more transparent. Additionally, we could use
geom_jitter() to reduce the overlap or
geom_count() to make the size of the points proportional to
the number of overlapping points.
ggplot(mpg, aes(x = cty, y = hwy)) + geom_point(alpha = 0.2)
ggplot(mpg, aes(x = cty, y = hwy)) + geom_jitter(alpha = 0.5, se = FALSE)
ggplot(mpg, aes(x = cty, y = hwy)) + geom_count(alpha = 0.5)Find all flights that:
glimpse(flights)
#> Rows: 336,776
#> Columns: 19
#> $ year <int> 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2…
#> $ month <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
#> $ day <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
#> $ dep_time <int> 517, 533, 542, 544, 554, 554, 555, 557, 557, 558, 558, …
#> $ sched_dep_time <int> 515, 529, 540, 545, 600, 558, 600, 600, 600, 600, 600, …
#> $ dep_delay <dbl> 2, 4, 2, -1, -6, -4, -5, -3, -3, -2, -2, -2, -2, -2, -1…
#> $ arr_time <int> 830, 850, 923, 1004, 812, 740, 913, 709, 838, 753, 849,…
#> $ sched_arr_time <int> 819, 830, 850, 1022, 837, 728, 854, 723, 846, 745, 851,…
#> $ arr_delay <dbl> 11, 20, 33, -18, -25, 12, 19, -14, -8, 8, -2, -3, 7, -1…
#> $ carrier <chr> "UA", "UA", "AA", "B6", "DL", "UA", "B6", "EV", "B6", "…
#> $ flight <int> 1545, 1714, 1141, 725, 461, 1696, 507, 5708, 79, 301, 4…
#> $ tailnum <chr> "N14228", "N24211", "N619AA", "N804JB", "N668DN", "N394…
#> $ origin <chr> "EWR", "LGA", "JFK", "JFK", "LGA", "EWR", "EWR", "LGA",…
#> $ dest <chr> "IAH", "IAH", "MIA", "BQN", "ATL", "ORD", "FLL", "IAD",…
#> $ air_time <dbl> 227, 227, 160, 183, 116, 150, 158, 53, 140, 138, 149, 1…
#> $ distance <dbl> 1400, 1416, 1089, 1576, 762, 719, 1065, 229, 944, 733, …
#> $ hour <dbl> 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6…
#> $ minute <dbl> 15, 29, 40, 45, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0…
#> $ time_hour <dttm> 2013-01-01 05:00:00, 2013-01-01 05:00:00, 2013-01-01 0…a. Had an arrival delay of two or more hours
b. Flew to Houston (IAH or HOU)
c. Were operated by United, American, or Delta
d. Departed in summer (July, August, and September)
e. Arrived more than two hours late, but didn’t leave late
f. Were delayed by at least an hour, but made up over 30 minutes in flight
g. Departed between midnight and 6 am (inclusive)
1. How could you use arrange() to sort all missing values to the start? (Hint: use is.na()).
2. Sort flights to find the most delayed flights. Find the flights that left earliest.
1. Brainstorm as many ways as possible (at least two) to select dep_time, dep_delay, arr_time, and arr_delay from flights.
# using a character vector of variable names
flights %>% select(c("dep_time", "dep_delay", "arr_time", "arr_delay"))# using all_of() with a character vector of variable names
flights %>% select(all_of(c("dep_time", "dep_delay", "arr_time", "arr_delay")))# using starts_with() to select variables "dep_*" or "arr_*"
flights %>% select(starts_with("dep_"), starts_with("arr_"))# using matches() with a regex to select "dep_*" or "arr_*"
flights %>% select(matches("^(dep|arr)_"))# we can also subtract all other columns but I don't want to type them all out
# and there are other ways to do this, but I guess these are enough2. What happens if you include the name of a variable multiple times in a select() call?
The variable is only selected once anyway, no erros are thrown.
Currently dep_time and sched_dep_time are convenient to look at, but hard to compute with because they’re not really continuous numbers. Convert them to a more convenient representation of number of minutes since midnight.
to_minutes_since_midnight <- function(time) {
(time %/% 100) * 60 + (time %% 100)
}
flights %>%
mutate(
dep_time_mins = to_minutes_since_midnight(dep_time),
sched_dep_time_mins = to_minutes_since_midnight(sched_dep_time)
) %>%
select(dep_time, dep_time_mins, sched_dep_time, sched_dep_time_mins)Come up with another approach that will give you the same output as
From 5.6:
a. not_cancelled %>% count(dest)
b. not_cancelled %>% count(tailnum, wt = distance) (without using count()).
For each destination, compute the total minutes of arrival delay. For each flight, compute the proportion of the total delay for its destination.
not_cancelled %>%
group_by(dest) %>%
mutate(total_arr_delay = sum(arr_delay, na.rm = TRUE)) %>%
ungroup() %>%
mutate(prop_total_delay = arr_delay / total_arr_delay) %>%
select(dest, arr_delay, total_arr_delay, prop_total_delay)Write both_na(), a function that takes two vectors of the same length and returns the number of positions that have an NA in both vectors
1. What’s the difference between if and ifelse()? Carefully read the help and construct three examples that illustrate the key differences.
date_threshold <- as.Date("2026-09-06")
# if with a single value, class preserved
test_date <- as.Date("2026-09-05")
if (test_date < date_threshold) {
cat(format(test_date), "is before threshold")
} else {
cat(format(test_date), "is not before threshold")
}
#> 2026-09-05 is before threshold
# if with NA throws an error (doesn't resolve it)
if (NA < date_threshold) {
cat("NA is before threshold")
} else {
cat("NA is not before threshold")
}
#> Error in `if (NA < date_threshold) ...`:
#> ! missing value where TRUE/FALSE needed
# ifelse with a vector, NA preserved, but the class is stripped
test_dates <- as.Date(c("2026-09-05", "2026-09-07", NA))
result <- ifelse(test_dates < date_threshold, "before threshold", "after threshold")
cat("class of test_dates:", class(test_dates), "\n")
#> class of test_dates: Date
cat("class of result:", class(result), "\n\n")
#> class of result: character
print(result)
#> [1] "before threshold" "after threshold" NA2. Write a greeting function that says “good morning”, “good afternoon”, or “good evening”, depending on the time of day. (Hint: use a time argument that defaults to lubridate::now(). That will make it easier to test your function.)
From 19.5:
commas <- function(...) stringr::str_c(..., collapse = ", ")
commas(letters[1:10])
#> [1] "a, b, c, d, e, f, g, h, i, j"What does commas(letters, collapse = “-”) do? Why?
commas(letters, collapse = "-")
#> Error in `stringr::str_c()`:
#> ! formal argument "collapse" matched by multiple actual argumentsIt throws an error because commas() already fixes the
collapse when passing to str_c(), so the “new”
collapse argument is not passed to str_c() and
instead treated as a new argument to commas(), which
doesn’t know what to do with it.